From 3d6b55d3e9b91e6afdbfdcea0268dfb88c69c1c2 Mon Sep 17 00:00:00 2001 From: Elias Åström Date: Thu, 2 Apr 2020 19:24:35 +0200 Subject: Fix texture distortion for flowing liquids (#9561) Previously textures of the side faces on flowing liquid nodes would become distorted on different axis depending on the liquid level. This is because the nodes always had the same texture coordinates, even when the generated face could have different sizes. This solves that problem by adjusting the texture coordinates for the vertices making up the top of the faces, so the textures will not look compressed for smaller faces. --- src/client/content_mapblock.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/client') diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 5be0caf19..9b4fd221e 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -556,17 +556,24 @@ void MapblockMeshGenerator::drawLiquidSides() for (int j = 0; j < 4; j++) { const UV &vertex = base_vertices[j]; const v3s16 &base = face.p[vertex.u]; + float v = vertex.v; + v3f pos; - pos.X = (base.X - 0.5) * BS; - pos.Z = (base.Z - 0.5) * BS; - if (vertex.v) - pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS; - else - pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS; + pos.X = (base.X - 0.5f) * BS; + pos.Z = (base.Z - 0.5f) * BS; + if (vertex.v) { + pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5f * BS; + } else if (top_is_same_liquid) { + pos.Y = 0.5f * BS; + } else { + pos.Y = corner_levels[base.Z][base.X]; + v += (0.5f * BS - corner_levels[base.Z][base.X]) / BS; + } + if (data->m_smooth_lighting) color = blendLightColor(pos); pos += origin; - vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v); + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, v); }; collector->append(tile_liquid, vertices, 4, quad_indices, 6); } -- cgit v1.2.3 From f45ba78a72d86fd481a2d2064ac63858d69ad7ee Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Mon, 6 Apr 2020 16:54:12 +0200 Subject: Allow relative directories for `screenshot_path`, tweak default path (#9122) This will likely be more intuitive for users and should play better with sandboxed distributions such as Flatpak. In addition, the screenshot directory will now be created if it doesn't exist already. --- builtin/settingtypes.txt | 5 +++-- src/client/client.cpp | 13 ++++++++++++- src/defaultsettings.cpp | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1f2889b45..a6cf8de60 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -903,8 +903,9 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255 # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf -# Path to save screenshots at. -screenshot_path (Screenshot folder) path +# Path to save screenshots at. Can be an absolute or relative path. +# The folder will be created if it doesn't already exist. +screenshot_path (Screenshot folder) path screenshots # Format of screenshots. screenshot_format (Screenshot format) enum png png,jpg,bmp,pcx,ppm,tga diff --git a/src/client/client.cpp b/src/client/client.cpp index c6d28ce80..e15391dde 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1780,13 +1780,24 @@ void Client::makeScreenshot() char timetstamp_c[64]; strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm); - std::string filename_base = g_settings->get("screenshot_path") + std::string screenshot_dir; + + if (fs::IsPathAbsolute(g_settings->get("screenshot_path"))) + screenshot_dir = g_settings->get("screenshot_path"); + else + screenshot_dir = porting::path_user + DIR_DELIM + g_settings->get("screenshot_path"); + + std::string filename_base = screenshot_dir + DIR_DELIM + std::string("screenshot_") + std::string(timetstamp_c); std::string filename_ext = "." + g_settings->get("screenshot_format"); std::string filename; + // Create the directory if it doesn't already exist. + // Otherwise, saving the screenshot would fail. + fs::CreateDir(screenshot_dir); + u32 quality = (u32)g_settings->getS32("screenshot_quality"); quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 472522bf4..b6b1ce1f2 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -48,7 +48,7 @@ void set_default_settings(Settings *settings) settings->setDefault("pitch_move", "false"); settings->setDefault("fast_move", "false"); settings->setDefault("noclip", "false"); - settings->setDefault("screenshot_path", "."); + settings->setDefault("screenshot_path", "screenshots"); settings->setDefault("screenshot_format", "png"); settings->setDefault("screenshot_quality", "0"); settings->setDefault("client_unload_unused_data_timeout", "600"); -- cgit v1.2.3 From de73f989eb1397b1103236031fd91309b294583c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 Apr 2020 20:13:23 +0200 Subject: Overall improvements to log messages (#9598) Hide some unnecessarily verbose ones behind --trace or disable them entirely. Remove duplicate ones. Improve their contents in some places. --- src/client/client.cpp | 40 ++++++++++++++++++++++++++----------- src/client/client.h | 19 ++++-------------- src/client/content_cao.cpp | 6 +++--- src/client/fontengine.cpp | 2 +- src/client/game.cpp | 3 +-- src/client/renderingengine.cpp | 20 ++----------------- src/client/sound_openal.cpp | 16 ++++++++------- src/client/tile.cpp | 9 +++++---- src/content/subgames.cpp | 2 +- src/content_sao.cpp | 12 +++++------ src/craftdef.cpp | 4 ++-- src/emerge.cpp | 1 - src/itemdef.cpp | 6 +++--- src/main.cpp | 8 +------- src/map.cpp | 8 ++++---- src/network/clientpackethandler.cpp | 8 ++++---- src/network/connectionthreads.cpp | 3 +-- src/nodedef.cpp | 2 +- src/script/cpp_api/s_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 1 - src/server.cpp | 17 ++++++++-------- src/server/mods.cpp | 9 +++------ 22 files changed, 87 insertions(+), 111 deletions(-) (limited to 'src/client') diff --git a/src/client/client.cpp b/src/client/client.cpp index e15391dde..c9cd24cb3 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -60,6 +60,20 @@ with this program; if not, write to the Free Software Foundation, Inc., extern gui::IGUIEnvironment* guienv; +/* + Utility classes +*/ + +void PacketCounter::print(std::ostream &o) const +{ + for (const auto &it : m_packets) { + auto name = it.first >= TOCLIENT_NUM_MSG_TYPES ? "?" + : toClientCommandTable[it.first].name; + o << "cmd " << it.first << " (" << name << ") count " + << it.second << std::endl; + } +} + /* Client */ @@ -336,12 +350,12 @@ void Client::step(float dtime) { float &counter = m_packetcounter_timer; counter -= dtime; - if(counter <= 0.0) + if(counter <= 0.0f) { - counter = 20.0; + counter = 30.0f; infostream << "Client packetcounter (" << m_packetcounter_timer - << "):"<getFloat("server_map_save_interval"); + int n = 0; for (std::unordered_map::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); + n++; } } + if (n > 0) + infostream << "Saved " << n << " modified mod storages." << std::endl; } // Write server map @@ -653,8 +670,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, image_ext); if (!name.empty()) { - verbosestream<<"Client: Attempting to load image " - <<"file \""<loadSoundData(name, data); - return true; + TRACESTREAM(<< "Client: Attempting to load sound " + << "file \"" << filename << "\"" << std::endl); + return m_sound->loadSoundData(name, data); } const char *model_ext[] = { @@ -714,8 +730,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, translate_ext); if (!name.empty()) { - verbosestream << "Client: Loading translation: " - << "\"" << filename << "\"" << std::endl; + TRACESTREAM(<< "Client: Loading translation: " + << "\"" << filename << "\"" << std::endl); g_translations->loadTranslation(data); return true; } diff --git a/src/client/client.h b/src/client/client.h index 1291b944c..eea78d456 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -82,30 +82,19 @@ public: void add(u16 command) { - std::map::iterator n = m_packets.find(command); - if(n == m_packets.end()) - { + auto n = m_packets.find(command); + if (n == m_packets.end()) m_packets[command] = 1; - } else - { n->second++; - } } void clear() { - for (auto &m_packet : m_packets) { - m_packet.second = 0; - } + m_packets.clear(); } - void print(std::ostream &o) - { - for (const auto &m_packet : m_packets) { - o << "cmd "<< m_packet.first <<" count "<< m_packet.second << std::endl; - } - } + void print(std::ostream &o) const; private: // command, count diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d148df522..8509eccb5 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -576,9 +576,10 @@ void GenericCAO::addToScene(ITextureSource *tsrc) m_visuals_expired = false; - if (!m_prop.is_visible) { + if (!m_prop.is_visible) return; - } + + infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl; if (m_enable_shaders) { IShaderSource *shader_source = m_client->getShaderSource(); @@ -593,7 +594,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc) } auto grabMatrixNode = [this] { - infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl; m_matrixnode = RenderingEngine::get_scene_manager()-> addDummyTransformationSceneNode(); m_matrixnode->grab(); diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 2b5841cd8..61d52cc2f 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -239,7 +239,7 @@ void FontEngine::updateSkin() FATAL_ERROR_IF(font == NULL, "Could not create/get font"); u32 text_height = font->getDimension(L"Hello, world!").Height; - infostream << "text_height=" << text_height << std::endl; + infostream << "FontEngine: measured text_height=" << text_height << std::endl; } /******************************************************************************/ diff --git a/src/client/game.cpp b/src/client/game.cpp index 0201ded69..437cc7871 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2010,7 +2010,6 @@ void Game::processItemSelection(u16 *new_playeritem) for (u16 i = 0; i <= max_item; i++) { if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) { *new_playeritem = i; - infostream << "Selected item: " << new_playeritem << std::endl; break; } } @@ -2039,7 +2038,7 @@ void Game::openInventory() if (!player || !player->getCAO()) return; - infostream << "the_game: " << "Launching inventory" << std::endl; + infostream << "Game: Launching inventory" << std::endl; PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 6e6509eeb..8b7bbf328 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -226,27 +226,17 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) { // FIXME: It would make more sense for there to be a switch of some // sort here that would call the correct toplevel setup methods for - // the environment Minetest is running in but for now not deviating - // from the original pattern. + // the environment Minetest is running in. /* Setting Xorg properties for the top level window */ setupTopLevelXorgWindow(name); - /* Done with Xorg properties */ /* Setting general properties for the top level window */ verbosestream << "Client: Configuring general top level" << " window properties" << std::endl; - bool result = setWindowIcon(); - verbosestream << "Client: Finished configuring general top level" - << " window properties" - << std::endl; - /* Done with general properties */ - - // FIXME: setWindowIcon returns a bool result but it is unused. - // For now continue to return this result. return result; } @@ -262,7 +252,7 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) return; } - verbosestream << "Client: Configuring Xorg specific top level" + verbosestream << "Client: Configuring X11-specific top level" << " window properties" << std::endl; @@ -309,8 +299,6 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) Atom NET_WM_PID = XInternAtom(x11_dpl, "_NET_WM_PID", false); pid_t pid = getpid(); - infostream << "Client: PID is '" << static_cast(pid) << "'" - << std::endl; XChangeProperty(x11_dpl, x11_win, NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, @@ -327,10 +315,6 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) XChangeProperty (x11_dpl, x11_win, WM_CLIENT_LEADER, XA_WINDOW, 32, PropModeReplace, reinterpret_cast(&x11_win), 1); - - verbosestream << "Client: Finished configuring Xorg specific top level" - << " window properties" - << std::endl; #endif } diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index 8e696f302..d0f935a7a 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -165,8 +165,8 @@ SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile, << "preparing sound buffer" << std::endl; } - infostream << "Audio file " - << filename_for_logging << " loaded" << std::endl; + //infostream << "Audio file " + // << filename_for_logging << " loaded" << std::endl; // Clean up! ov_clear(oggFile); @@ -498,9 +498,11 @@ public: // Remove stopped sounds void maintain() { - verbosestream<<"OpenALSoundManager::maintain(): " - < del_list; for (const auto &sp : m_sounds_playing) { int id = sp.first; @@ -530,7 +532,7 @@ public: SoundBuffer *buf = load_ogg_from_file(filepath); if (buf) addBuffer(name, buf); - return false; + return !!buf; } bool loadSoundData(const std::string &name, @@ -539,7 +541,7 @@ public: SoundBuffer *buf = load_ogg_from_buffer(filedata, name); if (buf) addBuffer(name, buf); - return false; + return !!buf; } void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 3189ab28c..0fa7a4ae2 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -471,8 +471,8 @@ TextureSource::~TextureSource() driver->removeTexture(t); } - infostream << "~TextureSource() "<< textures_before << "/" - << driver->getTextureCount() << std::endl; + infostream << "~TextureSource() before cleanup: "<< textures_before + << " after: " << driver->getTextureCount() << std::endl; } u32 TextureSource::getTextureId(const std::string &name) @@ -763,6 +763,9 @@ void TextureSource::rebuildImagesAndTextures() video::IVideoDriver *driver = RenderingEngine::get_video_driver(); sanity_check(driver); + infostream << "TextureSource: recreating " << m_textureinfo_cache.size() + << " textures" << std::endl; + // Recreate textures for (TextureInfo &ti : m_textureinfo_cache) { video::IImage *img = generateImage(ti.name); @@ -1270,8 +1273,6 @@ bool TextureSource::generateImagePart(std::string part_of_name, video::IImage *img = generateImage(filename); if (img) { core::dimension2d dim = img->getDimension(); - infostream<<"Size "< pos_base(x, y); video::IImage *img2 = driver->createImage(video::ECF_A8R8G8B8, dim); diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index bf947cf85..170f54e20 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -253,7 +253,7 @@ std::vector getAvailableWorlds() worldspaths.insert(porting::path_user + DIR_DELIM + "worlds"); infostream << "Searching worlds..." << std::endl; for (const std::string &worldspath : worldspaths) { - infostream << " In " << worldspath << ": " << std::endl; + infostream << " In " << worldspath << ": "; std::vector dirvector = fs::GetDirListing(worldspath); for (const fs::DirListNode &dln : dirvector) { if (!dln.dir) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 75c3eaf37..680bf372a 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -711,13 +711,11 @@ float LuaEntitySAO::getMinimumSavedMovement() std::string LuaEntitySAO::getDescription() { - std::ostringstream os(std::ios::binary); - os<<"LuaEntitySAO at ("; - os<<(m_base_position.X/BS)<<","; - os<<(m_base_position.Y/BS)<<","; - os<<(m_base_position.Z/BS); - os<<")"; - return os.str(); + std::ostringstream oss; + oss << "LuaEntitySAO \"" << m_init_name << "\" "; + auto pos = floatToInt(m_base_position, BS); + oss << "at " << PP(pos); + return oss.str(); } void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 0181ceb60..210605198 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -1066,8 +1066,8 @@ public: } virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) { - verbosestream << "registerCraft: registering craft definition: " - << def->dump() << std::endl; + TRACESTREAM(<< "registerCraft: registering craft definition: " + << def->dump() << std::endl); m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def); CraftInput input; diff --git a/src/emerge.cpp b/src/emerge.cpp index fc1da4ee7..4835c3fad 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -136,7 +136,6 @@ EmergeManager::EmergeManager(Server *server) nthreads = Thread::getNumberOfProcessors() - 2; if (nthreads < 1) nthreads = 1; - verbosestream << "Using " << nthreads << " emerge threads." << std::endl; m_qlimit_total = g_settings->getU16("emergequeue_limit_total"); if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly)) diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 0d0afeb2b..ba7bd6a0b 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -463,7 +463,7 @@ public: } virtual void registerItem(const ItemDefinition &def) { - verbosestream<<"ItemDefManager: registering \""< "< " << convert_to << std::endl); m_aliases[name] = convert_to; } } diff --git a/src/main.cpp b/src/main.cpp index 1993f7c24..8df2fe7d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -207,9 +207,6 @@ int main(int argc, char *argv[]) sanity_check(!game_params.world_path.empty()); - infostream << "Using commanded world path [" - << game_params.world_path << "]" << std::endl; - if (game_params.is_dedicated_server) return run_dedicated_server(game_params, cmd_args) ? 0 : 1; @@ -686,8 +683,6 @@ static bool auto_select_world(GameParams *game_params) // No world was specified; try to select it automatically // Get information about available worlds - verbosestream << _("Determining world path") << std::endl; - std::vector worldspecs = getAvailableWorlds(); std::string world_path; @@ -708,7 +703,7 @@ static bool auto_select_world(GameParams *game_params) // This is the ultimate default world path world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "world"; - infostream << "Creating default world at [" + infostream << "Using default world at [" << world_path << "]" << std::endl; } @@ -770,7 +765,6 @@ static bool determine_subgame(GameParams *game_params) assert(game_params->world_path != ""); // Pre-condition - verbosestream << _("Determining gameid/gamespec") << std::endl; // If world doesn't exist if (!game_params->world_path.empty() && !getWorldExists(game_params->world_path)) { diff --git a/src/map.cpp b/src/map.cpp index 40aba067e..eb69955ee 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1827,10 +1827,10 @@ void ServerMap::save(ModifiedState save_level) */ if(save_level == MOD_STATE_CLEAN || block_count != 0) { - infostream<<"ServerMap: Written: " - <id << " has timed out." - << " (source=peer->timeout_counter)" << std::endl; // Add peer to the list timeouted_peers.push_back(peer->id); @@ -292,7 +291,7 @@ void ConnectionSendThread::runTimeouts(float dtime) // Remove timed out peers for (u16 timeouted_peer : timeouted_peers) { - LOG(derr_con << m_connection->getDesc() + LOG(dout_con << m_connection->getDesc() << "RunTimeouts(): Removing peer " << timeouted_peer << std::endl); m_connection->deletePeer(timeouted_peer, true); } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 977a4533d..b6eca9497 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1458,7 +1458,7 @@ void NodeDefManager::deSerialize(std::istream &is) m_content_features.resize((u32)(i) + 1); m_content_features[i] = f; addNameIdMapping(i, f.name); - verbosestream << "deserialized " << f.name << std::endl; + TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl); getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index ab3b5fe46..8da5debaa 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -86,7 +86,7 @@ void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &t void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) { SCRIPTAPI_PRECHECKHEADER - verbosestream << "scriptapi_add_environment" << std::endl; + verbosestream << "ScriptApiEnv: Environment initialized" << std::endl; setEnv(env); /* diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 0c174feca..ff77cba32 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -522,7 +522,6 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) lua_getfield(L, table, "name"); if(lua_isstring(L, -1)){ name = readParam(L, -1); - verbosestream<<"register_item_raw: "<getFloat("server_map_save_interval"); + int n = 0; for (std::unordered_map::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); + n++; } } + if (n > 0) + infostream << "Saved " << n << " modified mod storages." << std::endl; } } @@ -809,7 +812,6 @@ void Server::AsyncRunStep(bool initial_step) disable_single_change_sending ? 5 : 30); break; case MEET_BLOCK_NODE_METADATA_CHANGED: { - verbosestream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl; prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1); if (!event->is_private_change) { // Don't send the change yet. Collect them to eliminate dupes. @@ -825,7 +827,6 @@ void Server::AsyncRunStep(bool initial_step) break; } case MEET_OTHER: - infostream << "Server: MEET_OTHER" << std::endl; prof.add("MEET_OTHER", 1); for (const v3s16 &modified_block : event->modified_blocks) { m_clients.markBlockposAsNotSent(modified_block); @@ -2535,9 +2536,6 @@ void Server::fillMediaCache() void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code) { - verbosestream << "Server: Announcing files to id(" << peer_id << ")" - << std::endl; - // Make packet NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id); @@ -2560,6 +2558,9 @@ void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_co pkt << g_settings->get("remote_media"); Send(&pkt); + + verbosestream << "Server: Announcing files to id(" << peer_id + << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl; } struct SendableMedia @@ -2938,10 +2939,8 @@ void Server::UpdateCrafting(RemotePlayer *player) if (!clist || clist->getSize() == 0) return; - if (!clist->checkModified()) { - verbosestream << "Skip Server::UpdateCrafting(): list unmodified" << std::endl; + if (!clist->checkModified()) return; - } // Get a preview for crafting ItemStack preview; diff --git a/src/server/mods.cpp b/src/server/mods.cpp index c5616dcd6..c8d8a28e2 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "scripting_server.h" #include "content/subgames.h" +#include "porting.h" /** * Manage server mods @@ -66,14 +67,10 @@ void ServerModManager::loadMods(ServerScripting *script) "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; - infostream << " [" << padStringRight(mod.name, 12) << "] [\"" - << script_path << "\"]" << std::endl; - auto t = std::chrono::steady_clock::now(); + auto t = porting::getTimeMs(); script->loadMod(script_path, mod.name); infostream << "Mod \"" << mod.name << "\" loaded after " - << std::chrono::duration_cast( - std::chrono::steady_clock::now() - t).count() * 0.001f - << " seconds" << std::endl; + << (porting::getTimeMs() - t) << " ms" << std::endl; } // Run a callback when mods are loaded -- cgit v1.2.3 From 3ad5388c6d3894e2f4aa7158cc2b62b626f0f967 Mon Sep 17 00:00:00 2001 From: TheTermos <55103816+TheTermos@users.noreply.github.com> Date: Wed, 8 Apr 2020 22:45:05 +0200 Subject: Collision various fixes (#9343) --- src/client/localplayer.h | 2 + src/collision.cpp | 222 +++++++++++++++++++--------------------- src/collision.h | 2 +- src/unittest/test_collision.cpp | 64 ++++++------ 4 files changed, 141 insertions(+), 149 deletions(-) (limited to 'src/client') diff --git a/src/client/localplayer.h b/src/client/localplayer.h index 95dceb1f4..d88ae17ac 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -141,6 +141,8 @@ public: void setCollisionbox(const aabb3f &box) { m_collisionbox = box; } + const aabb3f& getCollisionbox() const { return m_collisionbox; } + float getZoomFOV() const { return m_zoom_fov; } void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; } diff --git a/src/collision.cpp b/src/collision.cpp index a443be7ab..0d37ea436 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -25,16 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #ifndef SERVER #include "client/clientenvironment.h" +#include "client/localplayer.h" #endif #include "serverenvironment.h" #include "serverobject.h" #include "util/timetaker.h" #include "profiler.h" -// float error is 10 - 9.96875 = 0.03125 -//#define COLL_ZERO 0.032 // broken unit tests -#define COLL_ZERO 0 - struct NearbyCollisionInfo { NearbyCollisionInfo(bool is_ul, bool is_obj, int bouncy, @@ -61,118 +58,102 @@ struct NearbyCollisionInfo { // The time after which the collision occurs is stored in dtime. CollisionAxis axisAlignedCollision( const aabb3f &staticbox, const aabb3f &movingbox, - const v3f &speed, f32 d, f32 *dtime) + const v3f &speed, f32 *dtime) { //TimeTaker tt("axisAlignedCollision"); - f32 xsize = (staticbox.MaxEdge.X - staticbox.MinEdge.X) - COLL_ZERO; // reduce box size for solve collision stuck (flying sand) - f32 ysize = (staticbox.MaxEdge.Y - staticbox.MinEdge.Y); // - COLL_ZERO; // Y - no sense for falling, but maybe try later - f32 zsize = (staticbox.MaxEdge.Z - staticbox.MinEdge.Z) - COLL_ZERO; - aabb3f relbox( - movingbox.MinEdge.X - staticbox.MinEdge.X, - movingbox.MinEdge.Y - staticbox.MinEdge.Y, - movingbox.MinEdge.Z - staticbox.MinEdge.Z, - movingbox.MaxEdge.X - staticbox.MinEdge.X, - movingbox.MaxEdge.Y - staticbox.MinEdge.Y, - movingbox.MaxEdge.Z - staticbox.MinEdge.Z + movingbox.MaxEdge.X - movingbox.MinEdge.X + staticbox.MaxEdge.X - staticbox.MinEdge.X, // sum of the widths + movingbox.MaxEdge.Y - movingbox.MinEdge.Y + staticbox.MaxEdge.Y - staticbox.MinEdge.Y, + movingbox.MaxEdge.Z - movingbox.MinEdge.Z + staticbox.MaxEdge.Z - staticbox.MinEdge.Z, + std::max(movingbox.MaxEdge.X, staticbox.MaxEdge.X) - std::min(movingbox.MinEdge.X, staticbox.MinEdge.X), //outer bounding 'box' dimensions + std::max(movingbox.MaxEdge.Y, staticbox.MaxEdge.Y) - std::min(movingbox.MinEdge.Y, staticbox.MinEdge.Y), + std::max(movingbox.MaxEdge.Z, staticbox.MaxEdge.Z) - std::min(movingbox.MinEdge.Z, staticbox.MinEdge.Z) ); - if(speed.X > 0) // Check for collision with X- plane - { - if (relbox.MaxEdge.X <= d) { - *dtime = -relbox.MaxEdge.X / speed.X; - if ((relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) && - (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) && - (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_X; - } - else if(relbox.MinEdge.X > xsize) - { - return COLLISION_AXIS_NONE; - } - } - else if(speed.X < 0) // Check for collision with X+ plane - { - if (relbox.MinEdge.X >= xsize - d) { - *dtime = (xsize - relbox.MinEdge.X) / speed.X; - if ((relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) && - (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) && - (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_X; - } - else if(relbox.MaxEdge.X < 0) - { - return COLLISION_AXIS_NONE; + const f32 dtime_max = *dtime; + const f32 inner_margin = -1.5f; + f32 distance; + f32 time; + + if (speed.X) { + distance = relbox.MaxEdge.X - relbox.MinEdge.X; + + *dtime = distance >= 0 ? std::abs(distance / speed.X) : -std::abs(distance / speed.X); + time = std::max(*dtime, 0.0f); + + if (distance > inner_margin) { + if (*dtime <= dtime_max) { + if ((speed.X > 0 && staticbox.MaxEdge.X > movingbox.MaxEdge.X) || + (speed.X < 0 && staticbox.MinEdge.X < movingbox.MinEdge.X)) { + if ( + (std::max(movingbox.MaxEdge.Y + speed.Y * time, staticbox.MaxEdge.Y) + - std::min(movingbox.MinEdge.Y + speed.Y * time, staticbox.MinEdge.Y) + - relbox.MinEdge.Y < 0) && + (std::max(movingbox.MaxEdge.Z + speed.Z * time, staticbox.MaxEdge.Z) + - std::min(movingbox.MinEdge.Z + speed.Z * time, staticbox.MinEdge.Z) + - relbox.MinEdge.Z < 0) + ) + return COLLISION_AXIS_X; + } + } else { + return COLLISION_AXIS_NONE; + } } } // NO else if here - if(speed.Y > 0) // Check for collision with Y- plane - { - if (relbox.MaxEdge.Y <= d) { - *dtime = -relbox.MaxEdge.Y / speed.Y; - if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) && - (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) && - (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_Y; - } - else if(relbox.MinEdge.Y > ysize) - { - return COLLISION_AXIS_NONE; - } - } - else if(speed.Y < 0) // Check for collision with Y+ plane - { - if (relbox.MinEdge.Y >= ysize - d) { - *dtime = (ysize - relbox.MinEdge.Y) / speed.Y; - if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) && - (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) && - (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_Y; - } - else if(relbox.MaxEdge.Y < 0) - { - return COLLISION_AXIS_NONE; + if (speed.Y) { + distance = relbox.MaxEdge.Y - relbox.MinEdge.Y; + + *dtime = distance >= 0 ? std::abs(distance / speed.Y) : -std::abs(distance / speed.Y); + time = std::max(*dtime, 0.0f); + + if (distance > inner_margin) { + if (*dtime <= dtime_max) { + if ((speed.Y > 0 && staticbox.MaxEdge.Y > movingbox.MaxEdge.Y) || + (speed.Y < 0 && staticbox.MinEdge.Y < movingbox.MinEdge.Y)) { + if ( + (std::max(movingbox.MaxEdge.X + speed.X * time, staticbox.MaxEdge.X) + - std::min(movingbox.MinEdge.X + speed.X * time, staticbox.MinEdge.X) + - relbox.MinEdge.X < 0) && + (std::max(movingbox.MaxEdge.Z + speed.Z * time, staticbox.MaxEdge.Z) + - std::min(movingbox.MinEdge.Z + speed.Z * time, staticbox.MinEdge.Z) + - relbox.MinEdge.Z < 0) + ) + return COLLISION_AXIS_Y; + } + } else { + return COLLISION_AXIS_NONE; + } } } // NO else if here - if(speed.Z > 0) // Check for collision with Z- plane - { - if (relbox.MaxEdge.Z <= d) { - *dtime = -relbox.MaxEdge.Z / speed.Z; - if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) && - (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) && - (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_Z; - } - //else if(relbox.MinEdge.Z > zsize) - //{ - // return COLLISION_AXIS_NONE; - //} - } - else if(speed.Z < 0) // Check for collision with Z+ plane - { - if (relbox.MinEdge.Z >= zsize - d) { - *dtime = (zsize - relbox.MinEdge.Z) / speed.Z; - if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) && - (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) && - (relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) && - (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO)) - return COLLISION_AXIS_Z; + if (speed.Z) { + distance = relbox.MaxEdge.Z - relbox.MinEdge.Z; + + *dtime = distance >= 0 ? std::abs(distance / speed.Z) : -std::abs(distance / speed.Z); + time = std::max(*dtime, 0.0f); + + if (distance > inner_margin) { + if (*dtime <= dtime_max) { + if ((speed.Z > 0 && staticbox.MaxEdge.Z > movingbox.MaxEdge.Z) || + (speed.Z < 0 && staticbox.MinEdge.Z < movingbox.MinEdge.Z)) { + if ( + (std::max(movingbox.MaxEdge.X + speed.X * time, staticbox.MaxEdge.X) + - std::min(movingbox.MinEdge.X + speed.X * time, staticbox.MinEdge.X) + - relbox.MinEdge.X < 0) && + (std::max(movingbox.MaxEdge.Y + speed.Y * time, staticbox.MaxEdge.Y) + - std::min(movingbox.MinEdge.Y + speed.Y * time, staticbox.MinEdge.Y) + - relbox.MinEdge.Y < 0) + ) + return COLLISION_AXIS_Z; + } + } } - //else if(relbox.MaxEdge.Z < 0) - //{ - // return COLLISION_AXIS_NONE; - //} } return COLLISION_AXIS_NONE; @@ -405,22 +386,25 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, } } } +#ifndef SERVER + if (self && c_env) { + LocalPlayer *lplayer = c_env->getLocalPlayer(); + if (lplayer->getParent() == nullptr) { + aabb3f lplayer_collisionbox = lplayer->getCollisionbox(); + v3f lplayer_pos = lplayer->getPosition(); + lplayer_collisionbox.MinEdge += lplayer_pos; + lplayer_collisionbox.MaxEdge += lplayer_pos; + cinfo.emplace_back(false, true, 0, v3s16(), lplayer_collisionbox); + } + } +#endif } //tt3 /* Collision detection */ - /* - Collision uncertainty radius - Make it a bit larger than the maximum distance of movement - */ - f32 d = pos_max_d * 1.1f; - // A fairly large value in here makes moving smoother - //f32 d = 0.15*BS; - - // This should always apply, otherwise there are glitches - assert(d > pos_max_d); // invariant + f32 d = 0.0f; int loopcount = 0; @@ -450,9 +434,9 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, continue; // Find nearest collision of the two boxes (raytracing-like) - f32 dtime_tmp; + f32 dtime_tmp = nearest_dtime; CollisionAxis collided = axisAlignedCollision(box_info.box, - movingbox, *speed_f, d, &dtime_tmp); + movingbox, *speed_f, &dtime_tmp); if (collided == -1 || dtime_tmp >= nearest_dtime) continue; @@ -470,11 +454,18 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Otherwise, a collision occurred. NearbyCollisionInfo &nearest_info = cinfo[nearest_boxindex]; const aabb3f& cbox = nearest_info.box; + + //movingbox except moved to the horizontal position it would be after step up + aabb3f stepbox = movingbox; + stepbox.MinEdge.X += speed_f->X * dtime; + stepbox.MinEdge.Z += speed_f->Z * dtime; + stepbox.MaxEdge.X += speed_f->X * dtime; + stepbox.MaxEdge.Z += speed_f->Z * dtime; // Check for stairs. bool step_up = (nearest_collided != COLLISION_AXIS_Y) && // must not be Y direction (movingbox.MinEdge.Y < cbox.MaxEdge.Y) && (movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) && - (!wouldCollideWithCeiling(cinfo, movingbox, + (!wouldCollideWithCeiling(cinfo, stepbox, cbox.MaxEdge.Y - movingbox.MinEdge.Y, d)); @@ -483,7 +474,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Move to the point of collision and reduce dtime by nearest_dtime if (nearest_dtime < 0) { - // Handle negative nearest_dtime (can be caused by the d allowance) + // Handle negative nearest_dtime if (!step_up) { if (nearest_collided == COLLISION_AXIS_X) pos_f->X += speed_f->X * nearest_dtime; @@ -562,9 +553,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, Object touches ground if object's minimum Y is near node's maximum Y and object's X-Z-area overlaps with the node's X-Z-area. - - Use 0.15*BS so that it is easier to get on a node. */ + if (cbox.MaxEdge.X - d > box.MinEdge.X && cbox.MinEdge.X + d < box.MaxEdge.X && cbox.MaxEdge.Z - d > box.MinEdge.Z && cbox.MinEdge.Z + d < box.MaxEdge.Z) { @@ -574,7 +564,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, box.MinEdge += *pos_f; box.MaxEdge += *pos_f; } - if (std::fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.15f * BS) { + if (std::fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.05f) { result.touching_ground = true; if (box_info.is_object) diff --git a/src/collision.h b/src/collision.h index 85df02c96..fa47cccc1 100644 --- a/src/collision.h +++ b/src/collision.h @@ -77,7 +77,7 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef, // dtime receives time until first collision, invalid if -1 is returned CollisionAxis axisAlignedCollision( const aabb3f &staticbox, const aabb3f &movingbox, - const v3f &speed, f32 d, f32 *dtime); + const v3f &speed, f32 *dtime); // Helper function: // Checks if moving the movingbox up by the given distance would hit a ceiling. diff --git a/src/unittest/test_collision.cpp b/src/unittest/test_collision.cpp index 332d3fa13..2f39c2489 100644 --- a/src/unittest/test_collision.cpp +++ b/src/unittest/test_collision.cpp @@ -50,38 +50,38 @@ void TestCollision::testAxisAlignedCollision() aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); v3f v(1, 0, 0); - f32 dtime = 0; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 1.000) < 0.001); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); v3f v(-1, 0, 0); - f32 dtime = 0; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1); v3f v(1, 0, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); v3f v(0.5, 0.1, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 3.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 3.000) < 0.001); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); v3f v(0.5, 0.1, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 3.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 3.000) < 0.001); } @@ -90,38 +90,38 @@ void TestCollision::testAxisAlignedCollision() aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); v3f v(-1, 0, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 1.000) < 0.001); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); v3f v(1, 0, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5); v3f v(-1, 0, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); v3f v(-0.5, 0.2, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); // Y, not X! + f32 dtime = 2.5f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 1); // Y, not X! UASSERT(fabs(dtime - 2.500) < 0.001); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); v3f v(-0.5, 0.3, 0); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 2.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 2.000) < 0.001); } @@ -132,48 +132,48 @@ void TestCollision::testAxisAlignedCollision() aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 0.9) < 0.001); } { aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 1); UASSERT(fabs(dtime - 0.9) < 0.001); } { aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2); + f32 dtime = 1.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 2); UASSERT(fabs(dtime - 0.9) < 0.001); } { aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29); v3f v(1./7, 1./7, 1./7); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); + f32 dtime = 17.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 0); UASSERT(fabs(dtime - 16.1) < 0.001); } { aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29); v3f v(1./7, 1./7, 1./7); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); + f32 dtime = 17.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 1); UASSERT(fabs(dtime - 16.1) < 0.001); } { aabb3f s(bx, by, bz, bx+2, by+2, bz+2); aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3); v3f v(1./7, 1./7, 1./7); - f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2); + f32 dtime = 17.0f; + UASSERT(axisAlignedCollision(s, m, v, &dtime) == 2); UASSERT(fabs(dtime - 16.1) < 0.001); } } -- cgit v1.2.3 From f648fb76aef96a1da608c64346fc65d4dd44caa8 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Fri, 10 Apr 2020 19:49:20 +0200 Subject: Drop genericobject.{cpp,h} (#9629) * Drop genericobject.{cpp,h} This file is not for generic object but for ActiveObject message passing. Put ownership of the various commands to the right objects and cleanup the related code. * Protect ServerActiveObject::m_messages_out * typo fix --- build/android/jni/Android.mk | 1 - src/CMakeLists.txt | 1 - src/activeobject.h | 16 ++ src/client/clientenvironment.cpp | 2 +- src/client/content_cao.cpp | 38 +++-- src/client/content_cao.h | 1 + src/content_sao.cpp | 266 ++++++++++++++++++++++++--------- src/content_sao.h | 19 +++ src/genericobject.cpp | 207 ------------------------- src/genericobject.h | 87 ----------- src/network/networkprotocol.h | 8 +- src/server.cpp | 7 +- src/serverenvironment.cpp | 5 +- src/serverobject.cpp | 31 ++++ src/serverobject.h | 16 +- util/travis/clang-format-whitelist.txt | 2 - 16 files changed, 300 insertions(+), 407 deletions(-) delete mode 100644 src/genericobject.cpp delete mode 100644 src/genericobject.h (limited to 'src/client') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 72b0daab6..a2f32440a 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -176,7 +176,6 @@ LOCAL_SRC_FILES := \ jni/src/environment.cpp \ jni/src/face_position_cache.cpp \ jni/src/filesys.cpp \ - jni/src/genericobject.cpp \ jni/src/gettext.cpp \ jni/src/gui/guiAnimatedImage.cpp \ jni/src/gui/guiBackgroundImage.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d579bb965..4b1d6d647 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -384,7 +384,6 @@ set(common_SRCS environment.cpp face_position_cache.cpp filesys.cpp - genericobject.cpp gettext.cpp httpfetch.cpp hud.cpp diff --git a/src/activeobject.h b/src/activeobject.h index 4a2de92cd..c83243f86 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -55,6 +55,22 @@ struct ActiveObjectMessage std::string datastring; }; +enum ActiveObjectCommand { + AO_CMD_SET_PROPERTIES, + AO_CMD_UPDATE_POSITION, + AO_CMD_SET_TEXTURE_MOD, + AO_CMD_SET_SPRITE, + AO_CMD_PUNCHED, + AO_CMD_UPDATE_ARMOR_GROUPS, + AO_CMD_SET_ANIMATION, + AO_CMD_SET_BONE_POSITION, + AO_CMD_ATTACH_TO, + AO_CMD_SET_PHYSICS_OVERRIDE, + AO_CMD_UPDATE_NAMETAG_ATTRIBUTES, + AO_CMD_SPAWN_INFANT, + AO_CMD_SET_ANIMATION_SPEED +}; + /* Parent class for ServerActiveObject and ClientActiveObject */ diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 52d133781..6840f2db3 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -450,7 +450,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, // Object initialized: if ((obj = getActiveObject(new_id))) { // Final step is to update all children which are already known - // Data provided by GENERIC_CMD_SPAWN_INFANT + // Data provided by AO_CMD_SPAWN_INFANT const auto &children = obj->getAttachmentChildIds(); for (auto c_id : children) { if (auto *o = getActiveObject(c_id)) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 8509eccb5..798899f9a 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -304,7 +304,6 @@ void TestCAO::processMessage(const std::string &data) GenericCAO */ -#include "genericobject.h" #include "clientobject.h" GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): @@ -1421,14 +1420,23 @@ void GenericCAO::updateAttachments() } } +void GenericCAO::readAOMessageProperties(std::istream &is) +{ + // Reset object properties first + m_prop = ObjectProperties(); + + // Then read the whole new stream + m_prop.deSerialize(is); +} + void GenericCAO::processMessage(const std::string &data) { //infostream<<"GenericCAO: Got message"<physics_override_sneak_glitch = sneak_glitch; player->physics_override_new_move = new_move; } - } else if (cmd == GENERIC_CMD_SET_ANIMATION) { + } else if (cmd == AO_CMD_SET_ANIMATION) { // TODO: change frames send as v2s32 value v2f range = readV2F32(is); if (!m_is_local_player) { @@ -1565,17 +1573,17 @@ void GenericCAO::processMessage(const std::string &data) updateAnimation(); } } - } else if (cmd == GENERIC_CMD_SET_ANIMATION_SPEED) { + } else if (cmd == AO_CMD_SET_ANIMATION_SPEED) { m_animation_speed = readF32(is); updateAnimationSpeed(); - } else if (cmd == GENERIC_CMD_SET_BONE_POSITION) { + } else if (cmd == AO_CMD_SET_BONE_POSITION) { std::string bone = deSerializeString(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d(position, rotation); updateBonePosition(); - } else if (cmd == GENERIC_CMD_ATTACH_TO) { + } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString(is); v3f position = readV3F32(is); @@ -1586,7 +1594,7 @@ void GenericCAO::processMessage(const std::string &data) // localplayer itself can't be attached to localplayer if (!m_is_local_player) m_is_visible = !m_attached_to_local; - } else if (cmd == GENERIC_CMD_PUNCHED) { + } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); // Use this instead of the send damage to not interfere with prediction @@ -1624,7 +1632,7 @@ void GenericCAO::processMessage(const std::string &data) if (!m_is_player) clearChildAttachments(); } - } else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) { + } else if (cmd == AO_CMD_UPDATE_ARMOR_GROUPS) { m_armor_groups.clear(); int armor_groups_size = readU16(is); for(int i=0; inametag_pos = pos; } - } else if (cmd == GENERIC_CMD_SPAWN_INFANT) { + } else if (cmd == AO_CMD_SPAWN_INFANT) { u16 child_id = readU16(is); u8 type = readU8(is); // maybe this will be useful later (void)type; diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 7c29cbf17..c53b81433 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -68,6 +68,7 @@ struct SmoothTranslatorWrappedv3f : SmoothTranslator class GenericCAO : public ClientActiveObject { private: + void readAOMessageProperties(std::istream &is); // Only set at initialization std::string m_name = ""; bool m_is_player = false; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 680bf372a..be7674f52 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server.h" #include "scripting_server.h" -#include "genericobject.h" #include "settings.h" #include #include @@ -289,6 +288,120 @@ void UnitSAO::notifyObjectPropertiesModified() m_properties_sent = false; } +std::string UnitSAO::generateUpdateAttachmentCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_ATTACH_TO); + // parameters + writeS16(os, m_attachment_parent_id); + os << serializeString(m_attachment_bone); + writeV3F32(os, m_attachment_position); + writeV3F32(os, m_attachment_rotation); + return os.str(); +} + +std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_BONE_POSITION); + // parameters + os << serializeString(bone); + writeV3F32(os, position); + writeV3F32(os, rotation); + return os.str(); +} + + +std::string UnitSAO::generateUpdateAnimationSpeedCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION_SPEED); + // parameters + writeF32(os, m_animation_speed); + return os.str(); +} + +std::string UnitSAO::generateUpdateAnimationCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION); + // parameters + writeV2F32(os, m_animation_range); + writeF32(os, m_animation_speed); + writeF32(os, m_animation_blend); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_animation_loop); + return os.str(); +} + + +std::string UnitSAO::generateUpdateArmorGroupsCommand() const +{ + std::ostringstream os(std::ios::binary); + writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); + writeU16(os, m_armor_groups.size()); + for (const auto &armor_group : m_armor_groups) { + os<>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = gob_cmd_update_bone_position((*ii).first, + std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list ActiveObjectMessage aom(getId(), true, str); @@ -538,7 +647,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); + std::string str = generateUpdateAttachmentCommand(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); @@ -560,16 +669,14 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) std::ostringstream msg_os(std::ios::binary); msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 - msg_os << serializeLongString(gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 int message_count = 4 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); (ii != m_attachment_child_ids.end()); ++ii) { @@ -577,12 +684,11 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) message_count++; // TODO after a protocol bump: only send the object initialization data // to older clients (superfluous since this message exists) - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); } } - msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier)); + msg_os << serializeLongString(generateSetTextureModCommand()); message_count++; writeU8(os, message_count); @@ -655,10 +761,8 @@ u16 LuaEntitySAO::punch(v3f dir, setHP((s32)getHP() - result.damage, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); } } @@ -750,11 +854,9 @@ v3f LuaEntitySAO::getAcceleration() void LuaEntitySAO::setTextureMod(const std::string &mod) { - std::string str = gob_cmd_set_texture_mod(mod); m_current_texture_modifier = mod; // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); } std::string LuaEntitySAO::getTextureMod() const @@ -762,18 +864,42 @@ std::string LuaEntitySAO::getTextureMod() const return m_current_texture_modifier; } + +std::string LuaEntitySAO::generateSetTextureModCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_TEXTURE_MOD); + // parameters + os << serializeString(m_current_texture_modifier); + return os.str(); +} + +std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_SPRITE); + // parameters + writeV2S16(os, p); + writeU16(os, num_frames); + writeF32(os, framelength); + writeU8(os, select_horiz_by_yawpitch); + return os.str(); +} + void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch) { - std::string str = gob_cmd_set_sprite( + std::string str = generateSetSpriteCommand( p, num_frames, framelength, select_horiz_by_yawpitch ); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } std::string LuaEntitySAO::getName() @@ -783,7 +909,7 @@ std::string LuaEntitySAO::getName() std::string LuaEntitySAO::getPropertyPacket() { - return gob_cmd_set_properties(m_prop); + return generateSetPropertiesCommand(m_prop); } void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) @@ -802,7 +928,7 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) float update_interval = m_env->getSendRecommendedInterval(); - std::string str = gob_cmd_update_position( + std::string str = generateUpdatePositionCommand( m_base_position, m_velocity, m_acceleration, @@ -812,8 +938,7 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) update_interval ); // create message and add to list - ActiveObjectMessage aom(getId(), false, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), false, str); } bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const @@ -949,28 +1074,23 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) std::ostringstream msg_os(std::ios::binary); msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 - msg_os << serializeLongString(gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 - msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, - m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak, - m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5 - // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. + msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 int message_count = 6 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); ii != m_attachment_child_ids.end(); ++ii) { if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { message_count++; - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); } } @@ -1116,7 +1236,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) else pos = m_base_position; - std::string str = gob_cmd_update_position( + std::string str = generateUpdatePositionCommand( pos, v3f(0.0f, 0.0f, 0.0f), v3f(0.0f, 0.0f, 0.0f), @@ -1126,61 +1246,63 @@ void PlayerSAO::step(float dtime, bool send_recommended) update_interval ); // create message and add to list - ActiveObjectMessage aom(getId(), false, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), false, str); } if (!m_armor_groups_sent) { m_armor_groups_sent = true; - std::string str = gob_cmd_update_armor_groups( - m_armor_groups); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); } if (!m_physics_override_sent) { m_physics_override_sent = true; - std::string str = gob_cmd_update_physics_override(m_physics_override_speed, - m_physics_override_jump, m_physics_override_gravity, - m_physics_override_sneak, m_physics_override_sneak_glitch, - m_physics_override_new_move); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); } if (!m_animation_sent) { m_animation_sent = true; - std::string str = gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); } if (!m_bone_position_sent) { m_bone_position_sent = true; for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = gob_cmd_update_bone_position((*ii).first, + std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation); + std::string str = generateUpdateAttachmentCommand(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } } +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF32(os, m_physics_override_speed); + writeF32(os, m_physics_override_jump); + writeF32(os, m_physics_override_gravity); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_physics_override_sneak); + writeU8(os, !m_physics_override_sneak_glitch); + writeU8(os, !m_physics_override_new_move); + return os.str(); +} + void PlayerSAO::setBasePosition(const v3f &position) { if (m_player && position != m_base_position) @@ -1284,10 +1406,8 @@ u16 PlayerSAO::punch(v3f dir, // No effect if PvP disabled or if immortal if (isImmortal() || !g_settings->getBool("enable_pvp")) { if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); return 0; } } @@ -1307,10 +1427,8 @@ u16 PlayerSAO::punch(v3f dir, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); } else { // override client prediction if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); } } @@ -1408,7 +1526,7 @@ void PlayerSAO::unlinkPlayerSessionAndSave() std::string PlayerSAO::getPropertyPacket() { m_prop.is_visible = (true); - return gob_cmd_set_properties(m_prop); + return generateSetPropertiesCommand(m_prop); } void PlayerSAO::setMaxSpeedOverride(const v3f &vel) diff --git a/src/content_sao.h b/src/content_sao.h index e9047daf0..e0304299a 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -67,6 +67,19 @@ public: ServerActiveObject *getParent() const; ObjectProperties* accessObjectProperties(); void notifyObjectPropertiesModified(); + + std::string generateUpdateAttachmentCommand() const; + std::string generateUpdateAnimationSpeedCommand() const; + std::string generateUpdateAnimationCommand() const; + std::string generateUpdateArmorGroupsCommand() const; + static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, + const v3f &acceleration, const v3f &rotation, bool do_interpolate, + bool is_movement_end, f32 update_interval); + std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; + void sendPunchCommand(); + static std::string generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation); + protected: u16 m_hp = 1; @@ -98,6 +111,8 @@ protected: private: void onAttach(int parent_id); void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; }; /* @@ -155,6 +170,9 @@ public: private: std::string getPropertyPacket(); void sendPosition(bool do_interpolate, bool is_movement_end); + std::string generateSetTextureModCommand() const; + static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, f32 framelength, + bool select_horiz_by_yawpitch); std::string m_init_name; std::string m_init_state; @@ -350,6 +368,7 @@ public: private: std::string getPropertyPacket(); void unlinkPlayerSessionAndSave(); + std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; session_t m_peer_id = 0; diff --git a/src/genericobject.cpp b/src/genericobject.cpp deleted file mode 100644 index 49d16001f..000000000 --- a/src/genericobject.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -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 "genericobject.h" -#include -#include "util/serialize.h" - -std::string gob_cmd_set_properties(const ObjectProperties &prop) -{ - std::ostringstream os(std::ios::binary); - writeU8(os, GENERIC_CMD_SET_PROPERTIES); - prop.serialize(os); - return os.str(); -} - -ObjectProperties gob_read_set_properties(std::istream &is) -{ - ObjectProperties prop; - prop.deSerialize(is); - return prop; -} - -std::string gob_cmd_update_position( - v3f position, - v3f velocity, - v3f acceleration, - v3f rotation, - bool do_interpolate, - bool is_movement_end, - f32 update_interval -){ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, GENERIC_CMD_UPDATE_POSITION); - // pos - writeV3F32(os, position); - // velocity - writeV3F32(os, velocity); - // acceleration - writeV3F32(os, acceleration); - // rotation - writeV3F32(os, rotation); - // do_interpolate - writeU8(os, do_interpolate); - // is_end_position (for interpolation) - writeU8(os, is_movement_end); - // update_interval (for interpolation) - writeF32(os, update_interval); - return os.str(); -} - -std::string gob_cmd_set_texture_mod(const std::string &mod) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, GENERIC_CMD_SET_TEXTURE_MOD); - // parameters - os< - -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 -#include "irrlichttypes_bloated.h" -#include -#include "itemgroup.h" - -enum GenericCMD { - GENERIC_CMD_SET_PROPERTIES, - GENERIC_CMD_UPDATE_POSITION, - GENERIC_CMD_SET_TEXTURE_MOD, - GENERIC_CMD_SET_SPRITE, - GENERIC_CMD_PUNCHED, - GENERIC_CMD_UPDATE_ARMOR_GROUPS, - GENERIC_CMD_SET_ANIMATION, - GENERIC_CMD_SET_BONE_POSITION, - GENERIC_CMD_ATTACH_TO, - GENERIC_CMD_SET_PHYSICS_OVERRIDE, - GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES, - GENERIC_CMD_SPAWN_INFANT, - GENERIC_CMD_SET_ANIMATION_SPEED -}; - -#include "object_properties.h" -std::string gob_cmd_set_properties(const ObjectProperties &prop); -ObjectProperties gob_read_set_properties(std::istream &is); - -std::string gob_cmd_update_position( - v3f position, - v3f velocity, - v3f acceleration, - v3f rotation, - bool do_interpolate, - bool is_movement_end, - f32 update_interval -); - -std::string gob_cmd_set_texture_mod(const std::string &mod); - -std::string gob_cmd_set_sprite( - v2s16 p, - u16 num_frames, - f32 framelength, - bool select_horiz_by_yawpitch -); - -std::string gob_cmd_punched(u16 result_hp); - -std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups); - -std::string gob_cmd_update_physics_override(float physics_override_speed, - float physics_override_jump, float physics_override_gravity, - bool sneak, bool sneak_glitch, bool new_move); - -std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop); - -std::string gob_cmd_update_animation_speed(float frame_speed); - -std::string gob_cmd_update_bone_position(const std::string &bone, v3f position, - v3f rotation); - -std::string gob_cmd_update_attachment(int parent_id, const std::string &bone, - v3f position, v3f rotation); - -std::string gob_cmd_update_nametag_attributes(video::SColor color); - -std::string gob_cmd_update_infant(u16 id, u8 type, - const std::string &client_initialization_data); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index d3799868b..7223ce05c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -70,8 +70,8 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL_VERSION 14: Added transfer of player pressed keys to the server Added new messages for mesh and bone animation, as well as attachments - GENERIC_CMD_SET_ANIMATION - GENERIC_CMD_SET_BONE_POSITION + AO_CMD_SET_ANIMATION + AO_CMD_SET_BONE_POSITION GENERIC_CMD_SET_ATTACHMENT PROTOCOL_VERSION 15: Serialization format changes @@ -87,7 +87,7 @@ with this program; if not, write to the Free Software Foundation, Inc., damageGroups added to ToolCapabilities sound_place added to ItemDefinition PROTOCOL_VERSION 19: - GENERIC_CMD_SET_PHYSICS_OVERRIDE + AO_CMD_SET_PHYSICS_OVERRIDE PROTOCOL_VERSION 20: TOCLIENT_HUDADD TOCLIENT_HUDRM @@ -131,7 +131,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Add TOCLIENT_HELLO for presenting server to client after client presentation Add TOCLIENT_AUTH_ACCEPT to accept connection from client - Rename GENERIC_CMD_SET_ATTACHMENT to GENERIC_CMD_ATTACH_TO + Rename GENERIC_CMD_SET_ATTACHMENT to AO_CMD_ATTACH_TO PROTOCOL_VERSION 26: Add TileDef tileable_horizontal, tileable_vertical flags PROTOCOL_VERSION 27: diff --git a/src/server.cpp b/src/server.cpp index 9eea45b31..062fe0798 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -35,7 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "mapblock.h" #include "serverobject.h" -#include "genericobject.h" #include "settings.h" #include "profiler.h" #include "log.h" @@ -721,7 +720,7 @@ void Server::AsyncRunStep(bool initial_step) // Go through every message for (const ActiveObjectMessage &aom : *list) { // Send position updates to players who do not see the attachment - if (aom.datastring[0] == GENERIC_CMD_UPDATE_POSITION) { + if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) { if (sao->getId() == player->getId()) continue; @@ -1819,9 +1818,7 @@ void Server::SendPlayerHP(session_t peer_id) m_script->player_event(playersao,"health_changed"); // Send to other clients - std::string str = gob_cmd_punched(playersao->getHP()); - ActiveObjectMessage aom(playersao->getId(), true, str); - playersao->m_messages_out.push(aom); + playersao->sendPunchCommand(); } void Server::SendPlayerBreath(PlayerSAO *sao) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 333d32ff5..2d3ee078e 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1420,10 +1420,7 @@ void ServerEnvironment::step(float dtime) // Step object obj->step(dtime, send_recommended); // Read messages from object - while (!obj->m_messages_out.empty()) { - this->m_active_object_messages.push(obj->m_messages_out.front()); - obj->m_messages_out.pop(); - } + obj->dumpAOMessagesToQueue(m_active_object_messages); }; m_ao_manager.step(dtime, cb_state); } diff --git a/src/serverobject.cpp b/src/serverobject.cpp index 1ed33f66b..119a41b7b 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -81,3 +81,34 @@ bool ServerActiveObject::setWieldedItem(const ItemStack &item) { return false; } + +std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SPAWN_INFANT); + // parameters + writeU16(os, infant_id); + writeU8(os, getSendType()); + os << serializeLongString(getClientInitializationData(protocol_version)); + return os.str(); +} + +std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); + // parameters + writeU8(os, 1); // version for forward compatibility + writeARGB8(os, color); + return os.str(); +} + +void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) +{ + while (!m_messages_out.empty()) { + queue.push(m_messages_out.front()); + m_messages_out.pop(); + } +} \ No newline at end of file diff --git a/src/serverobject.h b/src/serverobject.h index 48689fcb4..2e013a6b6 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -113,7 +113,7 @@ public: The return value of this is passed to the client-side object when it is created */ - virtual std::string getClientInitializationData(u16 protocol_version){return "";} + virtual std::string getClientInitializationData(u16 protocol_version) {return "";} /* The return value of this is passed to the server-side object @@ -192,6 +192,10 @@ public: m_attached_particle_spawners.erase(id); } + std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); + std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; + + void dumpAOMessagesToQueue(std::queue &queue); /* Number of players which know about this object. Object won't be @@ -236,11 +240,6 @@ public: */ v3s16 m_static_block = v3s16(1337,1337,1337); - /* - Queue of messages to be sent to the client - */ - std::queue m_messages_out; - protected: virtual void onAttach(int parent_id) {} virtual void onDetach(int parent_id) {} @@ -255,6 +254,11 @@ protected: v3f m_base_position; std::unordered_set m_attached_particle_spawners; + /* + Queue of messages to be sent to the client + */ + std::queue m_messages_out; + private: // Used for creating objects based on type static std::map m_types; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 05b4a96c4..7b2fd8236 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -151,8 +151,6 @@ src/fontengine.h src/game.cpp src/gamedef.h src/game.h -src/genericobject.cpp -src/genericobject.h src/gettext.cpp src/gettext.h src/gui/guiAnimatedImage.cpp -- cgit v1.2.3 From 35e778ee9ff2e0e9ab096eaf2d1a903c5eaa42b3 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Fri, 10 Apr 2020 20:10:51 +0200 Subject: Move clientsimpleobject.h to client folder (#9630) This file is only called from client folder, retrieve its friends :) --- src/client/clientsimpleobject.h | 35 +++++++++++++++++++++++++++++++++++ src/clientsimpleobject.h | 35 ----------------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 src/client/clientsimpleobject.h delete mode 100644 src/clientsimpleobject.h (limited to 'src/client') diff --git a/src/client/clientsimpleobject.h b/src/client/clientsimpleobject.h new file mode 100644 index 000000000..f4a40bcd3 --- /dev/null +++ b/src/client/clientsimpleobject.h @@ -0,0 +1,35 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +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_bloated.h" +class ClientEnvironment; + +class ClientSimpleObject +{ +protected: +public: + bool m_to_be_removed = false; + + ClientSimpleObject() = default; + virtual ~ClientSimpleObject() = default; + + virtual void step(float dtime) {} +}; diff --git a/src/clientsimpleobject.h b/src/clientsimpleobject.h deleted file mode 100644 index f4a40bcd3..000000000 --- a/src/clientsimpleobject.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -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_bloated.h" -class ClientEnvironment; - -class ClientSimpleObject -{ -protected: -public: - bool m_to_be_removed = false; - - ClientSimpleObject() = default; - virtual ~ClientSimpleObject() = default; - - virtual void step(float dtime) {} -}; -- cgit v1.2.3 From f105bc8dc2444d98a9cd74a2caa0013ce2e07008 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 21:45:07 +0200 Subject: A few initialization cleanups --- src/client/client.cpp | 8 ++++++-- src/client/game.cpp | 5 ++++- src/script/scripting_client.cpp | 8 +++++--- src/script/scripting_client.h | 3 +++ 4 files changed, 18 insertions(+), 6 deletions(-) (limited to 'src/client') diff --git a/src/client/client.cpp b/src/client/client.cpp index c9cd24cb3..c3e2a4d2a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -178,7 +178,7 @@ void Client::loadMods() infostream << mod.name << " "; infostream << std::endl; - // Load and run "mod" scripts + // Load "mod" scripts for (const ModSpec &mod : m_mods) { if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + @@ -188,7 +188,7 @@ void Client::loadMods() scanModIntoMemory(mod.name, mod.path); } - // Load and run "mod" scripts + // Run them for (const ModSpec &mod : m_mods) m_script->loadModFromMemory(mod.name); @@ -197,10 +197,14 @@ void Client::loadMods() // Run a callback when mods are loaded m_script->on_mods_loaded(); + + // Create objects if they're ready if (m_state == LC_Ready) m_script->on_client_ready(m_env.getLocalPlayer()); if (m_camera) m_script->on_camera_ready(m_camera); + if (m_minimap) + m_script->on_minimap_ready(m_minimap); } bool Client::checkBuiltinIntegrity() diff --git a/src/client/game.cpp b/src/client/game.cpp index 437cc7871..505108caf 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1407,8 +1407,11 @@ bool Game::createClient(const std::string &playername, } mapper = client->getMinimap(); - if (mapper) + if (mapper) { mapper->setMinimapMode(MINIMAP_MODE_OFF); + if (client->modsLoaded()) + client->getScript()->on_minimap_ready(mapper); + } return true; } diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp index 1288b1df7..6643a9509 100644 --- a/src/script/scripting_client.cpp +++ b/src/script/scripting_client.cpp @@ -55,9 +55,6 @@ ClientScripting::ClientScripting(Client *client): InitializeModApi(L, top); lua_pop(L, 1); - if (client->getMinimap()) - LuaMinimap::create(L, client->getMinimap()); - // Push builtin initialization type lua_pushstring(L, "client"); lua_setglobal(L, "INIT"); @@ -94,3 +91,8 @@ void ClientScripting::on_camera_ready(Camera *camera) { LuaCamera::create(getStack(), camera); } + +void ClientScripting::on_minimap_ready(Minimap *minimap) +{ + LuaMinimap::create(getStack(), minimap); +} diff --git a/src/script/scripting_client.h b/src/script/scripting_client.h index cfecfa165..3088029f0 100644 --- a/src/script/scripting_client.h +++ b/src/script/scripting_client.h @@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class Client; class LocalPlayer; class Camera; +class Minimap; + class ClientScripting: virtual public ScriptApiBase, public ScriptApiSecurity, @@ -38,6 +40,7 @@ public: ClientScripting(Client *client); void on_client_ready(LocalPlayer *localplayer); void on_camera_ready(Camera *camera); + void on_minimap_ready(Minimap *minimap); private: virtual void InitializeModApi(lua_State *L, int top); -- cgit v1.2.3 From fbf74dc52456b13b0dbd7bb9d3206e5828254910 Mon Sep 17 00:00:00 2001 From: Alex <24834740+GreenXenith@users.noreply.github.com> Date: Sat, 11 Apr 2020 04:45:14 -0700 Subject: Use TILE_MATERIAL_ALPHA for use_texture_alpha entity flag (#9639) Fixes #9637. --- src/client/content_cao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 798899f9a..39ea4ab1e 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -584,7 +584,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) IShaderSource *shader_source = m_client->getShaderSource(); u32 shader_id = shader_source->getShader( "object_shader", - TILE_MATERIAL_BASIC, + (m_prop.use_texture_alpha) ? TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shader_source->getShaderInfo(shader_id).material; } else { -- cgit v1.2.3 From 40df3931d882daaeee42c8de69882b9f9df5c312 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 11 Apr 2020 20:03:59 +0200 Subject: Implement DPI scaling for Windows (#9586) --- misc/minetest.exe.manifest | 4 +-- misc/winresource.rc | 6 ++++ src/CMakeLists.txt | 2 +- src/client/renderingengine.cpp | 77 ++++++++++++++++++++++++++++++++---------- src/constants.h | 5 --- 5 files changed, 69 insertions(+), 25 deletions(-) (limited to 'src/client') diff --git a/misc/minetest.exe.manifest b/misc/minetest.exe.manifest index 3c32b0f8b..18f8c85f4 100644 --- a/misc/minetest.exe.manifest +++ b/misc/minetest.exe.manifest @@ -8,8 +8,8 @@ - - true + + true diff --git a/misc/winresource.rc b/misc/winresource.rc index e1e82581b..ffb493873 100644 --- a/misc/winresource.rc +++ b/misc/winresource.rc @@ -1,6 +1,8 @@ #include +#include #include #include + #ifndef USE_CMAKE_CONFIG_H #define USE_CMAKE_CONFIG_H #endif @@ -13,6 +15,10 @@ #define BUILDMODE "RUN_IN_PLACE=0" #endif +#ifdef __MINGW32__ +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "minetest.exe.manifest" +#endif + LANGUAGE 0, SUBLANG_NEUTRAL 130 ICON "minetest-icon.ico" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ebbd628c..0b550c09c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -451,7 +451,7 @@ if(WIN32) -i${WINRESOURCE_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${WINRESOURCE_FILE}) + DEPENDS ${WINRESOURCE_FILE} ${MINETEST_EXE_MANIFEST_FILE}) SET(extra_windows_SRCS ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o) else(MINGW) # Probably MSVC set(extra_windows_SRCS ${WINRESOURCE_FILE} ${MINETEST_EXE_MANIFEST_FILE}) diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 8b7bbf328..eae6ca7d3 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -45,7 +45,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#endif +#ifdef _WIN32 +#include +#include #endif #if ENABLE_GLES @@ -318,6 +322,28 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) #endif } +#ifdef _WIN32 +static bool getWindowHandle(irr::video::IVideoDriver *driver, HWND &hWnd) +{ + const video::SExposedVideoData exposedData = driver->getExposedVideoData(); + + switch (driver->getDriverType()) { + case video::EDT_DIRECT3D8: + hWnd = reinterpret_cast(exposedData.D3D8.HWnd); + break; + case video::EDT_DIRECT3D9: + hWnd = reinterpret_cast(exposedData.D3D9.HWnd); + break; + case video::EDT_OPENGL: + hWnd = reinterpret_cast(exposedData.OpenGLWin32.HWnd); + break; + default: + return false; + } + + return true; +} +#endif bool RenderingEngine::setWindowIcon() { @@ -335,22 +361,9 @@ bool RenderingEngine::setWindowIcon() "-xorg-icon-128.png"); #endif #elif defined(_WIN32) - const video::SExposedVideoData exposedData = driver->getExposedVideoData(); HWND hWnd; // Window handle - - switch (driver->getDriverType()) { - case video::EDT_DIRECT3D8: - hWnd = reinterpret_cast(exposedData.D3D8.HWnd); - break; - case video::EDT_DIRECT3D9: - hWnd = reinterpret_cast(exposedData.D3D9.HWnd); - break; - case video::EDT_OPENGL: - hWnd = reinterpret_cast(exposedData.OpenGLWin32.HWnd); - break; - default: + if (!getWindowHandle(driver, hWnd)) return false; - } // Load the ICON from resource file const HICON hicon = LoadIcon(GetModuleHandle(NULL), @@ -632,7 +645,7 @@ const char *RenderingEngine::getVideoDriverFriendlyName(irr::video::E_DRIVER_TYP } #ifndef __ANDROID__ -#ifdef XORG_USED +#if defined(XORG_USED) static float calcDisplayDensity() { @@ -667,12 +680,42 @@ float RenderingEngine::getDisplayDensity() return cached_display_density; } -#else // XORG_USED +#elif defined(_WIN32) + + +static float calcDisplayDensity(irr::video::IVideoDriver *driver) +{ + HWND hWnd; + if (getWindowHandle(driver, hWnd)) { + HDC hdc = GetDC(hWnd); + float dpi = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(hWnd, hdc); + return dpi / 96.0f; + } + + /* return manually specified dpi */ + return g_settings->getFloat("screen_dpi") / 96.0f; +} + +float RenderingEngine::getDisplayDensity() +{ + static bool cached = false; + static float display_density; + if (!cached) { + display_density = calcDisplayDensity(get_video_driver()); + cached = true; + } + return display_density; +} + +#else + float RenderingEngine::getDisplayDensity() { return g_settings->getFloat("screen_dpi") / 96.0; } -#endif // XORG_USED + +#endif v2u32 RenderingEngine::getDisplaySize() { diff --git a/src/constants.h b/src/constants.h index 7636b38e0..0a8b22e15 100644 --- a/src/constants.h +++ b/src/constants.h @@ -110,10 +110,5 @@ with this program; if not, write to the Free Software Foundation, Inc., GUI related things */ -// TODO: implement dpi-based scaling for windows and remove this hack -#if defined(_WIN32) -#define TTF_DEFAULT_FONT_SIZE (18) -#else #define TTF_DEFAULT_FONT_SIZE (16) -#endif #define DEFAULT_FONT_SIZE (10) -- cgit v1.2.3 From f780bae05cc2fdd23a6d7326c770783da8d94ea3 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Sat, 11 Apr 2020 16:39:30 -0400 Subject: Formspecs: Add state-selection to style elements (#9378) --- doc/lua_api.txt | 61 +++++++++--- games/minimal/mods/test/formspec.lua | 5 +- src/client/game.cpp | 9 +- src/gui/StyleSpec.h | 65 ++++++++++-- src/gui/guiButton.cpp | 163 +++++++++++++++--------------- src/gui/guiButton.h | 34 ++++--- src/gui/guiButtonImage.cpp | 102 ++++--------------- src/gui/guiButtonImage.h | 26 ++--- src/gui/guiButtonItemImage.cpp | 15 +-- src/gui/guiButtonItemImage.h | 9 +- src/gui/guiConfirmRegistration.cpp | 8 +- src/gui/guiConfirmRegistration.h | 4 +- src/gui/guiFormSpecMenu.cpp | 187 ++++++++++++++++++++++++----------- src/gui/guiFormSpecMenu.h | 8 +- src/gui/guiKeyChangeMenu.cpp | 12 ++- src/gui/guiKeyChangeMenu.h | 5 +- src/gui/guiPasswordChange.cpp | 10 +- src/gui/guiPasswordChange.h | 5 +- src/gui/guiVolumeChange.cpp | 7 +- src/gui/guiVolumeChange.h | 7 +- src/script/lua_api/l_mainmenu.cpp | 19 ++-- src/util/numeric.h | 10 ++ 22 files changed, 454 insertions(+), 317 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6ba87d619..b083b2907 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2188,12 +2188,12 @@ Elements * 9-sliced background. See https://en.wikipedia.org/wiki/9-slice_scaling * Middle is a rect which defines the middle of the 9-slice. - * `x` - The middle will be x pixels from all sides. - * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. - * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values - will be added to the width and height of the texture, allowing it to be used as the - distance from the far end. - * All numbers in middle are integers. + * `x` - The middle will be x pixels from all sides. + * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. + * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values + will be added to the width and height of the texture, allowing it to be used as the + distance from the far end. + * All numbers in middle are integers. * Example for formspec 8x4 in 16x resolution: image shall be sized 8 times 16px times 4 times 16px * If `auto_clip` is `true`, the background is clipped to the formspec size @@ -2508,16 +2508,28 @@ Elements * `span=`: number of following columns to affect (default: infinite). -### `style[,,...;;;...]` +### `style[,;;;...]` -* Set the style for the named element(s) `name`. +* Set the style for the element(s) matching `selector` by name. +* `selector` can be one of: + * `` - An element name. + * `:` - An element name, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * Note: this **must** be before the element is defined. * See [Styling Formspecs]. -### `style_type[,,...;;;...]` +### `style_type[,;;;...]` -* Sets the style for all elements of type(s) `type` which appear after this element. +* Set the style for the element(s) matching `selector` by type. +* `selector` can be one of: + * `` - An element type. + * `:` - An element type, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * See [Styling Formspecs]. Migrating to Real Coordinates @@ -2560,23 +2572,32 @@ Styling Formspecs Formspec elements can be themed using the style elements: - style[,,...;;;...] - style_type[,,...;;;...] + style[,;;;...] + style[:,:;;;...] + style_type[,;;;...] + style_type[:,:;;;...] Where a prop is: property_name=property_value +For example: + + style_type[button;bgcolor=#006699] + style[world_delete;bgcolor=red;textcolor=yellow] + button[4,3.95;2.6,1;world_delete;Delete] + A name/type can optionally be a comma separated list of names/types, like so: world_delete,world_create,world_configure button,image_button -For example: +Any name/type in the list can also be accompanied by a `+`-separated list of states, like so: - style_type[button;bgcolor=#006699] - style[world_delete;bgcolor=red;textcolor=yellow] - button[4,3.95;2.6,1;world_delete;Delete] + world_delete:hovered+pressed + button:pressed + +States allow you to apply styles in response to changes in the element, instead of applying at all times. Setting a property to nothing will reset it to the default value. For example: @@ -2654,6 +2675,14 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +### Valid States + +* *all elements* + * default - Equivalent to providing no states +* button, button_exit, image_button, item_image_button + * hovered - Active when the mouse is hovering over the element + * pressed - Active when the button is pressed + Markup Language --------------- diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index d2123b4af..14886aad2 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -65,7 +65,10 @@ local style_fs = [[ style[one_btn13;border=false] item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor] - style[one_btn14;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png;fgimg=bubble.png;fgimg_hovered=default_apple.png;fgimg_pressed=heart.png] + style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png] + style[one_btn14:hovered;bgimg=test_bg_hovered.png;fgimg=default_apple.png;textcolor=red] + style[one_btn14:pressed;bgimg=test_bg_pressed.png;fgimg=heart.png;textcolor=green] + style[one_btn14:hovered+pressed;textcolor=blue] image_button[0,9.6;1,1;bubble.png;one_btn14;Bg] style[one_btn15;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png] diff --git a/src/client/game.cpp b/src/client/game.cpp index 505108caf..06e76d170 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1556,7 +1556,8 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, connection_aborted))->drop(); + &g_menumgr, client, playername, password, + connection_aborted, texture_src))->drop(); } } else { wait_time += dtime; @@ -1711,19 +1712,19 @@ inline bool Game::handleCallbacks() if (g_gamecallback->changepassword_requested) { (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client))->drop(); + &g_menumgr, client, texture_src))->drop(); g_gamecallback->changepassword_requested = false; } if (g_gamecallback->changevolume_requested) { (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->changevolume_requested = false; } if (g_gamecallback->keyconfig_requested) { (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->keyconfig_requested = false; } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 999c1d237..799fbf46d 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "client/tile.h" // ITextureSource +#include "debug.h" #include "irrlichttypes_extrabloated.h" #include "util/string.h" #include @@ -31,25 +32,34 @@ public: { TEXTCOLOR, BGCOLOR, - BGCOLOR_HOVERED, - BGCOLOR_PRESSED, + BGCOLOR_HOVERED, // Note: Deprecated property + BGCOLOR_PRESSED, // Note: Deprecated property NOCLIP, BORDER, BGIMG, - BGIMG_HOVERED, + BGIMG_HOVERED, // Note: Deprecated property BGIMG_MIDDLE, - BGIMG_PRESSED, + BGIMG_PRESSED, // Note: Deprecated property FGIMG, - FGIMG_HOVERED, - FGIMG_PRESSED, + FGIMG_HOVERED, // Note: Deprecated property + FGIMG_PRESSED, // Note: Deprecated property ALPHA, NUM_PROPERTIES, NONE }; + enum State + { + STATE_DEFAULT = 0, + STATE_HOVERED = 1 << 0, + STATE_PRESSED = 1 << 1, + NUM_STATES = 1 << 2, + STATE_INVALID = 1 << 3, + }; private: std::array property_set{}; std::array properties; + State state_map = STATE_DEFAULT; public: static Property GetPropertyByName(const std::string &name) @@ -99,6 +109,49 @@ public: property_set[prop] = true; } + //! Parses a name and returns the corresponding state enum + static State getStateByName(const std::string &name) + { + if (name == "default") { + return STATE_DEFAULT; + } else if (name == "hovered") { + return STATE_HOVERED; + } else if (name == "pressed") { + return STATE_PRESSED; + } else { + return STATE_INVALID; + } + } + + //! Gets the state that this style is intended for + State getState() const + { + return state_map; + } + + //! Set the given state on this style + void addState(State state) + { + FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received"); + + state_map = static_cast(state_map | state); + } + + //! Using a list of styles mapped to state values, calculate the final + // combined style for a state by propagating values in its component states + static StyleSpec getStyleFromStatePropagation(const std::array &styles, State state) + { + StyleSpec temp = styles[StyleSpec::STATE_DEFAULT]; + temp.state_map = state; + for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) { + if ((state & i) != 0) { + temp = temp | styles[i]; + } + } + + return temp; + } + video::SColor getColor(Property prop, video::SColor def) const { const auto &val = properties[prop]; diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 4c16ee237..9dfe36bc4 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -14,6 +14,7 @@ #include "irrlicht_changes/static_text.h" #include "porting.h" #include "StyleSpec.h" +#include "util/numeric.h" using namespace irr; using namespace gui; @@ -26,14 +27,15 @@ using namespace gui; //! constructor GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, - s32 id, core::rect rectangle, bool noclip) + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip) : IGUIButton(environment, parent, id, rectangle), SpriteBank(0), OverrideFont(0), OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)), ClickTime(0), HoverTime(0), FocusTime(0), ClickShiftState(false), ClickControlState(false), IsPushButton(false), Pressed(false), - UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) + UseAlphaChannel(false), DrawBorder(true), ScaleImage(false), TSrc(tsrc) { setNotClipped(noclip); @@ -44,14 +46,6 @@ GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, // PATCH for (size_t i = 0; i < 4; i++) { Colors[i] = Environment->getSkin()->getColor((EGUI_DEFAULT_COLOR)i); - HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id); StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); @@ -262,6 +256,13 @@ void GUIButton::draw() return; // PATCH + // Track hovered state, if it has changed then we need to update the style. + bool hovered = isHovered(); + if (hovered != WasHovered) { + WasHovered = hovered; + setFromState(); + } + GUISkin* skin = dynamic_cast(Environment->getSkin()); video::IVideoDriver* driver = Environment->getVideoDriver(); // END PATCH @@ -271,21 +272,24 @@ void GUIButton::draw() if (!Pressed) { // PATCH - skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect, - isHovered() ? HoveredColors : Colors); + skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, + &AbsoluteClippingRect, Colors); // END PATCH } else { // PATCH - skin->drawColored3DButtonPanePressed(this, - AbsoluteRect, &AbsoluteClippingRect, PressedColors); + skin->drawColored3DButtonPanePressed(this, AbsoluteRect, + &AbsoluteClippingRect, Colors); // END PATCH } } const core::position2di buttonCenter(AbsoluteRect.getCenter()); - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed); + // PATCH + // The image changes based on the state, so we use the default every time. + EGUI_BUTTON_IMAGE_STATE imageState = EGBIS_IMAGE_UP; + // END PATCH if ( ButtonImages[(u32)imageState].Texture ) { core::position2d pos(buttonCenter); @@ -548,18 +552,6 @@ void GUIButton::setPressedImage(video::ITexture* image, const core::rect& p setImage(gui::EGBIS_IMAGE_DOWN, image, pos); } -void GUIButton::setHoveredImage(video::ITexture* image) -{ - setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image); - setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image); -} - -void GUIButton::setHoveredImage(video::ITexture* image, const core::rect& pos) -{ - setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos); - setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos); -} - //! Sets the text displayed by the button void GUIButton::setText(const wchar_t* text) { @@ -618,6 +610,8 @@ void GUIButton::setPressed(bool pressed) skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y))); } } + + setFromState(); } } @@ -729,10 +723,12 @@ void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWri } // PATCH -GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext) +GUIButton* GUIButton::addButton(IGUIEnvironment *environment, + const core::rect& rectangle, ISimpleTextureSource *tsrc, + IGUIElement* parent, s32 id, const wchar_t* text, + const wchar_t *tooltiptext) { - GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle); + GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc); if (text) button->setText(text); @@ -749,76 +745,87 @@ void GUIButton::setColor(video::SColor color) for (size_t i = 0; i < 4; i++) { video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); Colors[i] = base.getInterpolated(color, d); - HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } } -void GUIButton::setHoveredColor(video::SColor color) -{ - float d = 0.65f; - for (size_t i = 0; i < 4; i++) { - video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); - HoveredColors[i] = base.getInterpolated(color, d); - } -} -void GUIButton::setPressedColor(video::SColor color) + +//! Set element properties from a StyleSpec corresponding to the button state +void GUIButton::setFromState() { - float d = 0.65f; - for (size_t i = 0; i < 4; i++) { - video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); - PressedColors[i] = base.getInterpolated(color, d); - } + StyleSpec::State state = StyleSpec::STATE_DEFAULT; + + if (isPressed()) + state = static_cast(state | StyleSpec::STATE_PRESSED); + + if (isHovered()) + state = static_cast(state | StyleSpec::STATE_HOVERED); + + setFromStyle(StyleSpec::getStyleFromStatePropagation(Styles, state)); } //! Set element properties from a StyleSpec -void GUIButton::setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc) +void GUIButton::setFromStyle(const StyleSpec& style) { + bool hovered = (style.getState() & StyleSpec::STATE_HOVERED) != 0; + bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0; + if (style.isNotDefault(StyleSpec::BGCOLOR)) { + setColor(style.getColor(StyleSpec::BGCOLOR)); - } - if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) { - setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED)); - } - if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) { - setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED)); + + // 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); + } else if (hovered) { + Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD); + } + } + } + + } else { + for (size_t i = 0; i < 4; i++) { + video::SColor base = + Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); + if (pressed) { + Colors[i] = multiplyColorValue(base, COLOR_PRESSED_MOD); + } else if (hovered) { + Colors[i] = multiplyColorValue(base, COLOR_HOVERED_MOD); + } else { + Colors[i] = base; + } + } } if (style.isNotDefault(StyleSpec::TEXTCOLOR)) { setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR)); + } else { + setOverrideColor(video::SColor(255,255,255,255)); + OverrideColorEnabled = false; } - setNotClipped(style.getBool(StyleSpec::NOCLIP, isNotClipped())); - setDrawBorder(style.getBool(StyleSpec::BORDER, DrawBorder)); + setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + setDrawBorder(style.getBool(StyleSpec::BORDER, true)); setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); const core::position2di buttonCenter(AbsoluteRect.getCenter()); core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::BGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, tsrc); - + video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, + getTextureSource()); setImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::BGIMG_HOVERED)) { - video::ITexture *hovered_texture = style.getTexture(StyleSpec::BGIMG_HOVERED, tsrc); - - setHoveredImage(guiScalingImageButton( - Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::BGIMG_PRESSED)) { - video::ITexture *pressed_texture = style.getTexture(StyleSpec::BGIMG_PRESSED, tsrc); - - setPressedImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); + Environment->getVideoDriver(), texture, geom.X, geom.Y)); setScaleImage(true); + } else { + setImage(nullptr); } BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); } + +//! Set the styles used for each state +void GUIButton::setStyles(const std::array& styles) +{ + Styles = styles; + setFromState(); +} // END PATCH diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 3d1f98c32..ef10f926e 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -13,6 +13,7 @@ #include "ITexture.h" #include "SColor.h" #include "guiSkin.h" +#include "StyleSpec.h" using namespace irr; @@ -67,7 +68,6 @@ using namespace irr; #endif class ISimpleTextureSource; -class StyleSpec; class GUIButton : public gui::IGUIButton { @@ -75,7 +75,8 @@ public: //! constructor GUIButton(gui::IGUIEnvironment* environment, gui::IGUIElement* parent, - s32 id, core::rect rectangle, bool noclip=false); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip=false); //! destructor virtual ~GUIButton(); @@ -125,16 +126,10 @@ public: //! Sets an image which should be displayed on the button when it is in pressed state. virtual void setPressedImage(video::ITexture* image, const core::rect& pos) override; - //! Sets an image which should be displayed on the button when it is in hovered state. - virtual void setHoveredImage(video::ITexture* image=nullptr); - //! Sets the text displayed by the button virtual void setText(const wchar_t* text) override; // END PATCH - //! Sets an image which should be displayed on the button when it is in hovered state. - virtual void setHoveredImage(video::ITexture* image, const core::rect& pos); - //! Sets the sprite bank used by the button virtual void setSpriteBank(gui::IGUISpriteBank* bank=0) override; @@ -225,22 +220,29 @@ public: void setColor(video::SColor color); // PATCH - void setHoveredColor(video::SColor color); - void setPressedColor(video::SColor color); + //! Set element properties from a StyleSpec corresponding to the button state + void setFromState(); //! Set element properties from a StyleSpec - virtual void setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc); + virtual void setFromStyle(const StyleSpec& style); + + //! Set the styles used for each state + void setStyles(const std::array& styles); // END PATCH //! Do not drop returned handle - static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext=L""); + static GUIButton* addButton(gui::IGUIEnvironment *environment, + const core::rect& rectangle, ISimpleTextureSource *tsrc, + IGUIElement* parent, s32 id, const wchar_t* text, + const wchar_t *tooltiptext=L""); protected: void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center); gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const; + ISimpleTextureSource *getTextureSource() { return TSrc; } + struct ButtonImage { ButtonImage() : Texture(0), SourceRect(core::rect(0,0,0,0)) @@ -308,6 +310,8 @@ private: ButtonImage ButtonImages[gui::EGBIS_COUNT]; + std::array Styles; + gui::IGUIFont* OverrideFont; bool OverrideColorEnabled; @@ -326,8 +330,8 @@ private: video::SColor Colors[4]; // PATCH - video::SColor HoveredColors[4]; - video::SColor PressedColors[4]; + bool WasHovered = false; + ISimpleTextureSource *TSrc; gui::IGUIStaticText *StaticText; diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index 02d920277..2658ad967 100644 --- a/src/gui/guiButtonImage.cpp +++ b/src/gui/guiButtonImage.cpp @@ -30,8 +30,9 @@ using namespace irr; using namespace gui; GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, - gui::IGUIElement *parent, s32 id, core::rect rectangle, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) + gui::IGUIElement *parent, s32 id, core::rect rectangle, + ISimpleTextureSource *tsrc, bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = Environment->addImage( core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), this); @@ -39,100 +40,38 @@ GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, sendToBack(m_image); } -bool GUIButtonImage::OnEvent(const SEvent& event) -{ - bool result = GUIButton::OnEvent(event); - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - video::ITexture *texture = m_foreground_images[(u32)imageState].Texture; - if (texture != nullptr) - { - m_image->setImage(texture); - } - - m_image->setVisible(texture != nullptr); - - return result; -} - -void GUIButtonImage::setForegroundImage(EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image, const core::rect &sourceRect) +void GUIButtonImage::setForegroundImage(video::ITexture *image) { - if (state >= EGBIS_COUNT) + if (image == m_foreground_image) return; - if (image) + if (image != nullptr) image->grab(); - u32 stateIdx = (u32)state; - if (m_foreground_images[stateIdx].Texture) - m_foreground_images[stateIdx].Texture->drop(); - - m_foreground_images[stateIdx].Texture = image; - m_foreground_images[stateIdx].SourceRect = sourceRect; - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - if (imageState == stateIdx) - m_image->setImage(image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image, pos); -} - -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image); -} + if (m_foreground_image != nullptr) + m_foreground_image->drop(); -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image, pos); + m_foreground_image = image; + m_image->setImage(image); } -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image) +//! Set element properties from a StyleSpec +void GUIButtonImage::setFromStyle(const StyleSpec& style) { - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image); -} - -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos); -} - -void GUIButtonImage::setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) -{ - GUIButton::setFromStyle(style, tsrc); + GUIButton::setFromStyle(style); video::IVideoDriver *driver = Environment->getVideoDriver(); const core::position2di buttonCenter(AbsoluteRect.getCenter()); core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::FGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, tsrc); + video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, + getTextureSource()); setForegroundImage(guiScalingImageButton(driver, texture, geom.X, geom.Y)); setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_HOVERED)) { - video::ITexture *hovered_texture = style.getTexture(StyleSpec::FGIMG_HOVERED, tsrc); - - setHoveredForegroundImage(guiScalingImageButton(driver, hovered_texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_PRESSED)) { - video::ITexture *pressed_texture = style.getTexture(StyleSpec::FGIMG_PRESSED, tsrc); - - setPressedForegroundImage(guiScalingImageButton(driver, pressed_texture, geom.X, geom.Y)); - setScaleImage(true); + } else { + setForegroundImage(nullptr); } } @@ -143,11 +82,12 @@ void GUIButtonImage::setScaleImage(bool scaleImage) } GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext) + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext) { GUIButtonImage *button = new GUIButtonImage(environment, - parent ? parent : environment->getRootGUIElement(), id, rectangle); + parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc); if (text) button->setText(text); diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index 15901ee5d..a948d772b 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -27,33 +27,23 @@ class GUIButtonImage : public GUIButton public: //! constructor GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, bool noclip = false); - - virtual bool OnEvent(const SEvent& event) override; - - void setForegroundImage(gui::EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image = nullptr, - const core::rect &sourceRect = core::rect(0, 0, 0, 0)); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip = false); void setForegroundImage(video::ITexture *image = nullptr); - void setForegroundImage(video::ITexture *image, const core::rect &pos); - - void setPressedForegroundImage(video::ITexture *image = nullptr); - void setPressedForegroundImage(video::ITexture *image, const core::rect &pos); - - void setHoveredForegroundImage(video::ITexture *image = nullptr); - void setHoveredForegroundImage(video::ITexture *image, const core::rect &pos); - virtual void setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) override; + //! Set element properties from a StyleSpec + virtual void setFromStyle(const StyleSpec& style) override; virtual void setScaleImage(bool scaleImage=true) override; //! Do not drop returned handle static GUIButtonImage *addButton(gui::IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext = L""); + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext = L""); private: - ButtonImage m_foreground_images[gui::EGBIS_COUNT]; + video::ITexture *m_foreground_image = nullptr; gui::IGUIImage *m_image; }; diff --git a/src/gui/guiButtonItemImage.cpp b/src/gui/guiButtonItemImage.cpp index 5c48b2acd..d8b9042ac 100644 --- a/src/gui/guiButtonItemImage.cpp +++ b/src/gui/guiButtonItemImage.cpp @@ -28,9 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc., using namespace irr; using namespace gui; -GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, std::string item, Client *client, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) +GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, + gui::IGUIElement *parent, s32 id, core::rect rectangle, + ISimpleTextureSource *tsrc, std::string item, Client *client, + bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = new GUIItemImage(environment, this, id, core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), @@ -42,12 +44,13 @@ GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::I } GUIButtonItemImage *GUIButtonItemImage::addButton(IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client) + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client) { GUIButtonItemImage *button = new GUIButtonItemImage(environment, parent ? parent : environment->getRootGUIElement(), - id, rectangle, item, client); + id, rectangle, tsrc, item, client); if (text) button->setText(text); diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 0a61874da..9cd0f6188 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -30,13 +30,14 @@ class GUIButtonItemImage : public GUIButton public: //! constructor GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, std::string item, - Client *client, bool noclip = false); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + std::string item, Client *client, bool noclip = false); //! Do not drop returned handle static GUIButtonItemImage *addButton(gui::IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client); + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client); private: std::string m_item_name; diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 0d8bdf54e..58ac42740 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -40,10 +40,10 @@ const int ID_message = 266; GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted) : + bool *aborted, ISimpleTextureSource *tsrc) : GUIModalMenu(env, parent, id, menumgr), m_client(client), m_playername(playername), m_password(password), - m_aborted(aborted) + m_aborted(aborted), m_tsrc(tsrc) { #ifdef __ANDROID__ m_touchscreen_visible = false; @@ -130,14 +130,14 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) core::rect rect2(0, 0, 230 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 - 220 * s, ypos); text = wgettext("Register and Join"); - GUIButton::addButton(Environment, rect2, this, ID_confirm, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_confirm, text); delete[] text; } { core::rect rect2(0, 0, 120 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 + 70 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect2, this, ID_cancel, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_cancel, text); delete[] text; } { diff --git a/src/gui/guiConfirmRegistration.h b/src/gui/guiConfirmRegistration.h index 42c07e4ed..d8387201d 100644 --- a/src/gui/guiConfirmRegistration.h +++ b/src/gui/guiConfirmRegistration.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class Client; +class ISimpleTextureSource; class GUIConfirmRegistration : public GUIModalMenu { @@ -32,7 +33,7 @@ public: GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted); + bool *aborted, ISimpleTextureSource *tsrc); ~GUIConfirmRegistration(); void removeChildren(); @@ -63,4 +64,5 @@ private: const std::string &m_password; bool *m_aborted = nullptr; std::wstring m_pass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index acb153569..6a383a791 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -553,7 +553,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this, spec.fid, spec.flabel.c_str()); - auto style = getStyleForElement("checkbox", name); + auto style = getDefaultStyleForElement("checkbox", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == data->focused_fieldname) { @@ -613,7 +613,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect, is_horizontal, true); - auto style = getStyleForElement("scrollbar", name); + auto style = getDefaultStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); @@ -740,7 +740,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true); e->setImage(texture); e->setScaleImage(true); - auto style = getStyleForElement("image", spec.fname); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -776,7 +776,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) ); gui::IGUIImage *e = Environment->addImage(texture, pos, true, this, spec.fid, 0); - auto style = getStyleForElement("image", spec.fname); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -841,7 +841,7 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el if (parts.size() >= 7) e->setFrameIndex(stoi(parts[6]) - 1); - auto style = getStyleForElement("animated_image", spec.fname, "image"); + auto style = getDefaultStyleForElement("animated_image", spec.fname, "image"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->drop(); @@ -888,7 +888,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid, core::rect(pos, pos + geom), name, m_font, m_client); - auto style = getStyleForElement("item_image", spec.fname); + auto style = getDefaultStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); // item images should let events through @@ -949,10 +949,11 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, if(type == "button_exit") spec.is_exit = true; - GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, this, + spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); - e->setFromStyle(style, m_tsrc); + e->setStyles(style); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1155,7 +1156,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("table", name); + auto style = getDefaultStyleForElement("table", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1231,7 +1232,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("textlist", name); + auto style = getDefaultStyleForElement("textlist", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1306,7 +1307,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element if (!str_initial_selection.empty()) e->setSelected(stoi(str_initial_selection)-1); - auto style = getStyleForElement("dropdown", name); + auto style = getDefaultStyleForElement("dropdown", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1394,7 +1395,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element e->setPasswordBox(true,L'*'); - auto style = getStyleForElement("pwdfield", name, "field"); + auto style = getDefaultStyleForElement("pwdfield", name, "field"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1454,7 +1455,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } } - auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname); + auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname); if (e) { if (is_editable && spec.fname == data->focused_fieldname) @@ -1752,7 +1753,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); - auto style = getStyleForElement("label", spec.fname); + auto style = getDefaultStyleForElement("label", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1832,7 +1833,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - auto style = getStyleForElement("vertlabel", spec.fname, "label"); + auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1863,17 +1864,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); - bool noclip = false; - bool drawborder = true; std::string pressed_image_name; - if (parts.size() >= 7) { - if (parts[5] == "true") - noclip = true; - if (parts[6] == "false") - drawborder = false; - } - if (parts.size() >= 8) { pressed_image_name = parts[7]; } @@ -1911,35 +1903,30 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem if (type == "image_button_exit") spec.is_exit = true; - GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, + this, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } auto style = getStyleForElement("image_button", spec.fname); - e->setFromStyle(style, m_tsrc); - // We explicitly handle these arguments *after* the style properties in - // order to override them if they are provided + // Override style properties with values specified directly in the element if (!image_name.empty()) - { - video::ITexture *texture = m_tsrc->getTexture(image_name); - e->setForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, geom.X, geom.Y)); - } - if (!pressed_image_name.empty()) { - video::ITexture *pressed_texture = m_tsrc->getTexture(pressed_image_name); - e->setPressedForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); - } - e->setScaleImage(true); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); + + if (!pressed_image_name.empty()) + style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, pressed_image_name); if (parts.size() >= 7) { - e->setNotClipped(noclip); - e->setDrawBorder(drawborder); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, parts[5]); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, parts[6]); } + e->setStyles(style); + e->setScaleImage(true); + m_fields.push_back(spec); return; } @@ -2033,7 +2020,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen Environment->setFocus(e); } - auto style = getStyleForElement("tabheader", name); + auto style = getDefaultStyleForElement("tabheader", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2118,10 +2105,12 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & 2 ); - GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, rect, this, spec_btn.fid, spec_btn.flabel.c_str(), item_name, m_client); + GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, + rect, m_tsrc, this, spec_btn.fid, spec_btn.flabel.c_str(), + item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); - e_btn->setFromStyle(style, m_tsrc); + e_btn->setStyles(style); if (spec_btn.fname == data->focused_fieldname) { Environment->setFocus(e_btn); @@ -2177,7 +2166,7 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color); - auto style = getStyleForElement("box", spec.fname); + auto style = getDefaultStyleForElement("box", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); e->drop(); @@ -2469,6 +2458,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b StyleSpec spec; + // Parse properties for (size_t i = 1; i < parts.size(); i++) { size_t equal_pos = parts[i].find('='); if (equal_pos == std::string::npos) { @@ -2500,16 +2490,92 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b for (size_t sel = 0; sel < selectors.size(); sel++) { std::string selector = trim(selectors[sel]); - if (selector.empty()) { - errorstream << "Invalid style element (Empty selector): '" << element - << "'" << std::endl; + // Copy the style properties to a new StyleSpec + // This allows a separate state mask per-selector + StyleSpec selector_spec = spec; + + // Parse state information, if it exists + bool state_valid = true; + size_t state_pos = selector.find(':'); + if (state_pos != std::string::npos) { + std::string state_str = selector.substr(state_pos + 1); + selector = selector.substr(0, state_pos); + + if (state_str.empty()) { + errorstream << "Invalid style element (Invalid state): '" << element + << "'" << std::endl; + state_valid = false; + } else { + std::vector states = split(state_str, '+'); + for (std::string &state : states) { + StyleSpec::State converted = StyleSpec::getStateByName(state); + if (converted == StyleSpec::STATE_INVALID) { + infostream << "Unknown style state " << state << + " in element '" << element << "'" << std::endl; + state_valid = false; + break; + } + + selector_spec.addState(converted); + } + } + } + + if(!state_valid) { + // Skip this selector continue; } if (style_type) { - theme_by_type[selector] |= spec; + theme_by_type[selector].push_back(selector_spec); } else { - theme_by_name[selector] |= spec; + theme_by_name[selector].push_back(selector_spec); + } + + // Backwards-compatibility for existing _hovered/_pressed properties + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED) + || selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED) + || selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + StyleSpec hover_spec; + hover_spec.addState(StyleSpec::STATE_HOVERED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED)) { + hover_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED)) { + hover_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + hover_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_HOVERED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(hover_spec); + } else { + theme_by_name[selector].push_back(hover_spec); + } + } + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED) + || selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED) + || selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + StyleSpec press_spec; + press_spec.addState(StyleSpec::STATE_PRESSED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED)) { + press_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED)) { + press_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + press_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_PRESSED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(press_spec); + } else { + theme_by_name[selector].push_back(press_spec); + } } } @@ -3080,7 +3146,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2 ); const wchar_t *text = wgettext("Proceed"); - GUIButton::addButton(Environment, mydata.rect, this, 257, text); + GUIButton::addButton(Environment, mydata.rect, m_tsrc, this, 257, text); delete[] text; } } @@ -4432,25 +4498,34 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) return L""; } -StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type, +StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type, const std::string &name, const std::string &parent_type) { - StyleSpec ret; + return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT]; +} + +std::array GUIFormSpecMenu::getStyleForElement(const std::string &type, + const std::string &name, const std::string &parent_type) +{ + std::array ret; if (!parent_type.empty()) { auto it = theme_by_type.find(parent_type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } } auto it = theme_by_type.find(type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } it = theme_by_name.find(name); if (it != theme_by_name.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } return ret; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 17bfef205..b3bf67110 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -274,11 +274,13 @@ protected: v2s32 getRealCoordinateBasePos(const std::vector &v_pos); v2s32 getRealCoordinateGeometry(const std::vector &v_geom); - std::unordered_map theme_by_type; - std::unordered_map theme_by_name; + std::unordered_map> theme_by_type; + std::unordered_map> theme_by_name; std::unordered_set property_warned; - StyleSpec getStyleForElement(const std::string &type, + StyleSpec getDefaultStyleForElement(const std::string &type, + const std::string &name="", const std::string &parent_type=""); + std::array getStyleForElement(const std::string &type, const std::string &name="", const std::string &parent_type=""); v2s32 padding; diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 3f270fc7a..da0e25c23 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -82,8 +82,10 @@ enum }; GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : -GUIModalMenu(env, parent, id, menumgr) + gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, + ISimpleTextureSource *tsrc) : + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { init_keys(); } @@ -157,7 +159,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s); const wchar_t *text = wgettext(k->key.name()); - k->button = GUIButton::addButton(Environment, rect, this, k->id, text); + k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text); delete[] text; } if ((i + 1) % KMaxButtonPerColumns == 0) { @@ -217,14 +219,14 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Save"); - GUIButton::addButton(Environment, rect, this, GUI_ID_BACK_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text); delete[] text; } { core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, GUI_ID_ABORT_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text); delete[] text; } } diff --git a/src/gui/guiKeyChangeMenu.h b/src/gui/guiKeyChangeMenu.h index 528827fd9..1c0f40247 100644 --- a/src/gui/guiKeyChangeMenu.h +++ b/src/gui/guiKeyChangeMenu.h @@ -28,6 +28,8 @@ #include #include +class ISimpleTextureSource; + struct key_setting { int id; @@ -41,7 +43,7 @@ class GUIKeyChangeMenu : public GUIModalMenu { public: GUIKeyChangeMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIKeyChangeMenu(); void removeChildren(); @@ -74,4 +76,5 @@ private: key_setting *active_key = nullptr; gui::IGUIStaticText *key_used_text = nullptr; std::vector key_settings; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index af91ce84c..965a2d6f7 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -38,10 +38,12 @@ const int ID_cancel = 261; GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - Client* client + Client* client, + ISimpleTextureSource *tsrc ): GUIModalMenu(env, parent, id, menumgr), - m_client(client) + m_client(client), + m_tsrc(tsrc) { } @@ -146,14 +148,14 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 56 * s, ypos); text = wgettext("Change"); - GUIButton::addButton(Environment, rect, this, ID_change, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_change, text); delete[] text; } { core::rect rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 185 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, ID_cancel, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_cancel, text); delete[] text; } diff --git a/src/gui/guiPasswordChange.h b/src/gui/guiPasswordChange.h index 58541a399..7141100c0 100644 --- a/src/gui/guiPasswordChange.h +++ b/src/gui/guiPasswordChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include class Client; +class ISimpleTextureSource; class GUIPasswordChange : public GUIModalMenu { public: GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr, Client *client); + IMenuManager *menumgr, Client *client, + ISimpleTextureSource *tsrc); ~GUIPasswordChange(); void removeChildren(); @@ -57,4 +59,5 @@ private: std::wstring m_oldpass = L""; std::wstring m_newpass = L""; std::wstring m_newpass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp index 9428cde83..07b11248c 100644 --- a/src/gui/guiVolumeChange.cpp +++ b/src/gui/guiVolumeChange.cpp @@ -38,9 +38,10 @@ const int ID_soundMuteButton = 266; GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr + IMenuManager *menumgr, ISimpleTextureSource *tsrc ): - GUIModalMenu(env, parent, id, menumgr) + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { } @@ -104,7 +105,7 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 80 * s, 30 * s); rect = rect + v2s32(size.X / 2 - 80 * s / 2, size.Y / 2 + 55 * s); const wchar_t *text = wgettext("Exit"); - GUIButton::addButton(Environment, rect, this, ID_soundExitButton, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_soundExitButton, text); delete[] text; } { diff --git a/src/gui/guiVolumeChange.h b/src/gui/guiVolumeChange.h index f4f4e9eea..466e17f9d 100644 --- a/src/gui/guiVolumeChange.h +++ b/src/gui/guiVolumeChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "modalMenu.h" #include +class ISimpleTextureSource; + class GUIVolumeChange : public GUIModalMenu { public: GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIVolumeChange(); void removeChildren(); @@ -46,4 +48,7 @@ public: protected: std::wstring getLabelByID(s32 id) { return L""; } std::string getNameByID(s32 id) { return ""; } + +private: + ISimpleTextureSource *m_tsrc; }; diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 76db7ed13..867e84e13 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -571,9 +571,10 @@ int ModApiMainMenu::l_show_keys_menu(lua_State *L) sanity_check(engine != NULL); GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager); + engine->m_parent, + -1, + engine->m_menumanager, + engine->m_texture_source); kmenu->drop(); return 0; } @@ -904,12 +905,12 @@ int ModApiMainMenu::l_show_path_select_dialog(lua_State *L) GUIFileSelectMenu* fileOpenMenu = new GUIFileSelectMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager, - title, - formname, - is_file_select); + engine->m_parent, + -1, + engine->m_menumanager, + title, + formname, + is_file_select); fileOpenMenu->setTextDest(engine->m_buttonhandler); fileOpenMenu->drop(); return 0; diff --git a/src/util/numeric.h b/src/util/numeric.h index 6f82a18c1..864ab7543 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v2d.h" #include "irr_v3d.h" #include "irr_aabb3d.h" +#include "SColor.h" #include #define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d))) @@ -432,3 +433,12 @@ inline v3f getPitchYawRoll(const core::matrix4 &m) { return getPitchYawRollRad(m) * core::RADTODEG64; } + +// Muliply the RGB value of a color linearly, and clamp to black/white +inline irr::video::SColor multiplyColorValue(const irr::video::SColor &color, float mod) +{ + return irr::video::SColor(color.getAlpha(), + core::clamp(color.getRed() * mod, 0, 255), + core::clamp(color.getGreen() * mod, 0, 255), + core::clamp(color.getBlue() * mod, 0, 255)); +} -- cgit v1.2.3 From af2e6a6a10121cf971d4ce4c33d523b6dc037d31 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 11 Apr 2020 23:09:46 +0200 Subject: Improve waypoints and add image variant (#9480) --- doc/client_lua_api.txt | 25 ++++++++- doc/lua_api.txt | 20 ++++++++ src/client/hud.cpp | 111 ++++++++++++++++++++++++---------------- src/client/hud.h | 1 + src/hud.cpp | 1 + src/hud.h | 1 + src/script/common/c_content.cpp | 6 ++- 7 files changed, 118 insertions(+), 47 deletions(-) (limited to 'src/client') diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 71df91c68..53ed680d0 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -1416,12 +1416,35 @@ Displays a horizontal bar made up of half-images. * `offset`: offset in pixels from position. ### `waypoint` + Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. -* `number:` An integer containing the RGB value of the color used to draw the text. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` +* `number:` An integer containing the RGB value of the color used to draw the + text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. + +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. + +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. ### Particle definition (`add_particle`) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index b083b2907..1a9aad344 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1364,10 +1364,30 @@ Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` * `number:` An integer containing the RGB value of the color used to draw the text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. + +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 37de6640b..56763e7e4 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -272,6 +272,25 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } } +// 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) +{ + v3f w_pos = e->world_pos * BS; + scene::ICameraSceneNode* camera = + RenderingEngine::get_scene_manager()->getActiveCamera(); + w_pos -= intToFloat(camera_offset, BS); + core::matrix4 trans = camera->getProjectionMatrix(); + trans *= camera->getViewMatrix(); + f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f }; + trans.multiplyWith1x4Matrix(transformed_pos); + if (transformed_pos[3] < 0) + return false; + f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : + core::reciprocal(transformed_pos[3]); + pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5); + pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5); + return true; +} void Hud::drawLuaElements(const v3s16 &camera_offset) { @@ -299,28 +318,6 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5), floor(e->pos.Y * (float) m_screensize.Y + 0.5)); switch (e->type) { - case HUD_ELEM_IMAGE: { - video::ITexture *texture = tsrc->getTexture(e->text); - if (!texture) - continue; - - const video::SColor color(255, 255, 255, 255); - const video::SColor colors[] = {color, color, color, color}; - core::dimension2di imgsize(texture->getOriginalSize()); - v2s32 dstsize(imgsize.Width * e->scale.X, - imgsize.Height * e->scale.Y); - if (e->scale.X < 0) - dstsize.X = m_screensize.X * (e->scale.X * -0.01); - if (e->scale.Y < 0) - dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01); - v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, - (e->align.Y - 1.0) * dstsize.Y / 2); - core::rect rect(0, 0, dstsize.X, dstsize.Y); - rect += pos + offset + v2s32(e->offset.X, e->offset.Y); - draw2DImageFilterScaled(driver, texture, rect, - core::rect(core::position2d(0,0), imgsize), - NULL, colors, true); - break; } case HUD_ELEM_TEXT: { video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, @@ -343,34 +340,58 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) inv, e->item, e->dir); break; } case HUD_ELEM_WAYPOINT: { - v3f p_pos = player->getPosition() / BS; - v3f w_pos = e->world_pos * BS; - float distance = std::floor(10 * p_pos.getDistanceFrom(e->world_pos)) / - 10.0f; - scene::ICameraSceneNode* camera = - RenderingEngine::get_scene_manager()->getActiveCamera(); - w_pos -= intToFloat(camera_offset, BS); - core::matrix4 trans = camera->getProjectionMatrix(); - trans *= camera->getViewMatrix(); - f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f }; - trans.multiplyWith1x4Matrix(transformed_pos); - if (transformed_pos[3] < 0) + if (!calculateScreenPos(camera_offset, e, &pos)) break; - f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : - core::reciprocal(transformed_pos[3]); - pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5); - pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5); + v3f p_pos = player->getPosition() / BS; + pos += v2s32(e->offset.X, e->offset.Y); video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, (e->number >> 0) & 0xFF); - core::rect size(0, 0, 200, 2 * text_height); std::wstring text = unescape_translate(utf8_to_wide(e->name)); - font->draw(text.c_str(), size + pos, color); - std::ostringstream os; - os << distance << e->text; - text = unescape_translate(utf8_to_wide(os.str())); - pos.Y += text_height; - font->draw(text.c_str(), size + pos, color); + const std::string &unit = e->text; + // waypoints reuse the item field to store precision, item = precision + 1 + u32 item = e->item; + float precision = (item == 0) ? 10.0f : (item - 1.f); + bool draw_precision = precision > 0; + + core::rect bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height); + pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2; + bounds += pos; + font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color); + if (draw_precision) { + std::ostringstream os; + float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision; + os << distance << unit; + text = unescape_translate(utf8_to_wide(os.str())); + bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width; + font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color); + } + break; } + case HUD_ELEM_IMAGE_WAYPOINT: { + if (!calculateScreenPos(camera_offset, e, &pos)) + break; + } + case HUD_ELEM_IMAGE: { + video::ITexture *texture = tsrc->getTexture(e->text); + if (!texture) + continue; + + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + core::dimension2di imgsize(texture->getOriginalSize()); + v2s32 dstsize(imgsize.Width * e->scale.X, + imgsize.Height * e->scale.Y); + if (e->scale.X < 0) + dstsize.X = m_screensize.X * (e->scale.X * -0.01); + if (e->scale.Y < 0) + dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01); + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2); + core::rect rect(0, 0, dstsize.X, dstsize.Y); + rect += pos + offset + v2s32(e->offset.X, e->offset.Y); + draw2DImageFilterScaled(driver, texture, rect, + core::rect(core::position2d(0,0), imgsize), + NULL, colors, true); break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << diff --git a/src/client/hud.h b/src/client/hud.h index d9b5e0686..cab115990 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -81,6 +81,7 @@ public: void drawLuaElements(const v3s16 &camera_offset); private: + bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, s32 count, v2s32 offset, v2s32 size = v2s32()); diff --git a/src/hud.cpp b/src/hud.cpp index 7711e3a4a..39625b5fd 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -27,6 +27,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_STATBAR, "statbar"}, {HUD_ELEM_INVENTORY, "inventory"}, {HUD_ELEM_WAYPOINT, "waypoint"}, + {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index 23f189dff..b0977c6a4 100644 --- a/src/hud.h +++ b/src/hud.h @@ -61,6 +61,7 @@ enum HudElementType { HUD_ELEM_STATBAR = 2, HUD_ELEM_INVENTORY = 3, HUD_ELEM_WAYPOINT = 4, + HUD_ELEM_IMAGE_WAYPOINT = 5 }; enum HudElementStat { diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index c8cd7539f..ff9ceec6d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1850,7 +1850,11 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->name = getstringfield_default(L, 2, "name", ""); elem->text = getstringfield_default(L, 2, "text", ""); elem->number = getintfield_default(L, 2, "number", 0); - elem->item = getintfield_default(L, 2, "item", 0); + if (elem->type == HUD_ELEM_WAYPOINT) + // waypoints reuse the item field to store precision, item = precision + 1 + elem->item = getintfield_default(L, 2, "precision", -1) + 1; + else + elem->item = getintfield_default(L, 2, "item", 0); elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); -- cgit v1.2.3 From a24d3b360048bde87113d435ab0cfef78851153c Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Sun, 12 Apr 2020 01:50:40 +0200 Subject: Play 'player_jump' when player jumps (#9373) --- doc/lua_api.txt | 1 + src/client/game.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 1a9aad344..dff6c728e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -897,6 +897,7 @@ These sound files are played back by the engine if provided. * `player_damage`: Played when the local player takes damage (gain = 0.5) * `player_falling_damage`: Played when the local player takes damage by falling (gain = 0.5) + * `player_jump`: Played when the local player jumps * `default_dig_`: Default node digging sound (see node sound definition for details) diff --git a/src/client/game.cpp b/src/client/game.cpp index 06e76d170..ac6045190 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -266,6 +266,7 @@ class SoundMaker public: bool makes_footstep_sound; float m_player_step_timer; + float m_player_jump_timer; SimpleSoundSpec m_player_step_sound; SimpleSoundSpec m_player_leftpunch_sound; @@ -275,7 +276,8 @@ 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) { } @@ -288,6 +290,14 @@ public: } } + void playPlayerJump() + { + if (m_player_jump_timer <= 0.0f) { + m_player_jump_timer = 0.2f; + m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false); + } + } + static void viewBobbingStep(MtEvent *e, void *data) { SoundMaker *sm = (SoundMaker *)data; @@ -302,7 +312,8 @@ public: static void playerJump(MtEvent *e, void *data) { - //SoundMaker *sm = (SoundMaker*)data; + SoundMaker *sm = (SoundMaker *)data; + sm->playPlayerJump(); } static void cameraPunchLeft(MtEvent *e, void *data) @@ -351,6 +362,7 @@ public: void step(float dtime) { m_player_step_timer -= dtime; + m_player_jump_timer -= dtime; } }; -- cgit v1.2.3 From 7e21b3cd4883eb8b9eb7e9ca49e50f6f0c7bc0d6 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 13 Apr 2020 20:26:54 +0200 Subject: Remove sound menu and show proper msgs if sound is off (#9069) --- src/client/game.cpp | 66 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 22 deletions(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index ac6045190..f7234eea6 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1922,29 +1922,47 @@ void Game::processKeyInput() toggleFast(); } else if (wasKeyDown(KeyType::NOCLIP)) { toggleNoClip(); +#if USE_SOUND } else if (wasKeyDown(KeyType::MUTE)) { - bool new_mute_sound = !g_settings->getBool("mute_sound"); - g_settings->setBool("mute_sound", new_mute_sound); - if (new_mute_sound) - m_game_ui->showTranslatedStatusText("Sound muted"); - else - m_game_ui->showTranslatedStatusText("Sound unmuted"); + if (g_settings->getBool("enable_sound")) { + bool new_mute_sound = !g_settings->getBool("mute_sound"); + g_settings->setBool("mute_sound", new_mute_sound); + if (new_mute_sound) + m_game_ui->showTranslatedStatusText("Sound muted"); + else + m_game_ui->showTranslatedStatusText("Sound unmuted"); + } else { + m_game_ui->showTranslatedStatusText("Sound system is disabled"); + } } else if (wasKeyDown(KeyType::INC_VOLUME)) { - float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f); - wchar_t buf[100]; - g_settings->setFloat("sound_volume", new_volume); - const wchar_t *str = wgettext("Volume changed to %d%%"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); - delete[] str; - m_game_ui->showStatusText(buf); + if (g_settings->getBool("enable_sound")) { + float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f); + wchar_t buf[100]; + g_settings->setFloat("sound_volume", new_volume); + const wchar_t *str = wgettext("Volume changed to %d%%"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); + delete[] str; + m_game_ui->showStatusText(buf); + } else { + m_game_ui->showTranslatedStatusText("Sound system is disabled"); + } } else if (wasKeyDown(KeyType::DEC_VOLUME)) { - float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f); - wchar_t buf[100]; - g_settings->setFloat("sound_volume", new_volume); - const wchar_t *str = wgettext("Volume changed to %d%%"); - swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); - delete[] str; - m_game_ui->showStatusText(buf); + if (g_settings->getBool("enable_sound")) { + float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f); + wchar_t buf[100]; + g_settings->setFloat("sound_volume", new_volume); + const wchar_t *str = wgettext("Volume changed to %d%%"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100)); + delete[] str; + m_game_ui->showStatusText(buf); + } else { + m_game_ui->showTranslatedStatusText("Sound system is disabled"); + } +#else + } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME) + || wasKeyDown(KeyType::DEC_VOLUME)) { + m_game_ui->showTranslatedStatusText("Sound system is not supported on this build"); +#endif } else if (wasKeyDown(KeyType::CINEMATIC)) { toggleCinematic(); } else if (wasKeyDown(KeyType::SCREENSHOT)) { @@ -4162,8 +4180,12 @@ void Game::showPauseMenu() } #ifndef __ANDROID__ - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << strgettext("Sound Volume") << "]"; +#if USE_SOUND + if (g_settings->getBool("enable_sound")) { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" + << strgettext("Sound Volume") << "]"; + } +#endif os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" << strgettext("Change Keys") << "]"; #endif -- cgit v1.2.3 From 5cf6318117edcae6bf30d829d9e9dd9dbf1d4bf7 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Tue, 14 Apr 2020 14:41:29 -0400 Subject: Refactor texture overrides and add new features (#9600) * Refactor texture overrides, and add new features: - Texture overrides can support multiple targets in one line - Texture override files can have comment lines - Item images/wield images can be overridden * Formatting changes * Address soime feedback - Pass vectors by const reference - Log syntax errors as warnings - Remove 'C' prefix from TextureOverrideSource * Simplify override target checks with an inline helper function * make linter happy * Apply feedback suggestions Co-Authored-By: rubenwardy * Remove remaining != 0 checks * Update copyright notice Co-authored-by: sfan5 Co-authored-by: rubenwardy --- doc/texture_packs.txt | 45 +++++++++---- src/CMakeLists.txt | 1 + src/client/client.cpp | 7 +- src/itemdef.cpp | 19 ++++++ src/itemdef.h | 5 ++ src/nodedef.cpp | 67 ++++++------------ src/nodedef.h | 12 ++-- src/server.cpp | 7 +- src/texture_override.cpp | 120 +++++++++++++++++++++++++++++++++ src/texture_override.h | 72 ++++++++++++++++++++ util/travis/clang-format-whitelist.txt | 1 + 11 files changed, 285 insertions(+), 71 deletions(-) create mode 100644 src/texture_override.cpp create mode 100644 src/texture_override.h (limited to 'src/client') diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 7ab0aca94..4e7bc93c4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -145,34 +145,51 @@ are placeholders intended to be overwritten by the game. Texture Overrides ----------------- -You can override the textures of a node from a texture pack using -texture overrides. To do this, create a file in a texture pack -called override.txt +You can override the textures of nodes and items from a +texture pack using texture overrides. To do this, create one or +more files in a texture pack called override.txt Each line in an override.txt file is a rule. It consists of - nodename face-selector texture + itemname target texture For example, default:dirt_with_grass sides default_stone.png -You can use ^ operators as usual: +or + + default:sword_steel inventory my_steel_sword.png + +You can list multiple targets on one line as a comma-separated list: + + default:tree top,bottom my_special_tree.png + +You can use texture modifiers, as usual: default:dirt_with_grass sides default_stone.png^[brighten -Here are face selectors you can choose from: +Finally, if a line is empty or starts with '#' it will be considered +a comment and not read as a rule. You can use this to better organize +your override.txt files. + +Here are targets you can choose from: -| face-selector | behavior | +| target | behavior | |---------------|---------------------------------------------------| -| left | x- | -| right | x+ | -| front | z- | -| back | z+ | -| top | y+ | -| bottom | y- | -| sides | x-, x+, z-, z+ | +| left | x- face | +| right | x+ face | +| front | z- face | +| back | z+ face | +| top | y+ face | +| bottom | y- face | +| sides | x-, x+, z-, z+ faces | | all | All faces. You can also use '*' instead of 'all'. | +| inventory | The inventory texture | +| wield | The texture used when held by the player | + +Nodes support all targets, but other items only support 'inventory' +and 'wield' Designing leaves textures for the leaves rendering options ---------------------------------------------------------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b550c09c..fa261547b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -423,6 +423,7 @@ set(common_SRCS settings.cpp staticobject.cpp terminal_chat_console.cpp + texture_override.cpp tileanimation.cpp tool.cpp translation.cpp diff --git a/src/client/client.cpp b/src/client/client.cpp index c3e2a4d2a..8ee0869cd 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1742,8 +1742,11 @@ void Client::afterContentReceived() text = wgettext("Initializing nodes..."); RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 72); m_nodedef->updateAliases(m_itemdef); - for (const auto &path : getTextureDirs()) - m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt"); + for (const auto &path : getTextureDirs()) { + TextureOverrideSource override_source(path + DIR_DELIM + "override.txt"); + m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides()); + m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides()); + } m_nodedef->setNodeRegistrationStatus(true); m_nodedef->runNodeResolveCallbacks(); delete[] text; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index ba7bd6a0b..a13b3f7d4 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -422,6 +422,25 @@ public: return get(stack.name).color; } #endif + void applyTextureOverrides(const std::vector &overrides) + { + infostream << "ItemDefManager::applyTextureOverrides(): Applying " + "overrides to textures" << std::endl; + + for (const TextureOverride& texture_override : overrides) { + if (m_item_definitions.find(texture_override.id) == m_item_definitions.end()) { + continue; // Ignore unknown item + } + + ItemDefinition* itemdef = m_item_definitions[texture_override.id]; + + if (texture_override.hasTarget(OverrideTarget::INVENTORY)) + itemdef->inventory_image = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::WIELD)) + itemdef->wield_image = texture_override.texture; + } + } void clear() { for(std::map::const_iterator diff --git a/src/itemdef.h b/src/itemdef.h index 45cff582a..f47e6cbe7 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "itemgroup.h" #include "sound.h" +#include "texture_override.h" // TextureOverride class IGameDef; class Client; struct ToolCapabilities; @@ -157,6 +158,10 @@ public: Client *client) const=0; #endif + // Replace the textures of registered nodes with the ones specified in + // the texture pack's override.txt files + virtual void applyTextureOverrides(const std::vector &overrides)=0; + // Remove all registered item and node definitions and aliases // Then re-add the builtin item definitions virtual void clear()=0; diff --git a/src/nodedef.cpp b/src/nodedef.cpp index b6eca9497..37332c3c6 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1304,60 +1304,35 @@ void NodeDefManager::updateAliases(IItemDefManager *idef) } } -void NodeDefManager::applyTextureOverrides(const std::string &override_filepath) +void NodeDefManager::applyTextureOverrides(const std::vector &overrides) { infostream << "NodeDefManager::applyTextureOverrides(): Applying " - "overrides to textures from " << override_filepath << std::endl; - - std::ifstream infile(override_filepath.c_str()); - std::string line; - int line_c = 0; - while (std::getline(infile, line)) { - line_c++; - // Also trim '\r' on DOS-style files - line = trim(line); - if (line.empty()) - continue; - - std::vector splitted = str_split(line, ' '); - if (splitted.size() != 3) { - errorstream << override_filepath - << ":" << line_c << " Could not apply texture override \"" - << line << "\": Syntax error" << std::endl; - continue; - } + "overrides to textures" << std::endl; + for (const TextureOverride& texture_override : overrides) { content_t id; - if (!getId(splitted[0], id)) + if (!getId(texture_override.id, id)) continue; // Ignore unknown node ContentFeatures &nodedef = m_content_features[id]; - if (splitted[1] == "top") - nodedef.tiledef[0].name = splitted[2]; - else if (splitted[1] == "bottom") - nodedef.tiledef[1].name = splitted[2]; - else if (splitted[1] == "right") - nodedef.tiledef[2].name = splitted[2]; - else if (splitted[1] == "left") - nodedef.tiledef[3].name = splitted[2]; - else if (splitted[1] == "back") - nodedef.tiledef[4].name = splitted[2]; - else if (splitted[1] == "front") - nodedef.tiledef[5].name = splitted[2]; - else if (splitted[1] == "all" || splitted[1] == "*") - for (TileDef &i : nodedef.tiledef) - i.name = splitted[2]; - else if (splitted[1] == "sides") - for (int i = 2; i < 6; i++) - nodedef.tiledef[i].name = splitted[2]; - else { - errorstream << override_filepath - << ":" << line_c << " Could not apply texture override \"" - << line << "\": Unknown node side \"" - << splitted[1] << "\"" << std::endl; - continue; - } + if (texture_override.hasTarget(OverrideTarget::TOP)) + nodedef.tiledef[0].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::BOTTOM)) + nodedef.tiledef[1].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::RIGHT)) + nodedef.tiledef[2].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::LEFT)) + nodedef.tiledef[3].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::BACK)) + nodedef.tiledef[4].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::FRONT)) + nodedef.tiledef[5].name = texture_override.texture; } } diff --git a/src/nodedef.h b/src/nodedef.h index 1a12aae93..c77d53324 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -33,6 +33,7 @@ class Client; #include "itemgroup.h" #include "sound.h" // SimpleSoundSpec #include "constants.h" // BS +#include "texture_override.h" // TextureOverride #include "tileanimation.h" // PROTOCOL_VERSION >= 37 @@ -583,15 +584,12 @@ public: void updateAliases(IItemDefManager *idef); /*! - * Reads the used texture pack's override.txt, and replaces the textures - * of registered nodes with the ones specified there. + * Replaces the textures of registered nodes with the ones specified in + * the texturepack's override.txt file * - * Format of the input file: in each line - * `node_name top|bottom|right|left|front|back|all|*|sides texture_name.png` - * - * @param override_filepath path to 'texturepack/override.txt' + * @param overrides the texture overrides */ - void applyTextureOverrides(const std::string &override_filepath); + void applyTextureOverrides(const std::vector &overrides); /*! * Only the client uses this. Loads textures and shaders required for diff --git a/src/server.cpp b/src/server.cpp index 85d07fbc4..b3992b9b1 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -373,8 +373,11 @@ void Server::init() std::vector paths; fs::GetRecursiveDirs(paths, g_settings->get("texture_path")); fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); - for (const std::string &path : paths) - m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt"); + for (const std::string &path : paths) { + TextureOverrideSource override_source(path + DIR_DELIM + "override.txt"); + m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides()); + m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides()); + } m_nodedef->setNodeRegistrationStatus(true); diff --git a/src/texture_override.cpp b/src/texture_override.cpp new file mode 100644 index 000000000..10d129b87 --- /dev/null +++ b/src/texture_override.cpp @@ -0,0 +1,120 @@ +/* +Minetest +Copyright (C) 2020 Hugues Ross + +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 "texture_override.h" + +#include "log.h" +#include "util/string.h" +#include +#include + +TextureOverrideSource::TextureOverrideSource(std::string filepath) +{ + std::ifstream infile(filepath.c_str()); + std::string line; + int line_index = 0; + while (std::getline(infile, line)) { + line_index++; + + // Also trim '\r' on DOS-style files + line = trim(line); + + // Ignore empty lines and comments + if (line.empty() || line[0] == '#') + continue; + + std::vector splitted = str_split(line, ' '); + if (splitted.size() != 3) { + warningstream << filepath << ":" << line_index + << " Syntax error in texture override \"" << line + << "\": Expected 3 arguments, got " << splitted.size() + << std::endl; + continue; + } + + TextureOverride texture_override = {}; + texture_override.id = splitted[0]; + texture_override.texture = splitted[2]; + + // Parse the target mask + std::vector targets = str_split(splitted[1], ','); + for (const std::string &target : targets) { + if (target == "top") + texture_override.target |= static_cast(OverrideTarget::TOP); + else if (target == "bottom") + texture_override.target |= static_cast(OverrideTarget::BOTTOM); + else if (target == "left") + texture_override.target |= static_cast(OverrideTarget::LEFT); + else if (target == "right") + texture_override.target |= static_cast(OverrideTarget::RIGHT); + else if (target == "front") + texture_override.target |= static_cast(OverrideTarget::FRONT); + else if (target == "back") + texture_override.target |= static_cast(OverrideTarget::BACK); + else if (target == "inventory") + texture_override.target |= static_cast(OverrideTarget::INVENTORY); + else if (target == "wield") + texture_override.target |= static_cast(OverrideTarget::WIELD); + else if (target == "sides") + texture_override.target |= static_cast(OverrideTarget::SIDES); + else if (target == "all" || target == "*") + texture_override.target |= static_cast(OverrideTarget::ALL_FACES); + else { + // Report invalid target + warningstream << filepath << ":" << line_index + << " Syntax error in texture override \"" << line + << "\": Unknown target \"" << target << "\"" + << std::endl; + } + } + + // If there are no valid targets, skip adding this override + if (texture_override.target == static_cast(OverrideTarget::INVALID)) { + continue; + } + + m_overrides.push_back(texture_override); + } +} + +//! Get all overrides that apply to item definitions +std::vector TextureOverrideSource::getItemTextureOverrides() +{ + std::vector found_overrides; + + for (const TextureOverride &texture_override : m_overrides) { + if (texture_override.hasTarget(OverrideTarget::ITEM_TARGETS)) + found_overrides.push_back(texture_override); + } + + return found_overrides; +} + +//! Get all overrides that apply to node definitions +std::vector TextureOverrideSource::getNodeTileOverrides() +{ + std::vector found_overrides; + + for (const TextureOverride &texture_override : m_overrides) { + if (texture_override.hasTarget(OverrideTarget::ALL_FACES)) + found_overrides.push_back(texture_override); + } + + return found_overrides; +} diff --git a/src/texture_override.h b/src/texture_override.h new file mode 100644 index 000000000..db03bd014 --- /dev/null +++ b/src/texture_override.h @@ -0,0 +1,72 @@ +/* +Minetest +Copyright (C) 2020 Hugues Ross + +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.h" +#include +#include + +//! Bitmask enum specifying what a texture override should apply to +enum class OverrideTarget : u8 +{ + INVALID = 0, + TOP = 1 << 0, + BOTTOM = 1 << 1, + LEFT = 1 << 2, + RIGHT = 1 << 3, + FRONT = 1 << 4, + BACK = 1 << 5, + INVENTORY = 1 << 6, + WIELD = 1 << 7, + + SIDES = LEFT | RIGHT | FRONT | BACK, + ALL_FACES = TOP | BOTTOM | SIDES, + ITEM_TARGETS = INVENTORY | WIELD, +}; + +struct TextureOverride +{ + std::string id; + std::string texture; + u8 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(overrideTarget)) != 0; + } +}; + +//! Class that provides texture override information from a texture pack +class TextureOverrideSource +{ +public: + TextureOverrideSource(std::string filepath); + + //! Get all overrides that apply to item definitions + std::vector getItemTextureOverrides(); + + //! Get all overrides that apply to node definitions + std::vector getNodeTileOverrides(); + +private: + std::vector m_overrides; +}; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index bb97da7b5..02c8b2660 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -423,6 +423,7 @@ src/subgame.cpp src/subgame.h src/terminal_chat_console.cpp src/terminal_chat_console.h +src/texture_override.cpp src/threading/atomic.h src/threading/event.cpp src/threading/mutex_auto_lock.h -- cgit v1.2.3 From 45999b74e610b13e8cda20c0c420e152b9837ea6 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 16 Apr 2020 18:32:07 +0200 Subject: Camera: Fix shooting line offsets (#9681) Removes duplicated offset calculations from Game and use whatever the Camera class returns. This keeps the eye position nicely in sync, and gets rid of duplicated code. --- src/client/camera.cpp | 26 +++++++++++++++----------- src/client/camera.h | 6 ++++++ src/client/game.cpp | 20 +++++++------------- src/client/localplayer.h | 3 +++ 4 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src/client') diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 871ea709d..fb1c3ff56 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -333,17 +333,21 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r fall_bobbing *= m_cache_fall_bobbing_amount; } - // Calculate players eye offset for different camera modes - v3f PlayerEyeOffset = player->getEyeOffset(); - if (m_camera_mode == CAMERA_MODE_FIRST) - PlayerEyeOffset += player->eye_offset_first; - else - PlayerEyeOffset += player->eye_offset_third; - - // Set head node transformation - m_headnode->setPosition(PlayerEyeOffset+v3f(0,cameratilt*-player->hurt_tilt_strength+fall_bobbing,0)); - m_headnode->setRotation(v3f(player->getPitch(), 0, cameratilt*player->hurt_tilt_strength)); - m_headnode->updateAbsolutePosition(); + // Calculate and translate the head SceneNode offsets + { + v3f eye_offset = player->getEyeOffset(); + if (m_camera_mode == CAMERA_MODE_FIRST) + eye_offset += player->eye_offset_first; + else + eye_offset += player->eye_offset_third; + + // Set head node transformation + eye_offset.Y += cameratilt * -player->hurt_tilt_strength + fall_bobbing; + m_headnode->setPosition(eye_offset); + m_headnode->setRotation(v3f(player->getPitch(), 0, + cameratilt * player->hurt_tilt_strength)); + m_headnode->updateAbsolutePosition(); + } // Compute relative camera position and target v3f rel_cam_pos = v3f(0,0,0); diff --git a/src/client/camera.h b/src/client/camera.h index 88de3570a..6ec37fe10 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -75,6 +75,12 @@ public: return m_camera_position; } + // Returns the absolute position of the head SceneNode in the world + inline v3f getHeadPosition() const + { + return m_headnode->getAbsolutePosition(); + } + // Get the camera direction (in absolute camera coordinates). // This has view bobbing applied. inline v3f getDirection() const diff --git a/src/client/game.cpp b/src/client/game.cpp index f7234eea6..4b2d7c652 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3029,16 +3029,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - v3f player_position = player->getPosition(); - v3f player_eye_position = player->getEyePosition(); - v3f camera_position = camera->getPosition(); - v3f camera_direction = camera->getDirection(); - v3s16 camera_offset = camera->getOffset(); - - if (camera->getCameraMode() == CAMERA_MODE_FIRST) - player_eye_position += player->eye_offset_first; - else - player_eye_position += player->eye_offset_third; + const v3f head_position = camera->getHeadPosition(); + const v3f camera_direction = camera->getDirection(); + const v3s16 camera_offset = camera->getOffset(); /* Calculate what block is the crosshair pointing to @@ -3053,11 +3046,11 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) core::line3d shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d(player_eye_position, - player_eye_position + camera_direction * BS * d); + shootline = core::line3d(head_position, + head_position + camera_direction * BS * d); } else { // prevent player pointing anything in front-view - shootline = core::line3d(camera_position, camera_position); + shootline = core::line3d(head_position, head_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -3145,6 +3138,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) } 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()) { // When button is held down in air, show continuous animation diff --git a/src/client/localplayer.h b/src/client/localplayer.h index d88ae17ac..345aec9d9 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -135,6 +135,9 @@ public: } v3f getPosition() const { return m_position; } + + // Non-transformed eye offset getters + // For accurate positions, use the Camera functions v3f getEyePosition() const { return m_position + getEyeOffset(); } v3f getEyeOffset() const; void setEyeHeight(float eye_height) { m_eye_height = eye_height; } -- cgit v1.2.3 From cdbe3c5e5784b34e548c58b08579ff55b3096fb9 Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Sun, 19 Apr 2020 19:47:13 +0300 Subject: Reuse object_shader for "wielditem" and "item" entity drawtypes (#9537) --- client/shaders/wielded_shader/opengl_fragment.glsl | 127 --------------------- client/shaders/wielded_shader/opengl_vertex.glsl | 32 ------ src/client/camera.cpp | 2 +- src/client/content_cao.cpp | 9 +- src/client/wieldmesh.cpp | 17 ++- src/client/wieldmesh.h | 2 + 6 files changed, 24 insertions(+), 165 deletions(-) delete mode 100644 client/shaders/wielded_shader/opengl_fragment.glsl delete mode 100644 client/shaders/wielded_shader/opengl_vertex.glsl (limited to 'src/client') diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl deleted file mode 100644 index 546aef71d..000000000 --- a/client/shaders/wielded_shader/opengl_fragment.glsl +++ /dev/null @@ -1,127 +0,0 @@ -uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; - -uniform vec4 skyBgColor; -uniform float fogDistance; -uniform vec3 eyePosition; - -varying vec3 vPosition; -varying vec3 worldPosition; - -varying vec3 eyeVec; -varying vec3 lightVec; - -bool normalTexturePresent = false; -bool texTileableHorizontal = false; -bool texTileableVertical = false; -bool texSeamless = false; - -const float e = 2.718281828459; -const float BS = 10.0; -const float fogStart = FOG_START; -const float fogShadingParameter = 1 / ( 1 - fogStart); - -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } - if (flags.g > 0.5) { - texTileableHorizontal = true; - } - if (flags.b > 0.5) { - texTileableVertical = true; - } - if (texTileableHorizontal && texTileableVertical) { - texSeamless = true; - } -} - -float intensity(vec3 color) -{ - return (color.r + color.g + color.b) / 3.0; -} - -float get_rgb_height(vec2 uv) -{ - if (texSeamless) { - return intensity(texture2D(baseTexture, uv).rgb); - } else { - return intensity(texture2D(baseTexture, clamp(uv, 0.0, 0.999)).rgb); - } -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - -void main(void) -{ - vec3 color; - vec4 bump; - vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif - -#if GENERATE_NORMALMAPS == 1 - if (normalTexturePresent == false) { - float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); - float r = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y)); - float br = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float b = get_rgb_height(vec2(uv.x, uv.y - SAMPLE_STEP)); - float bl = get_rgb_height(vec2(uv.x -SAMPLE_STEP, uv.y - SAMPLE_STEP)); - float l = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y)); - float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl); - float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr); - bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); - use_normalmap = true; - } -#endif - - vec4 base = texture2D(baseTexture, uv).rgba; - -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; - } -#else - color = base.rgb; -#endif - - vec4 col = vec4(color.rgb, base.a); - col *= gl_Color; - // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), - // the fog will only be rendered correctly if the last operation before the - // clamp() is an addition. Else, the clamp() seems to be ignored. - // E.g. the following won't work: - // float clarity = clamp(fogShadingParameter - // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); - // As additions usually come for free following a multiplication, the new formula - // should be more efficient as well. - // Note: clarity = (1 - fogginess) - float clarity = clamp(fogShadingParameter - - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(skyBgColor, col, clarity); - - gl_FragColor = vec4(col.rgb, base.a); -} diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl deleted file mode 100644 index 9f05b833a..000000000 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ /dev/null @@ -1,32 +0,0 @@ -uniform mat4 mWorldViewProj; -uniform mat4 mWorld; - -uniform vec3 eyePosition; -uniform float animationTimer; - -varying vec3 vPosition; -varying vec3 worldPosition; - -varying vec3 eyeVec; -varying vec3 lightVec; -varying vec3 tsEyeVec; -varying vec3 tsLightVec; - -const float e = 2.718281828459; -const float BS = 10.0; - -void main(void) -{ - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; - - vPosition = gl_Position.xyz; - worldPosition = (mWorld * gl_Vertex).xyz; - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - lightVec = sunPosition - worldPosition; - eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - - gl_FrontColor = gl_BackColor = gl_Color; -} diff --git a/src/client/camera.cpp b/src/client/camera.cpp index fb1c3ff56..69bd82a47 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -542,7 +542,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); - m_wieldnode->setColor(player->light_color); + m_wieldnode->setNodeLightColor(player->light_color); // Set render distance updateViewingRange(); diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 39ea4ab1e..e9e1cebd3 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -826,11 +826,12 @@ void GenericCAO::setNodeLight(u8 light) video::SColor color(255, light, light, light); if (m_prop.visual == "wielditem" || m_prop.visual == "item") { - // Since these types of visuals are using their own shader - // they should be handled separately if (m_wield_meshnode) - m_wield_meshnode->setColor(color); - } else if (m_enable_shaders) { + m_wield_meshnode->setNodeLightColor(color); + return; + } + + if (m_enable_shaders) { scene::ISceneNode *node = getSceneNode(); if (node == nullptr) diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 2c6807fab..997eb1b5b 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -347,7 +347,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che scene::SMesh *mesh = nullptr; if (m_enable_shaders) { - u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); + u32 shader_id = shdrsrc->getShader("object_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } @@ -471,6 +471,21 @@ void WieldMeshSceneNode::setColor(video::SColor c) } } +void WieldMeshSceneNode::setNodeLightColor(video::SColor color) +{ + if (!m_meshnode) + return; + + if (m_enable_shaders) { + for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) { + video::SMaterial &material = m_meshnode->getMaterial(i); + material.EmissiveColor = color; + } + } else { + setColor(color); + } +} + void WieldMeshSceneNode::render() { // note: if this method is changed to actually do something, diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h index 7c80a811b..933097230 100644 --- a/src/client/wieldmesh.h +++ b/src/client/wieldmesh.h @@ -87,6 +87,8 @@ public: // Must only be used if the constructor was called with lighting = false void setColor(video::SColor color); + void setNodeLightColor(video::SColor color); + scene::IMesh *getMesh() { return m_meshnode->getMesh(); } virtual void render(); -- cgit v1.2.3 From 6ba44d74526031a07bbc5093b708b8b99a27456a Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 22 Apr 2020 20:03:46 +0200 Subject: Android: add OpenGL ES 2 support (#9715) .. and bump gradle to 3.6.3 --- build/android/app/build.gradle | 9 ++++++--- build/android/build.gradle | 2 +- src/client/renderingengine.cpp | 7 ++----- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/client') diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index 6a34a6d70..9d14cdab8 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -63,8 +63,12 @@ task prepareAssets() { copy { from "${projRoot}/builtin" into "${assetsFolder}/builtin" } - copy { + /*copy { + // ToDo: fix Minetest shaders that currently don't work with OpenGL ES from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders" + }*/ + copy { + from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht" } copy { from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts" @@ -73,8 +77,7 @@ task prepareAssets() { from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}" } /*copy { - // locales broken right now - // ToDo: fix it! + // ToDo: fix broken locales from "${projRoot}/po" into "${assetsFolder}/po" }*/ copy { diff --git a/build/android/build.gradle b/build/android/build.gradle index b02e8c6df..c4de09bf8 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -15,7 +15,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.android.tools.build:gradle:3.6.3' classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index eae6ca7d3..f5aca8f58 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -130,12 +130,9 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu"); params.ZBufferBits = 24; #ifdef __ANDROID__ - // clang-format off params.PrivateData = porting::app_global; - params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM + "media" + - DIR_DELIM + "Shaders" + DIR_DELIM).c_str(); - // clang-format on -#elif ENABLE_GLES +#endif +#if ENABLE_GLES // there is no standardized path for these on desktop std::string rel_path = std::string("client") + DIR_DELIM + "shaders" + DIR_DELIM + "Irrlicht"; -- cgit v1.2.3 From ce5b0932f8e17244bd6c1e307f1fce76b16e6474 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 23 Apr 2020 12:16:36 +0200 Subject: Camera: Fix shootline line offsets II (#9730) --- src/client/game.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index 4b2d7c652..3429cc57b 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3029,7 +3029,6 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - const v3f head_position = camera->getHeadPosition(); const v3f camera_direction = camera->getDirection(); const v3s16 camera_offset = camera->getOffset(); @@ -3045,13 +3044,22 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) core::line3d shootline; - if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d(head_position, - head_position + camera_direction * BS * d); - } else { + switch (camera->getCameraMode()) { + case CAMERA_MODE_FIRST: + // Shoot from camera position, with bobbing + shootline.start = camera->getPosition(); + break; + case CAMERA_MODE_THIRD: + // Shoot from player head, no bobbing + shootline.start = camera->getHeadPosition(); + break; + case CAMERA_MODE_THIRD_FRONT: + shootline.start = camera->getHeadPosition(); // prevent player pointing anything in front-view - shootline = core::line3d(head_position, head_position); + d = 0; + break; } + shootline.end = shootline.start + camera_direction * BS * d; #ifdef HAVE_TOUCHSCREENGUI -- cgit v1.2.3 From cee3c5e73d7af2a876aa76275234ee76e7cb1bbc Mon Sep 17 00:00:00 2001 From: EvidenceB Kidscode <49488517+EvidenceBKidscode@users.noreply.github.com> Date: Sat, 25 Apr 2020 07:20:00 +0200 Subject: Add server side translations capability (#9733) * Add server side translations capability --- doc/lua_api.txt | 15 ++++++++++++++ src/client/client.cpp | 2 +- src/client/game.cpp | 2 +- src/clientiface.h | 6 ++++++ src/network/serverpackethandler.cpp | 3 +++ src/script/lua_api/l_env.cpp | 16 +++++++++++++++ src/script/lua_api/l_env.h | 3 +++ src/script/lua_api/l_server.cpp | 7 ++++++- src/server.cpp | 22 +++++++++++++++++++- src/server.h | 5 ++++- src/translation.cpp | 13 ++++++++++-- src/translation.h | 5 ++++- src/util/string.cpp | 41 ++++++++++++++++++++++++++++--------- src/util/string.h | 4 ++++ 14 files changed, 126 insertions(+), 18 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 77f06682f..3ca32649a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3176,8 +3176,22 @@ Strings that need to be translated can contain several escapes, preceded by `@`. `minetest.translate`, but is in translation files. * `@n` acts as a literal newline as well. +Server side translations +------------------------ + +On some specific cases, server translation could be useful. For example, filter +a list on labels and send results to client. A method is supplied to achieve +that: + +`minetest.get_translated_string(lang_code, string)`: Translates `string` using +translations for `lang_code` language. It gives the same result as if the string +was translated by the client. +The `lang_code` to use for a given player can be retrieved from +the table returned by `minetest.get_player_information(name)`. +IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes. +You do not need to use this to get translated strings to show up on the client. Perlin noise ============ @@ -4153,6 +4167,7 @@ Utilities connection_uptime = 200, -- seconds since client connected protocol_version = 32, -- protocol version used by client formspec_version = 2, -- supported formspec version + lang_code = "fr" -- Language code used for translation -- following information is available on debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client diff --git a/src/client/client.cpp b/src/client/client.cpp index 8ee0869cd..941fc203d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -736,7 +736,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) if (!name.empty()) { TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); - g_translations->loadTranslation(data); + g_client_translations->loadTranslation(data); return true; } diff --git a/src/client/game.cpp b/src/client/game.cpp index 3429cc57b..610522dc2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1055,7 +1055,7 @@ bool Game::startup(bool *kill, m_invert_mouse = g_settings->getBool("invert_mouse"); m_first_loop_after_window_activation = true; - g_translations->clear(); + g_client_translations->clear(); if (!init(map_dir, address, port, gamespec)) return false; diff --git a/src/clientiface.h b/src/clientiface.h index bf95df4a8..83fa6fe99 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -339,12 +339,18 @@ public: u8 getMinor() const { return m_version_minor; } u8 getPatch() const { return m_version_patch; } const std::string &getFull() const { return m_full_version; } + + void setLangCode(const std::string &code) { m_lang_code = code; } + const std::string &getLangCode() const { return m_lang_code; } private: // Version is stored in here after INIT before INIT2 u8 m_pending_serialization_version = SER_FMT_VER_INVALID; /* current state of client */ ClientState m_state = CS_Created; + + // Client sent language code + std::string m_lang_code; /* Blocks that have been sent to client. diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index c685500ce..5136eb0ec 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -311,6 +311,9 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) RemoteClient *client = getClient(peer_id, CS_InitDone); + // Keep client language for server translations + client->setLangCode(lang); + // Send active objects { PlayerSAO *sao = getPlayerSAO(peer_id); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 831464d3b..3fb58b8c8 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -40,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "util/string.h" +#include "translation.h" #ifndef SERVER #include "client/client.h" #endif @@ -1302,6 +1304,19 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L) return 0; } +// get_translated_string(lang_code, string) +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])); + lua_pushstring(L, string.c_str()); + return 1; +} + void ModApiEnvMod::Initialize(lua_State *L, int top) { API_FCT(set_node); @@ -1349,6 +1364,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(transforming_liquid_add); API_FCT(forceload_block); API_FCT(forceload_free_block); + API_FCT(get_translated_string); } void ModApiEnvMod::InitializeClient(lua_State *L, int top) diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index ac2f8b588..9050b4306 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -187,6 +187,9 @@ private: // stops forceloading a position static int l_forceload_free_block(lua_State *L); + // Get a string translated server side + static int l_get_translated_string(lua_State * L); + public: static void Initialize(lua_State *L, int top); static void InitializeClient(lua_State *L, int top); diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 00e849cdf..7137484e8 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -163,6 +163,7 @@ int ModApiServer::l_get_player_information(lua_State *L) u16 prot_vers; u8 ser_vers,major,minor,patch; std::string vers_string; + std::string lang_code; #define ERET(code) \ if (!(code)) { \ @@ -182,7 +183,7 @@ int ModApiServer::l_get_player_information(lua_State *L) &avg_jitter)) ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers, - &prot_vers, &major, &minor, &patch, &vers_string)) + &prot_vers, &major, &minor, &patch, &vers_string, &lang_code)) lua_newtable(L); int table = lua_gettop(L); @@ -237,6 +238,10 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_pushnumber(L, player->formspec_version); lua_settable(L, table); + lua_pushstring(L, "lang_code"); + lua_pushstring(L, lang_code.c_str()); + lua_settable(L, table); + #ifndef NDEBUG lua_pushstring(L,"serialization_version"); lua_pushnumber(L, ser_vers); diff --git a/src/server.cpp b/src/server.cpp index c32aa5306..af6d3e40d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "translation.h" class ClientNotFoundException : public BaseException { @@ -1266,7 +1267,8 @@ bool Server::getClientInfo( u8* major, u8* minor, u8* patch, - std::string* vers_string + std::string* vers_string, + std::string* lang_code ) { *state = m_clients.getClientState(peer_id); @@ -1286,6 +1288,7 @@ bool Server::getClientInfo( *minor = client->getMinor(); *patch = client->getPatch(); *vers_string = client->getFull(); + *lang_code = client->getLangCode(); m_clients.unlock(); @@ -3937,3 +3940,20 @@ void Server::broadcastModChannelMessage(const std::string &channel, m_script->on_modchannel_message(channel, sender, message); } } + +void Server::loadTranslationLanguage(const std::string &lang_code) +{ + if (g_server_translations->count(lang_code)) + return; // Already loaded + + 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(t)), + std::istreambuf_iterator()); + + (*g_server_translations)[lang_code].loadTranslation(data); + } + } +} diff --git a/src/server.h b/src/server.h index eecc2c0f0..b995aba28 100644 --- a/src/server.h +++ b/src/server.h @@ -334,7 +334,7 @@ public: bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval); bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime, u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch, - std::string* vers_string); + std::string* vers_string, std::string* lang_code); void printToConsoleOnly(const std::string &text); @@ -358,6 +358,9 @@ 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); + // Bind address Address m_bind_addr; diff --git a/src/translation.cpp b/src/translation.cpp index d17467ce7..8bbaee0a3 100644 --- a/src/translation.cpp +++ b/src/translation.cpp @@ -20,9 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "translation.h" #include "log.h" #include "util/string.h" +#include -static Translations main_translations; -Translations *g_translations = &main_translations; + +#ifndef SERVER +// Client translations +Translations client_translations; +Translations *g_client_translations = &client_translations; +#endif + +// Per language server translations +std::unordered_map server_translations; +std::unordered_map *g_server_translations = &server_translations; Translations::~Translations() { diff --git a/src/translation.h b/src/translation.h index 18fc6c38f..71423b15e 100644 --- a/src/translation.h +++ b/src/translation.h @@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class Translations; -extern Translations *g_translations; +extern std::unordered_map *g_server_translations; +#ifndef SERVER +extern Translations *g_client_translations; +#endif class Translations { diff --git a/src/util/string.cpp b/src/util/string.cpp index 2ee3ec735..6e1db798c 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -693,10 +693,12 @@ void str_replace(std::string &str, char from, char to) * before filling it again. */ -void translate_all(const std::wstring &s, size_t &i, std::wstring &res); +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res); -void translate_string(const std::wstring &s, const std::wstring &textdomain, - size_t &i, std::wstring &res) { +void translate_string(const std::wstring &s, Translations *translations, + const std::wstring &textdomain, size_t &i, std::wstring &res) +{ std::wostringstream output; std::vector args; int arg_number = 1; @@ -750,7 +752,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, if (arg_number >= 10) { errorstream << "Ignoring too many arguments to translation" << std::endl; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); continue; } @@ -758,7 +760,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, output << arg_number; ++arg_number; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); } else { // This is an escape sequence *inside* the template string to translate itself. @@ -767,8 +769,13 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, } } + std::wstring toutput; // Translate the template. - std::wstring toutput = g_translations->getTranslation(textdomain, output.str()); + if (translations != nullptr) + toutput = translations->getTranslation( + textdomain, output.str()); + else + toutput = output.str(); // Put back the arguments in the translated template. std::wostringstream result; @@ -802,7 +809,9 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, res = result.str(); } -void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res) +{ std::wostringstream output; while (i < s.length()) { // Not an escape sequence: just add the character. @@ -851,7 +860,7 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { if (parts.size() > 1) textdomain = parts[1]; std::wstring translated; - translate_string(s, textdomain, i, translated); + translate_string(s, translations, textdomain, i, translated); output << translated; } else { // Another escape sequence, such as colors. Preserve it. @@ -862,9 +871,21 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { res = output.str(); } -std::wstring translate_string(const std::wstring &s) { +// Translate string server side +std::wstring translate_string(const std::wstring &s, Translations *translations) +{ size_t i = 0; std::wstring res; - translate_all(s, i, res); + translate_all(s, i, translations, res); return res; } + +// Translate string client side +std::wstring translate_string(const std::wstring &s) +{ +#ifdef SERVER + return translate_string(s, nullptr); +#else + return translate_string(s, g_client_translations); +#endif +} diff --git a/src/util/string.h b/src/util/string.h index 0d2a6bdb2..185fb55e2 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +class Translations; + #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -650,6 +652,8 @@ std::vector > split(const std::basic_string &s, T delim) return tokens; } +std::wstring translate_string(const std::wstring &s, Translations *translations); + std::wstring translate_string(const std::wstring &s); inline std::wstring unescape_translate(const std::wstring &s) { -- cgit v1.2.3 From 49ed0ca00a9a79b5db0bd6cc4589d56d7f1b3d45 Mon Sep 17 00:00:00 2001 From: Paul Ouellette Date: Sat, 25 Apr 2020 03:42:18 -0400 Subject: Ensure game is shutdown if server throws exception (#9742) --- src/client/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index 610522dc2..3bdac786c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -4284,7 +4284,6 @@ void the_game(bool *kill, reconnect_requested, &chat_backend, gamespec, simple_singleplayer_mode)) { game.run(); - game.shutdown(); } } catch (SerializationError &e) { @@ -4300,4 +4299,5 @@ void the_game(bool *kill, strgettext("\nCheck debug.txt for details."); errorstream << error_message << std::endl; } + game.shutdown(); } -- cgit v1.2.3 From 73180a73da9b290e2da8629799696cd8c4eab268 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 25 Apr 2020 12:39:17 +0200 Subject: mapblock_mesh: Optimize a few things (#9713) --- src/client/mapblock_mesh.cpp | 91 +++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 47 deletions(-) (limited to 'src/client') diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index a5bee6b88..0a1619b3f 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -225,7 +225,7 @@ static u16 getSmoothLightCombined(const v3s16 &p, return f.light_propagates; }; - std::array obstructed = {{ 1, 1, 1, 1 }}; + bool obstructed[4] = { true, true, true, true }; add_node(0); bool opaque1 = !add_node(1); bool opaque2 = !add_node(2); @@ -372,6 +372,32 @@ void final_color_blend(video::SColor *result, Mesh generation helpers */ +// This table is moved outside getNodeVertexDirs to avoid the compiler using +// a mutex to initialize this table at runtime right in the hot path. +// For details search the internet for "cxa_guard_acquire". +static const v3s16 vertex_dirs_table[] = { + // ( 1, 0, 0) + v3s16( 1,-1, 1), v3s16( 1,-1,-1), + v3s16( 1, 1,-1), v3s16( 1, 1, 1), + // ( 0, 1, 0) + v3s16( 1, 1,-1), v3s16(-1, 1,-1), + v3s16(-1, 1, 1), v3s16( 1, 1, 1), + // ( 0, 0, 1) + v3s16(-1,-1, 1), v3s16( 1,-1, 1), + v3s16( 1, 1, 1), v3s16(-1, 1, 1), + // invalid + v3s16(), v3s16(), v3s16(), v3s16(), + // ( 0, 0,-1) + v3s16( 1,-1,-1), v3s16(-1,-1,-1), + v3s16(-1, 1,-1), v3s16( 1, 1,-1), + // ( 0,-1, 0) + v3s16( 1,-1, 1), v3s16(-1,-1, 1), + v3s16(-1,-1,-1), v3s16( 1,-1,-1), + // (-1, 0, 0) + v3s16(-1,-1,-1), v3s16(-1,-1, 1), + v3s16(-1, 1, 1), v3s16(-1, 1,-1) +}; + /* vertex_dirs: v3s16[4] */ @@ -384,44 +410,16 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs) 2: top-left 3: top-right */ - if (dir == v3s16(0, 0, 1)) { - // If looking towards z+, this is the face that is behind - // the center point, facing towards z+. - vertex_dirs[0] = v3s16(-1,-1, 1); - vertex_dirs[1] = v3s16( 1,-1, 1); - vertex_dirs[2] = v3s16( 1, 1, 1); - vertex_dirs[3] = v3s16(-1, 1, 1); - } else if (dir == v3s16(0, 0, -1)) { - // faces towards Z- - vertex_dirs[0] = v3s16( 1,-1,-1); - vertex_dirs[1] = v3s16(-1,-1,-1); - vertex_dirs[2] = v3s16(-1, 1,-1); - vertex_dirs[3] = v3s16( 1, 1,-1); - } else if (dir == v3s16(1, 0, 0)) { - // faces towards X+ - vertex_dirs[0] = v3s16( 1,-1, 1); - vertex_dirs[1] = v3s16( 1,-1,-1); - vertex_dirs[2] = v3s16( 1, 1,-1); - vertex_dirs[3] = v3s16( 1, 1, 1); - } else if (dir == v3s16(-1, 0, 0)) { - // faces towards X- - vertex_dirs[0] = v3s16(-1,-1,-1); - vertex_dirs[1] = v3s16(-1,-1, 1); - vertex_dirs[2] = v3s16(-1, 1, 1); - vertex_dirs[3] = v3s16(-1, 1,-1); - } else if (dir == v3s16(0, 1, 0)) { - // faces towards Y+ (assume Z- as "down" in texture) - vertex_dirs[0] = v3s16( 1, 1,-1); - vertex_dirs[1] = v3s16(-1, 1,-1); - vertex_dirs[2] = v3s16(-1, 1, 1); - vertex_dirs[3] = v3s16( 1, 1, 1); - } else if (dir == v3s16(0, -1, 0)) { - // faces towards Y- (assume Z+ as "down" in texture) - vertex_dirs[0] = v3s16( 1,-1, 1); - vertex_dirs[1] = v3s16(-1,-1, 1); - vertex_dirs[2] = v3s16(-1,-1,-1); - vertex_dirs[3] = v3s16( 1,-1,-1); - } + + // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), + // (0,0,1), (0,0,-1) + assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1); + + // Convert direction to single integer for table lookup + u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; + idx = (idx - 1) * 4; + + memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16)); } static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v) @@ -892,6 +890,8 @@ static void updateFastFaceRow( u16 lights[4] = {0, 0, 0, 0}; u8 waving; TileSpec tile; + + // Get info of first tile getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected, lights, waving, tile); @@ -902,8 +902,6 @@ static void updateFastFaceRow( // If tiling can be done, this is set to false in the next step bool next_is_different = true; - v3s16 p_next; - bool next_makes_face = false; v3s16 next_p_corrected; v3s16 next_face_dir_corrected; @@ -912,9 +910,9 @@ static void updateFastFaceRow( // If at last position, there is nothing to compare to and // the face must be drawn anyway if (j != MAP_BLOCKSIZE - 1) { - p_next = p + translate_dir; + p += translate_dir; - getTileInfo(data, p_next, face_dir, + getTileInfo(data, p, face_dir, next_makes_face, next_p_corrected, next_face_dir_corrected, next_lights, waving, @@ -923,7 +921,7 @@ static void updateFastFaceRow( if (next_makes_face == makes_face && next_p_corrected == p_corrected + translate_dir && next_face_dir_corrected == face_dir_corrected - && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0 + && memcmp(next_lights, lights, sizeof(lights)) == 0 // Don't apply fast faces to waving water. && (waving != 3 || !waving_liquids) && next_tile.isTileable(tile)) { @@ -961,10 +959,9 @@ static void updateFastFaceRow( makes_face = next_makes_face; p_corrected = next_p_corrected; face_dir_corrected = next_face_dir_corrected; - std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16)); + memcpy(lights, next_lights, sizeof(lights)); if (next_is_different) - tile = next_tile; - p = p_next; + tile = std::move(next_tile); // faster than copy } } -- cgit v1.2.3 From e1fc72c6f3450c82e2dd3e0892838498a6a8a092 Mon Sep 17 00:00:00 2001 From: theviper121 Date: Sun, 26 Apr 2020 12:32:04 -0500 Subject: Fix UpdateBonePosition() breaking animations (#9577) --- src/client/content_cao.cpp | 48 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index e9e1cebd3..aadd33bb9 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -890,6 +890,11 @@ void GenericCAO::updateNodePos() void GenericCAO::step(float dtime, ClientEnvironment *env) { + if (m_animated_meshnode) { + m_animated_meshnode->animateJoints(); + updateBonePosition(); + } + // Handle model animations and update positions instantly to prevent lags if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); @@ -1360,16 +1365,41 @@ void GenericCAO::updateBonePosition() return; m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render - for(std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string bone_name = (*ii).first; - v3f bone_pos = (*ii).second.X; - v3f bone_rot = (*ii).second.Y; + for (auto &it : m_bone_position) { + std::string bone_name = it.first; irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str()); - if(bone) - { - bone->setPosition(bone_pos); + if (bone) { + bone->setPosition(it.second.X); + bone->setRotation(it.second.Y); + } + } + + // search through bones to find mistakenly rotated bones due to bug in Irrlicht + for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) { + irr::scene::IBoneSceneNode *bone = m_animated_meshnode->getJointNode(i); + if (!bone) + continue; + + //If bone is manually positioned there is no need to perform the bug check + bool skip = false; + for (auto &it : m_bone_position) { + if (it.first == bone->getName()) { + skip = true; + break; + } + } + if (skip) + continue; + + // Workaround for Irrlicht bug + // We check each bone to see if it has been rotated ~180deg from its expected position due to a bug in Irricht + // when using EJUOR_CONTROL joint control. If the bug is detected we update the bone to the proper position + // and update the bones transformation. + v3f bone_rot = bone->getRelativeTransformation().getRotationDegrees(); + float offset = fabsf(bone_rot.X - bone->getRotation().X); + if (offset > 179.9f && offset < 180.1f) { bone->setRotation(bone_rot); + bone->updateAbsolutePosition(); } } } @@ -1583,7 +1613,7 @@ void GenericCAO::processMessage(const std::string &data) v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d(position, rotation); - updateBonePosition(); + // updateBonePosition(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString(is); -- cgit v1.2.3 From e0ea87f1f32273dba2eb5421c2a8c890479ba078 Mon Sep 17 00:00:00 2001 From: ANAND Date: Sat, 2 May 2020 16:22:11 +0530 Subject: set_fov: Add support for time-based transitions (#9705) --- doc/lua_api.txt | 15 ++++--- src/client/camera.cpp | 84 +++++++++++++++++++++++++++++-------- src/client/camera.h | 15 ++++++- src/network/clientpackethandler.cpp | 15 ++++++- src/network/networkprotocol.h | 3 +- src/player.h | 12 ++++-- src/script/lua_api/l_object.cpp | 11 +++-- src/server.cpp | 4 +- 8 files changed, 124 insertions(+), 35 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f9107b623..44f62f7a7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5998,15 +5998,18 @@ object you are working with still exists. * max: bubbles bar is not shown * See [Object properties] for more information * Is limited to range 0 ... 65535 (2^16 - 1) -* `set_fov(fov, is_multiplier)`: Sets player's FOV +* `set_fov(fov, is_multiplier, transition_time)`: Sets player's FOV * `fov`: FOV value. * `is_multiplier`: Set to `true` if the FOV value is a multiplier. Defaults to `false`. - * Set to 0 to clear FOV override. -* `get_fov()`: - * Returns player's FOV override in degrees, and a boolean depending on whether - the value is a multiplier. - * Returns 0 as first value if player's FOV hasn't been overridden. + * `transition_time`: If defined, enables smooth FOV transition. + Interpreted as the time (in seconds) to reach target FOV. + If set to 0, FOV change is instantaneous. Defaults to 0. + * Set `fov` to 0 to clear FOV override. +* `get_fov()`: Returns the following: + * Server-sent FOV value. Returns 0 if an FOV override doesn't exist. + * Boolean indicating whether the FOV value is a multiplier. + * Time (in seconds) taken for the FOV transition. Set by `set_fov`. * `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 69bd82a47..1a5253db4 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -86,6 +86,51 @@ Camera::~Camera() m_wieldmgr->drop(); } +void Camera::notifyFovChange() +{ + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + assert(player); + + PlayerFovSpec spec = player->getFov(); + + /* + * Update m_old_fov_degrees first - it serves as the starting point of the + * upcoming transition. + * + * If an FOV transition is already active, mark current FOV as the start of + * the new transition. If not, set it to the previous transition's target FOV. + */ + if (m_fov_transition_active) + m_old_fov_degrees = m_curr_fov_degrees; + else + m_old_fov_degrees = m_server_sent_fov ? m_target_fov_degrees : m_cache_fov; + + /* + * Update m_server_sent_fov next - it corresponds to the target FOV of the + * upcoming transition. + * + * Set it to m_cache_fov, if server-sent FOV is 0. Otherwise check if + * server-sent FOV is a multiplier, and multiply it with m_cache_fov instead + * of overriding. + */ + if (spec.fov == 0.0f) { + m_server_sent_fov = false; + m_target_fov_degrees = m_cache_fov; + } else { + m_server_sent_fov = true; + m_target_fov_degrees = spec.is_multiplier ? m_cache_fov * spec.fov : spec.fov; + } + + if (spec.transition_time > 0.0f) + m_fov_transition_active = true; + + // If FOV smooth transition is active, initialize required variables + if (m_fov_transition_active) { + m_transition_time = spec.transition_time; + m_fov_diff = m_target_fov_degrees - m_old_fov_degrees; + } +} + bool Camera::successfullyCreated(std::string &error_message) { if (!m_playernode) { @@ -462,33 +507,38 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_camera_position = my_cp; /* - * Apply server-sent FOV. If server doesn't enforce FOV, - * check for zoom and set to zoom FOV. - * Otherwise, default to m_cache_fov + * Apply server-sent FOV, instantaneous or smooth transition. + * If not, check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov. */ - - f32 fov_degrees; - PlayerFovSpec fov_spec = player->getFov(); - if (fov_spec.fov > 0.0f) { - // If server-sent FOV is a multiplier, multiply - // it with m_cache_fov instead of overriding - if (fov_spec.is_multiplier) - fov_degrees = m_cache_fov * fov_spec.fov; - else - fov_degrees = fov_spec.fov; + if (m_fov_transition_active) { + // Smooth FOV transition + // Dynamically calculate FOV delta based on frametimes + f32 delta = (frametime / m_transition_time) * m_fov_diff; + m_curr_fov_degrees += delta; + + // Mark transition as complete if target FOV has been reached + if ((m_fov_diff > 0.0f && m_curr_fov_degrees >= m_target_fov_degrees) || + (m_fov_diff < 0.0f && m_curr_fov_degrees <= m_target_fov_degrees)) { + m_fov_transition_active = false; + m_curr_fov_degrees = m_target_fov_degrees; + } + } else if (m_server_sent_fov) { + // Instantaneous FOV change + m_curr_fov_degrees = m_target_fov_degrees; } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { // Player requests zoom, apply zoom FOV - fov_degrees = player->getZoomFOV(); + m_curr_fov_degrees = player->getZoomFOV(); } else { // Set to client's selected FOV - fov_degrees = m_cache_fov; + m_curr_fov_degrees = m_cache_fov; } - fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); + m_curr_fov_degrees = rangelim(m_curr_fov_degrees, 1.0f, 160.0f); // FOV and aspect ratio const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); m_aspect = (f32) window_size.X / (f32) window_size.Y; - m_fov_y = fov_degrees * M_PI / 180.0; + m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); diff --git a/src/client/camera.h b/src/client/camera.h index 6ec37fe10..3a59637bc 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -112,6 +112,9 @@ public: return MYMAX(m_fov_x, m_fov_y); } + // Notify about new server-sent FOV and initialize smooth FOV transition + void notifyFovChange(); + // Checks if the constructor was able to create the scene nodes bool successfullyCreated(std::string &error_message); @@ -186,6 +189,9 @@ private: Client *m_client; + // Default Client FOV (as defined by the "fov" setting) + f32 m_cache_fov; + // Absolute camera position v3f m_camera_position; // Absolute camera direction @@ -193,6 +199,14 @@ private: // Camera offset v3s16 m_camera_offset; + // Server-sent FOV variables + bool m_server_sent_fov = false; + f32 m_curr_fov_degrees, m_old_fov_degrees, m_target_fov_degrees; + + // FOV transition variables + bool m_fov_transition_active = false; + f32 m_fov_diff, m_transition_time; + v2f m_wieldmesh_offset = v2f(55.0f, -35.0f); v2f m_arm_dir; v2f m_cam_vel; @@ -230,7 +244,6 @@ private: f32 m_cache_fall_bobbing_amount; f32 m_cache_view_bobbing_amount; - f32 m_cache_fov; bool m_arm_inertia; std::list m_nametags; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d19dc3818..6428ed752 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #include "util/base64.h" +#include "client/camera.h" #include "chatmessage.h" #include "client/clientmedia.h" #include "log.h" @@ -530,11 +531,21 @@ void Client::handleCommand_Movement(NetworkPacket* pkt) void Client::handleCommand_Fov(NetworkPacket *pkt) { f32 fov; - bool is_multiplier; + bool is_multiplier = false; + f32 transition_time = 0.0f; + *pkt >> fov >> is_multiplier; + // Wrap transition_time extraction within a + // try-catch to preserve backwards compat + try { + *pkt >> transition_time; + } catch (PacketError &e) {}; + LocalPlayer *player = m_env.getLocalPlayer(); - player->setFov({ fov, is_multiplier }); + assert(player); + player->setFov({ fov, is_multiplier, transition_time }); + m_camera->notifyFovChange(); } void Client::handleCommand_HP(NetworkPacket *pkt) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 4b7345b15..73523ea42 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -384,8 +384,9 @@ enum ToClientCommand /* Sends an FOV override/multiplier to client. - float fov + f32 fov bool is_multiplier + f32 transition_time */ TOCLIENT_DEATHSCREEN = 0x37, diff --git a/src/player.h b/src/player.h index de7f427e9..3bc7762fa 100644 --- a/src/player.h +++ b/src/player.h @@ -35,7 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc., struct PlayerFovSpec { f32 fov; + + // Whether to multiply the client's FOV or to override it bool is_multiplier; + + // The time to be take to trasition to the new FOV value. + // Transition is instantaneous if omitted. Omitted by default. + f32 transition_time; }; struct PlayerControl @@ -186,12 +192,12 @@ public: void setFov(const PlayerFovSpec &spec) { - m_fov_spec = spec; + m_fov_override_spec = spec; } const PlayerFovSpec &getFov() const { - return m_fov_spec; + return m_fov_override_spec; } u32 keyPressed = 0; @@ -208,7 +214,7 @@ protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; u16 m_wield_index = 0; - PlayerFovSpec m_fov_spec = { 0.0f, false }; + PlayerFovSpec m_fov_override_spec = { 0.0f, false, 0.0f }; std::vector hud; private: diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 77e1e7dc2..dcaee10b2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1249,7 +1249,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } -// set_fov(self, degrees[, is_multiplier]) +// set_fov(self, degrees[, is_multiplier, transition_time]) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -1258,7 +1258,11 @@ int ObjectRef::l_set_fov(lua_State *L) if (!player) return 0; - player->setFov({ static_cast(luaL_checknumber(L, 2)), readParam(L, 3) }); + player->setFov({ + static_cast(luaL_checknumber(L, 2)), + readParam(L, 3, false), + lua_isnumber(L, 4) ? static_cast(luaL_checknumber(L, 4)) : 0.0f + }); getServer(L)->SendPlayerFov(player->getPeerId()); return 0; @@ -1276,8 +1280,9 @@ int ObjectRef::l_get_fov(lua_State *L) 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 2; + return 3; } // set_breath(self, breath) diff --git a/src/server.cpp b/src/server.cpp index a7eb52837..0346d197d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1892,10 +1892,10 @@ void Server::SendMovePlayer(session_t peer_id) void Server::SendPlayerFov(session_t peer_id) { - NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id); PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); - pkt << fov_spec.fov << fov_spec.is_multiplier; + pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time; Send(&pkt); } -- cgit v1.2.3 From cad5b987ad4720bd6a49d3604be9e81ea348f799 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 4 May 2020 20:19:12 +0200 Subject: Sky API: Rename *_tint to fog_*_tint for consistency --- src/client/game.cpp | 12 ++++++------ src/client/sky.cpp | 12 ++++++------ src/network/clientpackethandler.cpp | 8 ++++---- src/network/networkprotocol.h | 6 +++--- src/remoteplayer.cpp | 6 +++--- src/script/lua_api/l_object.cpp | 22 +++++++++++----------- src/server.cpp | 4 ++-- src/skyparams.h | 6 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index 3bdac786c..d1eb3bba2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2799,9 +2799,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Update mesh based skybox colours if applicable. sky->setSkyColors(*event->set_sky); sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); } else if (event->set_sky->type == "skybox" && event->set_sky->textures.size() == 6) { @@ -2811,9 +2811,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) sky->setFallbackBgColor(event->set_sky->bgcolor); // Set sunrise and sunset fog tinting: sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); // Add textures to skybox. for (int i = 0; i < 6; i++) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 7a7b188ce..ce33b96ae 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -529,7 +529,7 @@ void Sky::update(float time_of_day, float time_brightness, pointcolor_sun_f.g = pointcolor_light * (float)m_materials[3].EmissiveColor.getGreen() / 255; } else if (!m_default_tint) { - pointcolor_sun_f = m_sky_params.sun_tint; + pointcolor_sun_f = m_sky_params.fog_sun_tint; } else { pointcolor_sun_f.r = pointcolor_light * 1; pointcolor_sun_f.b = pointcolor_light * @@ -548,9 +548,9 @@ void Sky::update(float time_of_day, float time_brightness, ); } else { pointcolor_moon_f = video::SColorf( - (m_sky_params.moon_tint.getRed() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getGreen() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getBlue() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light, 1 ); } @@ -941,8 +941,8 @@ void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint) { // Change sun and moon tinting: - m_sky_params.sun_tint = sun_tint; - m_sky_params.moon_tint = moon_tint; + m_sky_params.fog_sun_tint = sun_tint; + m_sky_params.fog_moon_tint = moon_tint; // Faster than comparing strings every rendering frame if (use_sun_tint == "default") m_default_tint = true; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 6428ed752..8d0225a3d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1276,9 +1276,9 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) // Fix for "regular" skies, as color isn't kept: if (skybox.type == "regular") { skybox.sky_color = sky_defaults.getSkyColorDefaults(); - skybox.tint_type = "default"; - skybox.moon_tint = video::SColor(255, 255, 255, 255); - skybox.sun_tint = video::SColor(255, 255, 255, 255); + skybox.fog_tint_type = "default"; + skybox.fog_moon_tint = video::SColor(255, 255, 255, 255); + skybox.fog_sun_tint = video::SColor(255, 255, 255, 255); } else { sun.visible = false; @@ -1313,7 +1313,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) std::string texture; *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >> - skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type; + skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type; if (skybox.type == "skybox") { *pkt >> texture_count; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 73523ea42..527ebba7c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -634,9 +634,9 @@ enum ToClientCommand u8[4] night_sky (ARGB) u8[4] night_horizon (ARGB) u8[4] indoors (ARGB) - u8[4] sun_tint (ARGB) - u8[4] moon_tint (ARGB) - std::string tint_type + u8[4] fog_sun_tint (ARGB) + u8[4] fog_moon_tint (ARGB) + std::string fog_tint_type */ TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50, diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 7a603d53e..bef60c792 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -74,9 +74,9 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): m_skybox_params.sky_color = sky_defaults.getSkyColorDefaults(); m_skybox_params.type = "regular"; m_skybox_params.clouds = true; - m_skybox_params.sun_tint = video::SColor(255, 244, 125, 29); - m_skybox_params.moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); - m_skybox_params.tint_type = "default"; + m_skybox_params.fog_sun_tint = video::SColor(255, 244, 125, 29); + m_skybox_params.fog_moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); + m_skybox_params.fog_tint_type = "default"; m_sun_params = sky_defaults.getSunDefaults(); m_moon_params = sky_defaults.getMoonDefaults(); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index dcaee10b2..f71130378 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1782,19 +1782,19 @@ int ObjectRef::l_set_sky(lua_State *L) lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.sun_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.sun_tint); + read_color(L, -1, &skybox_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.moon_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.moon_tint); + read_color(L, -1, &skybox_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.tint_type = luaL_checkstring(L, -1); + skybox_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1912,12 +1912,12 @@ int ObjectRef::l_get_sky_color(lua_State *L) push_ARGB8(L, skybox_params.sky_color.indoors); lua_setfield(L, -2, "indoors"); } - push_ARGB8(L, skybox_params.sun_tint); - lua_setfield(L, -2, "sun_tint"); - push_ARGB8(L, skybox_params.moon_tint); - lua_setfield(L, -2, "moon_tint"); - lua_pushstring(L, skybox_params.tint_type.c_str()); - lua_setfield(L, -2, "tint_type"); + push_ARGB8(L, skybox_params.fog_sun_tint); + lua_setfield(L, -2, "fog_sun_tint"); + push_ARGB8(L, skybox_params.fog_moon_tint); + lua_setfield(L, -2, "fog_moon_tint"); + lua_pushstring(L, skybox_params.fog_tint_type.c_str()); + lua_setfield(L, -2, "fog_tint_type"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 0346d197d..05584be2d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1770,8 +1770,8 @@ void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms) pkt << params.clouds; } else { // Handle current clients and future clients pkt << params.bgcolor << params.type - << params.clouds << params.sun_tint - << params.moon_tint << params.tint_type; + << params.clouds << params.fog_sun_tint + << params.fog_moon_tint << params.fog_tint_type; if (params.type == "skybox") { pkt << (u16) params.textures.size(); diff --git a/src/skyparams.h b/src/skyparams.h index 9fdfd89da..c362ef8f3 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -37,9 +37,9 @@ struct SkyboxParams std::vector textures; bool clouds; SkyColor sky_color; - video::SColor sun_tint; - video::SColor moon_tint; - std::string tint_type; + video::SColor fog_sun_tint; + video::SColor fog_moon_tint; + std::string fog_tint_type; }; struct SunParams -- cgit v1.2.3 From f1a05d0f71d69641fd954daf68d13acad91114f1 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Tue, 5 May 2020 08:38:18 +0200 Subject: Fix broken client if openal cannot be opened (#9804) --- src/client/clientlauncher.cpp | 2 +- src/client/game.cpp | 2 +- src/client/sound_openal.cpp | 33 +++++++++++++++++++++++++-------- src/gui/guiEngine.cpp | 4 ++-- 4 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/client') diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 2a9d6097f..f18915a55 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -105,7 +105,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) } RenderingEngine::get_instance()->setupTopLevelWindow(PROJECT_NAME_C); - + /* This changes the minimum allowed number of vertices in a VBO. Default is 500. diff --git a/src/client/game.cpp b/src/client/game.cpp index d1eb3bba2..1577a37db 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1249,7 +1249,7 @@ bool Game::init( bool Game::initSound() { #if USE_SOUND - if (g_settings->getBool("enable_sound")) { + if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) { infostream << "Attempting to use OpenAL audio" << std::endl; sound = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); if (!sound) diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index d0f935a7a..20a651c1d 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -275,25 +275,38 @@ public: m_device(nullptr, delete_alcdevice), m_context(nullptr, delete_alccontext) { - if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) - throw std::runtime_error("Audio: Global Initialization: Device Open"); + } + + bool init() + { + if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) { + errorstream << "Audio: Global Initialization: Failed to open device" << std::endl; + return false; + } if (!(m_context = unique_ptr_alccontext( alcCreateContext(m_device.get(), nullptr), delete_alccontext))) { - throw std::runtime_error("Audio: Global Initialization: Context Create"); + errorstream << "Audio: Global Initialization: Failed to create context" << std::endl; + return false; } - if (!alcMakeContextCurrent(m_context.get())) - throw std::runtime_error("Audio: Global Initialization: Context Current"); + if (!alcMakeContextCurrent(m_context.get())) { + errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl; + return false; + } alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); - if (alGetError() != AL_NO_ERROR) - throw std::runtime_error("Audio: Global Initialization: OpenAL Error"); + if (alGetError() != AL_NO_ERROR) { + errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl; + return false; + } infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION) << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER) << std::endl; + + return true; } ~SoundManagerSingleton() @@ -682,7 +695,11 @@ public: std::shared_ptr createSoundManagerSingleton() { - return std::shared_ptr(new SoundManagerSingleton()); + auto smg = std::make_shared(); + if (!smg->init()) { + smg.reset(); + } + return smg; } ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher) diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 3107d64cd..b40707d01 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -144,10 +144,10 @@ GUIEngine::GUIEngine(JoystickController *joystick, //create soundmanager MenuMusicFetcher soundfetcher; #if USE_SOUND - if (g_settings->getBool("enable_sound")) + if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) m_sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); #endif - if(!m_sound_manager) + if (!m_sound_manager) m_sound_manager = &dummySoundManager; //create topleft header -- cgit v1.2.3 From 4f9ccd89b347dad3db5ce63d3405a8d60c163af5 Mon Sep 17 00:00:00 2001 From: Jozef Behran Date: Wed, 6 May 2020 21:35:18 +0200 Subject: Get rid of non-ascii characters in the debug display code (#8821) --- src/client/gameui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client') diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 138dfb4da..bbe7caeb1 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -128,9 +128,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)) << "° " + << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "\xC2\xB0 " << yawToDirectionString(cam.camera_yaw) - << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°" + << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "\xC2\xB0" << " | seed: " << ((u64)client->getMapSeed()); if (pointed_old.type == POINTEDTHING_NODE) { -- cgit v1.2.3 From 454dbf83a9bf292910c1495a2aa49fd8b960c28f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 7 May 2020 22:38:41 +0200 Subject: Server class code cleanups (#9769) * Server::overrideDayNightRatio doesn't require to return bool There is no sense to sending null player, the caller should send a valid object * Server::init: make private & cleanup This function is always called before start() and loads some variables which can be loaded in constructor directly. Make it private and call it directly in start * Split Server inventory responsibility to a dedicated object This splits permit to found various historical issues: * duplicate lookups on player connection * sending inventory to non related player when a player connects * non friendly lookups on detached inventories ownership This reduce the detached inventory complexity and also increased the lookup performance in a quite interesting way for servers with thousands of inventories. --- src/client/game.cpp | 1 - src/main.cpp | 2 - src/network/serverpackethandler.cpp | 11 ++- src/script/lua_api/l_base.cpp | 5 + src/script/lua_api/l_base.h | 2 + src/script/lua_api/l_inventory.cpp | 13 +-- src/script/lua_api/l_object.cpp | 7 +- src/server.cpp | 192 +++++------------------------------- src/server.h | 33 ++----- src/server/CMakeLists.txt | 1 + src/server/serverinventorymgr.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/server/serverinventorymgr.h | 60 +++++++++++ 12 files changed, 312 insertions(+), 207 deletions(-) create mode 100644 src/server/serverinventorymgr.cpp create mode 100644 src/server/serverinventorymgr.h (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index 1577a37db..4d7a85526 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1302,7 +1302,6 @@ bool Game::createSingleplayerServer(const std::string &map_dir, } server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); - server->init(); server->start(); return true; diff --git a/src/main.cpp b/src/main.cpp index 147f686ed..b3b17c2d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -887,7 +887,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true, &iface); - server.init(); g_term_console.setup(&iface, &kill, admin_nick); @@ -922,7 +921,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true); - server.init(); server.start(); // Run server diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index adaa9a965..39a912827 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" @@ -620,9 +621,9 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ma->from_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ma->from_inv); + m_inventory_mgr->setInventoryModified(ma->from_inv); if (ma->from_inv != ma->to_inv) - setInventoryModified(ma->to_inv); + m_inventory_mgr->setInventoryModified(ma->to_inv); bool from_inv_is_current_player = (ma->from_inv.type == InventoryLocation::PLAYER) && @@ -687,7 +688,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) da->from_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(da->from_inv); + m_inventory_mgr->setInventoryModified(da->from_inv); /* Disable dropping items out of craftpreview @@ -723,7 +724,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ca->craft_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ca->craft_inv); + m_inventory_mgr->setInventoryModified(ca->craft_inv); //bool craft_inv_is_current_player = // (ca->craft_inv.type == InventoryLocation::PLAYER) && @@ -739,7 +740,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) } // Do the action - a->apply(this, playersao, this); + a->apply(m_inventory_mgr.get(), playersao, this); // Eat the action delete a; } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index b8658f62b..2bee09436 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -45,6 +45,11 @@ Server *ModApiBase::getServer(lua_State *L) return getScriptApiBase(L)->getServer(); } +ServerInventoryManager *ModApiBase::getServerInventoryMgr(lua_State *L) +{ + return getScriptApiBase(L)->getServer()->getInventoryMgr(); +} + #ifndef SERVER Client *ModApiBase::getClient(lua_State *L) { diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index e32647628..65fce8481 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -38,12 +38,14 @@ class GUIEngine; class ScriptApiBase; class Server; class Environment; +class ServerInventoryManager; class ModApiBase : protected LuaHelper { public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); + static ServerInventoryManager *getServerInventoryMgr(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 4c8977898..e41b5cb41 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" +#include "server/serverinventorymgr.h" #include "remoteplayer.h" /* @@ -38,7 +39,7 @@ InvRef* InvRef::checkobject(lua_State *L, int narg) Inventory* InvRef::getinv(lua_State *L, InvRef *ref) { - return getServer(L)->getInventory(ref->m_loc); + return getServerInventoryMgr(L)->getInventory(ref->m_loc); } InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, @@ -54,7 +55,7 @@ InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed - getServer(L)->setInventoryModified(ref->m_loc); + getServerInventoryMgr(L)->setInventoryModified(ref->m_loc); } // Exported functions @@ -497,7 +498,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -515,7 +516,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) lua_pop(L, 1); } - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -530,7 +531,7 @@ int ModApiInventory::l_create_detached_inventory_raw(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *name = luaL_checkstring(L, 1); std::string player = readParam(L, 2, ""); - if (getServer(L)->createDetachedInventory(name, player) != NULL) { + if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) { InventoryLocation loc; loc.setDetached(name); InvRef::create(L, loc); @@ -545,7 +546,7 @@ int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L) { NO_MAP_LOCK_REQUIRED; const std::string &name = luaL_checkstring(L, 1); - lua_pushboolean(L, getServer(L)->removeDetachedInventory(name)); + lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name)); return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f71130378..0a9f3117b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" /* ObjectRef @@ -289,7 +290,7 @@ int ObjectRef::l_get_inventory(lua_State *L) if (co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -2172,9 +2173,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) ratio = readParam(L, 2); } - if (!getServer(L)->overrideDayNightRatio(player, do_override, ratio)) - return 0; - + getServer(L)->overrideDayNightRatio(player, do_override, ratio); lua_pushboolean(L, true); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 05584be2d..16e026ce2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "translation.h" class ClientNotFoundException : public BaseException @@ -338,11 +339,6 @@ Server::~Server() infostream << "Server: Deinitializing scripting" << std::endl; delete m_script; - // Delete detached inventories - for (auto &detached_inventory : m_detached_inventories) { - delete detached_inventory.second; - } - while (!m_unsent_map_edit_queue.empty()) { delete m_unsent_map_edit_queue.front(); m_unsent_map_edit_queue.pop(); @@ -388,6 +384,9 @@ void Server::init() m_script = new ServerScripting(this); + // Must be created before mod loading because we have some inventory creation + m_inventory_mgr = std::unique_ptr(new ServerInventoryManager()); + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); m_modmgr->loadMods(m_script); @@ -422,6 +421,7 @@ void Server::init() // Initialize Environment m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + m_inventory_mgr->setEnv(m_env); m_clients.setEnv(m_env); if (!servermap->settings_mgr.makeMapgenParams()) @@ -443,6 +443,8 @@ void Server::init() m_env->loadMeta(); + // Those settings can be overwritten in world.mt, they are + // intended to be cached after environment loading. m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags"); @@ -451,6 +453,8 @@ void Server::init() void Server::start() { + init(); + infostream << "Starting server on " << m_bind_addr.serializeString() << "..." << std::endl; @@ -1180,82 +1184,6 @@ void Server::onMapEditEvent(const MapEditEvent &event) m_unsent_map_edit_queue.push(new MapEditEvent(event)); } -Inventory* Server::getInventory(const InventoryLocation &loc) -{ - switch (loc.type) { - case InventoryLocation::UNDEFINED: - case InventoryLocation::CURRENT_PLAYER: - break; - case InventoryLocation::PLAYER: - { - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - if(!player) - return NULL; - PlayerSAO *playersao = player->getPlayerSAO(); - if(!playersao) - return NULL; - return playersao->getInventory(); - } - break; - case InventoryLocation::NODEMETA: - { - NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); - if(!meta) - return NULL; - return meta->getInventory(); - } - break; - case InventoryLocation::DETACHED: - { - if(m_detached_inventories.count(loc.name) == 0) - return NULL; - return m_detached_inventories[loc.name]; - } - break; - default: - sanity_check(false); // abort - break; - } - return NULL; -} - -void Server::setInventoryModified(const InventoryLocation &loc) -{ - switch(loc.type){ - case InventoryLocation::UNDEFINED: - break; - case InventoryLocation::PLAYER: - { - - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - - if (!player) - return; - - player->setModified(true); - player->inventory.setModified(true); - // Updates are sent in ServerEnvironment::step() - } - break; - case InventoryLocation::NODEMETA: - { - MapEditEvent event; - event.type = MEET_BLOCK_NODE_METADATA_CHANGED; - event.p = loc.p; - m_env->getMap().dispatchEvent(event); - } - break; - case InventoryLocation::DETACHED: - { - // Updates are sent in ServerEnvironment::step() - } - break; - default: - sanity_check(false); // abort - break; - } -} - void Server::SetBlocksNotSent(std::map& block) { std::vector clients = m_clients.getClientIDs(); @@ -2712,40 +2640,20 @@ void Server::sendRequestedMedia(session_t peer_id, } } -void Server::sendDetachedInventory(const std::string &name, session_t peer_id) +void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { - const auto &inv_it = m_detached_inventories.find(name); - const auto &player_it = m_detached_inventories_player.find(name); - - if (player_it == m_detached_inventories_player.end() || - player_it->second.empty()) { - // OK. Send to everyone - } else { - if (!m_env) - return; // Mods are not done loading - - RemotePlayer *p = m_env->getPlayer(player_it->second.c_str()); - if (!p) - return; // Player is offline - - if (peer_id != PEER_ID_INEXISTENT && peer_id != p->getPeerId()) - return; // Caller requested send to a different player, so don't send. - - peer_id = p->getPeerId(); - } - NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); pkt << name; - if (inv_it == m_detached_inventories.end()) { + if (!inventory) { pkt << false; // Remove inventory } else { pkt << true; // Update inventory // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); - inv_it->second->serialize(os); - inv_it->second->setModified(false); + inventory->serialize(os); + inventory->setModified(false); const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients @@ -2760,16 +2668,17 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) void Server::sendDetachedInventories(session_t peer_id, bool incremental) { - for (const auto &detached_inventory : m_detached_inventories) { - const std::string &name = detached_inventory.first; - if (incremental) { - Inventory *inv = detached_inventory.second; - if (!inv || !inv->checkModified()) - continue; - } - - sendDetachedInventory(name, peer_id); + // Lookup player name, to filter detached inventories just after + std::string peer_name; + if (peer_id != PEER_ID_INEXISTENT) { + peer_name = getClient(peer_id, CS_Created)->getName(); } + + auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) { + sendDetachedInventory(inv, name, peer_id); + }; + + m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb); } /* @@ -3442,15 +3351,12 @@ void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms) SendCloudParams(player->getPeerId(), params); } -bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, +void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, float ratio) { - if (!player) - return false; - + sanity_check(player); player->overrideDayNightRatio(do_override, ratio); SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio); - return true; } void Server::notifyPlayers(const std::wstring &msg) @@ -3541,52 +3447,6 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player) -{ - if(m_detached_inventories.count(name) > 0){ - infostream<<"Server clearing detached inventory \""<second; - m_detached_inventories.erase(inv_it); - - if (!m_env) // Mods are not done loading - return true; - - const auto &player_it = m_detached_inventories_player.find(name); - if (player_it != m_detached_inventories_player.end()) { - RemotePlayer *player = m_env->getPlayer(player_it->second.c_str()); - - if (player && player->getPeerId() != PEER_ID_INEXISTENT) - sendDetachedInventory(name, player->getPeerId()); - - m_detached_inventories_player.erase(player_it); - } else { - // Notify all players about the change - sendDetachedInventory(name, PEER_ID_INEXISTENT); - } - return true; -} - // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, @@ -3607,7 +3467,7 @@ bool Server::rollbackRevertActions(const std::list &actions, for (const RollbackAction &action : actions) { num_tried++; - bool success = action.applyRevert(map, this, this); + bool success = action.applyRevert(map, m_inventory_mgr.get(), this); if(!success){ num_failed++; std::ostringstream os; diff --git a/src/server.h b/src/server.h index 71059dd30..7a1de9370 100644 --- a/src/server.h +++ b/src/server.h @@ -68,6 +68,7 @@ struct MoonParams; struct StarParams; class ServerThread; class ServerModManager; +class ServerInventoryManager; enum ClientDeletionReason { CDR_LEAVE, @@ -116,7 +117,7 @@ struct ServerPlayingSound }; class Server : public con::PeerHandler, public MapEventReceiver, - public InventoryManager, public IGameDef + public IGameDef { public: /* @@ -134,7 +135,6 @@ public: ~Server(); DISABLE_CLASS_COPY(Server); - void init(); void start(); void stop(); // This is mainly a way to pass the time to the server. @@ -196,12 +196,6 @@ public: */ void onMapEditEvent(const MapEditEvent &event); - /* - Shall be called with the environment and the connection locked. - */ - Inventory* getInventory(const InventoryLocation &loc); - void setInventoryModified(const InventoryLocation &loc); - // Connection must be locked when called std::wstring getStatusString(); inline double getUptime() const { return m_uptime_counter->get(); } @@ -253,10 +247,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); - // Creates or resets inventory - Inventory *createDetachedInventory(const std::string &name, - const std::string &player = ""); - bool removeDetachedInventory(const std::string &name); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } + void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); // Envlock and conlock should be locked when using scriptapi ServerScripting *getScriptIface(){ return m_script; } @@ -318,7 +310,7 @@ public: void setClouds(RemotePlayer *player, const CloudParams ¶ms); - bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); + void overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); @@ -389,6 +381,8 @@ private: float m_timer = 0.0f; }; + void init(); + void SendMovement(session_t peer_id); void SendHP(session_t peer_id, u16 hp); void SendBreath(session_t peer_id, u16 breath); @@ -457,8 +451,6 @@ private: void sendRequestedMedia(session_t peer_id, const std::vector &tosend); - void sendDetachedInventory(const std::string &name, session_t peer_id); - // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, u16 amount, float spawntime, @@ -656,14 +648,6 @@ private: s32 m_next_sound_id = 0; // positive values only s32 nextSoundId(); - /* - Detached inventories (behind m_env_mutex) - */ - // key = name - std::map m_detached_inventories; - // value = "" (visible to all players) or player name - std::map m_detached_inventories_player; - std::unordered_map m_mod_storages; float m_mod_storage_save_timer = 10.0f; @@ -674,6 +658,9 @@ private: // ModChannel manager std::unique_ptr m_modchannel_mgr; + // Inventory manager + std::unique_ptr m_inventory_mgr; + // Global server metrics backend std::unique_ptr m_metrics_backend; diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4d94504f6..0a5a8f3a7 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -4,5 +4,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serverinventorymgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/serverinventorymgr.cpp b/src/server/serverinventorymgr.cpp new file mode 100644 index 000000000..555e01ec6 --- /dev/null +++ b/src/server/serverinventorymgr.cpp @@ -0,0 +1,192 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +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 "serverinventorymgr.h" +#include "map.h" +#include "nodemetadata.h" +#include "player_sao.h" +#include "remoteplayer.h" +#include "server.h" +#include "serverenvironment.h" + +ServerInventoryManager::ServerInventoryManager() : InventoryManager() +{ +} + +ServerInventoryManager::~ServerInventoryManager() +{ + // Delete detached inventories + for (auto &detached_inventory : m_detached_inventories) { + delete detached_inventory.second.inventory; + } +} + +Inventory *ServerInventoryManager::getInventory(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + case InventoryLocation::PLAYER: { + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + if (!player) + return NULL; + PlayerSAO *playersao = player->getPlayerSAO(); + if (!playersao) + return NULL; + return playersao->getInventory(); + } break; + case InventoryLocation::NODEMETA: { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if (!meta) + return NULL; + return meta->getInventory(); + } break; + case InventoryLocation::DETACHED: { + auto it = m_detached_inventories.find(loc.name); + if (it == m_detached_inventories.end()) + return nullptr; + return it->second.inventory; + } break; + default: + sanity_check(false); // abort + break; + } + return NULL; +} + +void ServerInventoryManager::setInventoryModified(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + break; + case InventoryLocation::PLAYER: { + + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + + if (!player) + return; + + player->setModified(true); + player->inventory.setModified(true); + // Updates are sent in ServerEnvironment::step() + } break; + case InventoryLocation::NODEMETA: { + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = loc.p; + m_env->getMap().dispatchEvent(event); + } break; + case InventoryLocation::DETACHED: { + // Updates are sent in ServerEnvironment::step() + } break; + default: + sanity_check(false); // abort + break; + } +} + +Inventory *ServerInventoryManager::createDetachedInventory( + const std::string &name, IItemDefManager *idef, const std::string &player) +{ + if (m_detached_inventories.count(name) > 0) { + infostream << "Server clearing detached inventory \"" << name << "\"" + << std::endl; + delete m_detached_inventories[name].inventory; + } else { + infostream << "Server creating detached inventory \"" << name << "\"" + << std::endl; + } + + Inventory *inv = new Inventory(idef); + sanity_check(inv); + m_detached_inventories[name].inventory = inv; + if (!player.empty()) { + m_detached_inventories[name].owner = player; + + if (!m_env) + return inv; // Mods are not loaded yet, ignore + + RemotePlayer *p = m_env->getPlayer(name.c_str()); + + // if player is connected, send him the inventory + if (p && p->getPeerId() != PEER_ID_INEXISTENT) { + m_env->getGameDef()->sendDetachedInventory( + inv, name, p->getPeerId()); + } + } else { + if (!m_env) + return inv; // Mods are not loaded yet, don't send + + // Inventory is for everybody, broadcast + m_env->getGameDef()->sendDetachedInventory(inv, name, PEER_ID_INEXISTENT); + } + + return inv; +} + +bool ServerInventoryManager::removeDetachedInventory(const std::string &name) +{ + const auto &inv_it = m_detached_inventories.find(name); + if (inv_it == m_detached_inventories.end()) + return false; + + delete inv_it->second.inventory; + const std::string &owner = inv_it->second.owner; + + if (!owner.empty()) { + RemotePlayer *player = m_env->getPlayer(owner.c_str()); + + if (player && player->getPeerId() != PEER_ID_INEXISTENT) + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, player->getPeerId()); + + } else { + // Notify all players about the change + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, PEER_ID_INEXISTENT); + } + + m_detached_inventories.erase(inv_it); + + return true; +} + +void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name, + bool incremental, + std::function apply_cb) +{ + for (const auto &detached_inventory : m_detached_inventories) { + const DetachedInventory &dinv = detached_inventory.second; + if (incremental) { + if (!dinv.inventory || !dinv.inventory->checkModified()) + continue; + } + + // if we are pushing inventories to a specific player + // we should filter to send only the right inventories + if (!peer_name.empty()) { + const std::string &attached_player = dinv.owner; + if (!attached_player.empty() && peer_name != attached_player) + continue; + } + + apply_cb(detached_inventory.first, detached_inventory.second.inventory); + } +} diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h new file mode 100644 index 000000000..d0aac4dae --- /dev/null +++ b/src/server/serverinventorymgr.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +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 "inventorymanager.h" +#include + +class ServerEnvironment; + +class ServerInventoryManager : public InventoryManager +{ +public: + ServerInventoryManager(); + virtual ~ServerInventoryManager(); + + void setEnv(ServerEnvironment *env) + { + assert(!m_env); + m_env = env; + } + + Inventory *getInventory(const InventoryLocation &loc); + void setInventoryModified(const InventoryLocation &loc); + + // Creates or resets inventory + Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, + const std::string &player = ""); + bool removeDetachedInventory(const std::string &name); + + void sendDetachedInventories(const std::string &peer_name, bool incremental, + std::function apply_cb); + +private: + struct DetachedInventory + { + Inventory *inventory; + std::string owner; + }; + + ServerEnvironment *m_env = nullptr; + + std::unordered_map m_detached_inventories; +}; \ No newline at end of file -- cgit v1.2.3 From 7cb53791c3a6547164a9f8eae10ae8a2e2ddecdb Mon Sep 17 00:00:00 2001 From: TheTermos <55103816+TheTermos@users.noreply.github.com> Date: Sat, 9 May 2020 17:14:56 +0200 Subject: Color gradient for default and 'regular' type sky (#9502) * add regular sky gradient * add regular sky gradient * Update sky.cpp * change default day sky colors --- src/client/sky.cpp | 33 ++++----------------------------- src/skyparams.h | 4 ++-- 2 files changed, 6 insertions(+), 31 deletions(-) (limited to 'src/client') diff --git a/src/client/sky.cpp b/src/client/sky.cpp index ce33b96ae..d21b56fcc 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -252,35 +252,10 @@ void Sky::render() if (m_visible) { driver->setMaterial(m_materials[1]); for (u32 j = 0; j < 4; j++) { - video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45); - vertices[0] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.12, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.12, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - if (j == 0) - // Don't switch - {} - else if (j == 1) - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - else if (j == 2) - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - else - // Switch from -Z (south) to +Z (north) - vertex.Pos.rotateXZBy(-180); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } - - // Draw far cloudy fog thing at and below all horizons - for (u32 j = 0; j < 4; j++) { - video::SColor c = cloudyfogcolor; - vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, o); + vertices[0] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, m_bgcolor, t, t); + vertices[1] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, m_bgcolor, o, t); + vertices[2] = video::S3DVertex( 1, 0.45, -1, 0, 0, 1, m_skycolor, o, o); + vertices[3] = video::S3DVertex(-1, 0.45, -1, 0, 0, 1, m_skycolor, t, o); for (video::S3DVertex &vertex : vertices) { if (j == 0) // Don't switch diff --git a/src/skyparams.h b/src/skyparams.h index c362ef8f3..1de494d69 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -76,12 +76,12 @@ public: { SkyColor sky; // Horizon colors - sky.day_horizon = video::SColor(255, 155, 193, 240); + sky.day_horizon = video::SColor(255, 144, 211, 246); sky.indoors = video::SColor(255, 100, 100, 100); sky.dawn_horizon = video::SColor(255, 186, 193, 240); sky.night_horizon = video::SColor(255, 64, 144, 255); // Sky colors - sky.day_sky = video::SColor(255, 140, 186, 250); + sky.day_sky = video::SColor(255, 97, 181, 245); sky.dawn_sky = video::SColor(255, 180, 186, 250); sky.night_sky = video::SColor(255, 0, 107, 255); return sky; -- cgit v1.2.3 From 6e1372bd894d955300c40d69e5c882e9cc7d7523 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 11 May 2020 21:40:45 +0200 Subject: Add support for statbar “off state” icons (#9462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for optional “off state” icons for statbars. “off state icons” can be used to denote the lack of something, like missing hearts or bubbles. Add "off state" textures to the builtin statbars. Co-authored-by: SmallJoker --- builtin/game/statbars.lua | 4 ++ doc/lua_api.txt | 16 ++++-- doc/texture_packs.txt | 4 ++ src/client/clientevent.h | 1 + src/client/game.cpp | 7 +++ src/client/hud.cpp | 96 ++++++++++++++++++++++++++++++------ src/client/hud.h | 5 +- src/hud.cpp | 1 + src/hud.h | 2 + src/network/clientpackethandler.cpp | 15 ++---- src/network/networkprotocol.h | 6 ++- src/script/common/c_content.cpp | 8 +++ src/server.cpp | 3 +- textures/base/pack/bubble_gone.png | Bin 0 -> 68 bytes textures/base/pack/heart_gone.png | Bin 0 -> 68 bytes 15 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 textures/base/pack/bubble_gone.png create mode 100644 textures/base/pack/heart_gone.png (limited to 'src/client') diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 6b5b54428..d192029c5 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -5,7 +5,9 @@ local health_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "heart.png", + text2 = "heart_gone.png", number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, direction = 0, size = {x = 24, y = 24}, offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, @@ -15,7 +17,9 @@ local breath_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "bubble.png", + text2 = "bubble_gone.png", number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, direction = 0, size = {x = 24, y = 24}, offset = {x = 25, y= -(48 + 24 + 16)}, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 961e1ff37..4078e21a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1289,9 +1289,9 @@ To account for differing resolutions, the position coordinates are the percentage of the screen, ranging in value from `0` to `1`. The name field is not yet used, but should contain a description of what the -HUD element represents. The direction field is the direction in which something -is drawn. +HUD element represents. +The `direction` field is the direction in which something is drawn. `0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom, and `3` draws from bottom to top. @@ -1355,12 +1355,16 @@ Displays text on the HUD. ### `statbar` -Displays a horizontal bar made up of half-images. +Displays a horizontal bar made up of half-images with an optional background. -* `text`: The name of the texture that is used. +* `text`: The name of the texture to use. +* `text2`: Optional texture name to enable a background / "off state" + texture (useful to visualize the maximal value). Both textures + must have the same size. * `number`: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. -* `direction` +* `item`: Same as `number` but for the "off state" texture +* `direction`: To which direction the images will extend to * `offset`: offset in pixels from position. * `size`: If used, will force full-image size to this value (override texture pack image size) @@ -7772,6 +7776,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. text = "", + text2 = "", + number = 2, item = 3, diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 4e7bc93c4..94151f1a4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -64,6 +64,8 @@ by texture packs. All existing fallback textures can be found in the directory * `bubble.png`: the bubble texture when the player is drowning (default size: 12×12) +* `bubble_gone.png`: like `bubble.png`, but denotes lack of breath + (transparent by default, same size as bubble.png) * `crack_anylength.png`: node overlay texture when digging @@ -76,6 +78,8 @@ by texture packs. All existing fallback textures can be found in the directory * `heart.png`: used to display the health points of the player (default size: 12×12) +* `heart_gone.png`: like `heart.png`, but denotes lack of health points + (transparent by default, same size as heart.png) * `minimap_mask_round.png`: round minimap mask, white gets replaced by the map * `minimap_mask_square.png`: mask used for the square minimap diff --git a/src/client/clientevent.h b/src/client/clientevent.h index f5689c25b..7f3984b03 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -136,6 +136,7 @@ struct ClientEvent v3f *world_pos; v2s32 *size; s16 z_index; + std::string *text2; } hudadd; struct { diff --git a/src/client/game.cpp b/src/client/game.cpp index 4d7a85526..422e17d4f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2672,6 +2672,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; return; } @@ -2689,6 +2690,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->world_pos = *event->hudadd.world_pos; e->size = *event->hudadd.size; e->z_index = event->hudadd.z_index; + e->text2 = *event->hudadd.text2; hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; @@ -2699,6 +2701,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; } void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) @@ -2771,6 +2774,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca case HUD_STAT_Z_INDEX: e->z_index = event->hudchange.data; break; + + case HUD_STAT_TEXT2: + e->text2 = *event->hudchange.sdata; + break; } delete event->hudchange.v3fdata; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 56763e7e4..f8f712762 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -332,7 +332,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); - drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size); + drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2, + e->number, e->item, offs, e->size); break; } case HUD_ELEM_INVENTORY: { InventoryList *inv = inventory->getList(e->text); @@ -401,8 +402,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } -void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size) +void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string &bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size) { const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; @@ -411,6 +413,11 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex if (!stat_texture) return; + video::ITexture *stat_texture_bg = nullptr; + if (!bgtexture.empty()) { + stat_texture_bg = tsrc->getTexture(bgtexture); + } + core::dimension2di srcd(stat_texture->getOriginalSize()); core::dimension2di dstd; if (size == v2s32()) { @@ -430,43 +437,100 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex p += offset; v2s32 steppos; - core::rect srchalfrect, dsthalfrect; switch (drawdir) { case HUD_DIR_RIGHT_LEFT: steppos = v2s32(-1, 0); - srchalfrect = core::rect(srcd.Width / 2, 0, srcd.Width, srcd.Height); - dsthalfrect = core::rect(dstd.Width / 2, 0, dstd.Width, dstd.Height); break; case HUD_DIR_TOP_BOTTOM: steppos = v2s32(0, 1); - srchalfrect = core::rect(0, 0, srcd.Width, srcd.Height / 2); - dsthalfrect = core::rect(0, 0, dstd.Width, dstd.Height / 2); break; case HUD_DIR_BOTTOM_TOP: steppos = v2s32(0, -1); - srchalfrect = core::rect(0, srcd.Height / 2, srcd.Width, srcd.Height); - dsthalfrect = core::rect(0, dstd.Height / 2, dstd.Width, dstd.Height); break; default: + // From left to right steppos = v2s32(1, 0); - srchalfrect = core::rect(0, 0, srcd.Width / 2, srcd.Height); - dsthalfrect = core::rect(0, 0, dstd.Width / 2, dstd.Height); + break; + } + + auto calculate_clipping_rect = [] (core::dimension2di src, + v2s32 steppos) -> core::rect { + + // Create basic rectangle + core::rect rect(0, 0, + src.Width - std::abs(steppos.X) * src.Width / 2, + src.Height - std::abs(steppos.Y) * src.Height / 2 + ); + // Move rectangle left or down + if (steppos.X == -1) + rect += v2s32(src.Width / 2, 0); + if (steppos.Y == -1) + rect += v2s32(0, src.Height / 2); + return rect; + }; + // Rectangles for 1/2 the actual value to display + core::rect srchalfrect, dsthalfrect; + // Rectangles for 1/2 the "off state" texture + core::rect srchalfrect2, dsthalfrect2; + + if (count % 2 == 1) { + // Need to draw halves: Calculate rectangles + srchalfrect = calculate_clipping_rect(srcd, steppos); + dsthalfrect = calculate_clipping_rect(dstd, steppos); + srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1); + dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1); } + steppos.X *= dstd.Width; steppos.Y *= dstd.Height; + // Draw full textures for (s32 i = 0; i < count / 2; i++) { core::rect srcrect(0, 0, srcd.Width, srcd.Height); - core::rect dstrect(0,0, dstd.Width, dstd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); dstrect += p; - draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true); + draw2DImageFilterScaled(driver, stat_texture, + dstrect, srcrect, NULL, colors, true); p += steppos; } if (count % 2 == 1) { - dsthalfrect += p; - draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true); + // Draw half a texture + draw2DImageFilterScaled(driver, stat_texture, + dsthalfrect + p, srchalfrect, NULL, colors, true); + + if (stat_texture_bg && maxcount > count) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect2 + p, srchalfrect2, + NULL, colors, true); + p += steppos; + } + } + + if (stat_texture_bg && maxcount > count / 2) { + // Draw "off state" textures + s32 start_offset; + if (count % 2 == 1) + start_offset = count / 2 + 1; + else + start_offset = count / 2; + for (s32 i = start_offset; i < maxcount / 2; i++) { + core::rect srcrect(0, 0, srcd.Width, srcd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); + + dstrect += p; + draw2DImageFilterScaled(driver, stat_texture_bg, + dstrect, srcrect, + NULL, colors, true); + p += steppos; + } + + if (maxcount % 2 == 1) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect + p, srchalfrect, + NULL, colors, true); + } } } diff --git a/src/client/hud.h b/src/client/hud.h index cab115990..6274b1a83 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -82,8 +82,9 @@ public: private: bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); - void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size = v2s32()); + void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string& bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, s32 inv_offset, InventoryList *mainlist, u16 selectitem, diff --git a/src/hud.cpp b/src/hud.cpp index 39625b5fd..3079b5cd8 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -46,6 +46,7 @@ const struct EnumString es_HudElementStat[] = {HUD_STAT_WORLD_POS, "world_pos"}, {HUD_STAT_SIZE, "size"}, {HUD_STAT_Z_INDEX, "z_index"}, + {HUD_STAT_TEXT2, "text2"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index b0977c6a4..bab420ed2 100644 --- a/src/hud.h +++ b/src/hud.h @@ -77,6 +77,7 @@ enum HudElementStat { HUD_STAT_WORLD_POS, HUD_STAT_SIZE, HUD_STAT_Z_INDEX, + HUD_STAT_TEXT2, }; struct HudElement { @@ -93,6 +94,7 @@ struct HudElement { v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; }; extern const EnumString es_HudElementType[]; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8d0225a3d..7b1b1368c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1102,22 +1102,16 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item >> dir >> align >> offset; try { *pkt >> world_pos; - } - catch(SerializationError &e) {}; - - try { *pkt >> size; - } catch(SerializationError &e) {}; - - try { *pkt >> z_index; - } - catch(PacketError &e) {} + *pkt >> text2; + } catch(PacketError &e) {}; ClientEvent *event = new ClientEvent(); event->type = CE_HUDADD; @@ -1135,6 +1129,7 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) event->hudadd.world_pos = new v3f(world_pos); event->hudadd.size = new v2s32(size); event->hudadd.z_index = z_index; + event->hudadd.text2 = new std::string(text2); m_client_event_queue.push(event); } @@ -1171,7 +1166,7 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE || stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET) *pkt >> v2fdata; - else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) + else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2) *pkt >> sdata; else if (stat == HUD_STAT_WORLD_POS) *pkt >> v3fdata; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 527ebba7c..ab924f1db 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -560,10 +560,10 @@ enum ToClientCommand u32 id u8 type v2f1000 pos - u32 len + u16 len u8[len] name v2f1000 scale - u32 len2 + u16 len2 u8[len2] text u32 number u32 item @@ -573,6 +573,8 @@ enum ToClientCommand v3f1000 world_pos v2s32 size s16 z_index + u16 len3 + u8[len3] text2 */ TOCLIENT_HUDRM = 0x4a, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index dac828316..540b7222f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1871,6 +1871,7 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); + elem->text2 = getstringfield_default(L, 2, "text2", ""); // Deprecated, only for compatibility's sake if (elem->dir == 0) @@ -1939,6 +1940,9 @@ void push_hud_element(lua_State *L, HudElement *elem) lua_pushnumber(L, elem->z_index); lua_setfield(L, -2, "z_index"); + + lua_pushstring(L, elem->text2.c_str()); + lua_setfield(L, -2, "text2"); } HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) @@ -2000,6 +2004,10 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, luaL_checknumber(L, 4))); *value = &elem->z_index; break; + case HUD_STAT_TEXT2: + elem->text2 = luaL_checkstring(L, 4); + *value = &elem->text2; + break; } return stat; } diff --git a/src/server.cpp b/src/server.cpp index 16e026ce2..b28c30e1e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1621,7 +1621,7 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir << form->align << form->offset << form->world_pos << form->size - << form->z_index; + << form->z_index << form->text2; Send(&pkt); } @@ -1647,6 +1647,7 @@ void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void break; case HUD_STAT_NAME: case HUD_STAT_TEXT: + case HUD_STAT_TEXT2: pkt << *(std::string *) value; break; case HUD_STAT_WORLD_POS: diff --git a/textures/base/pack/bubble_gone.png b/textures/base/pack/bubble_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/bubble_gone.png differ diff --git a/textures/base/pack/heart_gone.png b/textures/base/pack/heart_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/heart_gone.png differ -- cgit v1.2.3 From 9ba24f89f5846de6a8f0d7e01c89acfee1254704 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Mon, 11 May 2020 21:41:36 +0200 Subject: Damage texture modifier (#9833) Adds a new object property "damage_texture_modifier" --- doc/lua_api.txt | 3 +++ src/client/content_cao.cpp | 14 +++++--------- src/object_properties.cpp | 5 +++++ src/object_properties.h | 1 + src/script/common/c_content.cpp | 4 ++++ 5 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4078e21a1..db13f4224 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6589,6 +6589,9 @@ Player properties need to be saved manually. -- deleted when the block gets unloaded. -- The get_staticdata() callback is never called then. -- Defaults to 'true'. + + damage_texture_modifier = "^[brighten", + -- Texture modifier to be applied for a short duration when object is hit } Entity definition diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index aadd33bb9..eb1dad22b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1532,7 +1532,7 @@ void GenericCAO::processMessage(const std::string &data) } else if (cmd == AO_CMD_SET_TEXTURE_MOD) { std::string mod = deSerializeString(is); - // immediatly reset a engine issued texture modifier if a mod sends a different one + // immediately reset a engine issued texture modifier if a mod sends a different one if (m_reset_textures_timer > 0) { m_reset_textures_timer = -1; updateTextures(m_previous_texture_modifier); @@ -1646,13 +1646,11 @@ void GenericCAO::processMessage(const std::string &data) m_smgr, m_env, m_position, v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); m_env->addSimpleObject(simple); - } else if (m_reset_textures_timer < 0) { - // TODO: Execute defined fast response - // Flashing shall suffice as there is no definition + } else if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) { m_reset_textures_timer = 0.05; if(damage >= 2) m_reset_textures_timer += 0.05 * damage; - updateTextures(m_current_texture_modifier + "^[brighten"); + updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier); } } @@ -1723,13 +1721,11 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); m_env->addSimpleObject(simple); } - // TODO: Execute defined fast response - // Flashing shall suffice as there is no definition - if (m_reset_textures_timer < 0) { + if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) { m_reset_textures_timer = 0.05; if (result.damage >= 2) m_reset_textures_timer += 0.05 * result.damage; - updateTextures(m_current_texture_modifier + "^[brighten"); + updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier); } } diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 4cf180b18..6ff344dce 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -68,6 +68,7 @@ std::string ObjectProperties::dump() os << ", eye_height=" << eye_height; os << ", zoom_fov=" << zoom_fov; os << ", use_texture_alpha=" << use_texture_alpha; + os << ", damage_texture_modifier=" << damage_texture_modifier; return os.str(); } @@ -114,6 +115,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeF32(os, eye_height); writeF32(os, zoom_fov); writeU8(os, use_texture_alpha); + os << serializeString(damage_texture_modifier); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -166,4 +168,7 @@ void ObjectProperties::deSerialize(std::istream &is) eye_height = readF32(is); zoom_fov = readF32(is); use_texture_alpha = readU8(is); + try { + damage_texture_modifier = deSerializeString(is); + } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index 3895f3379..f7848f5a2 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -39,6 +39,7 @@ struct ObjectProperties std::string mesh = ""; v3f visual_size = v3f(1, 1, 1); std::vector textures; + std::string damage_texture_modifier = "^[brighten"; std::vector colors; v2s16 spritediv = v2s16(1, 1); v2s16 initial_sprite_basepos; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 540b7222f..de9634c42 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -327,6 +327,8 @@ 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); + + getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } /******************************************************************************/ @@ -409,6 +411,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "zoom_fov"); lua_pushboolean(L, prop->use_texture_alpha); lua_setfield(L, -2, "use_texture_alpha"); + lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size()); + lua_setfield(L, -2, "damage_texture_modifier"); } /******************************************************************************/ -- cgit v1.2.3 From 836dd4a1e4f97411519578cd9e59b6dbe3b2c00d Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 14 May 2020 19:26:15 +0200 Subject: Add chat_log_level setting (#9223) Log all higher levels in LogOutputBuffer Move StreamLogOutput::logRaw to source file like LogOutputBuffer::logRaw for compiling speed --- builtin/settingtypes.txt | 3 ++ src/client/game.cpp | 16 ++++------- src/defaultsettings.cpp | 1 + src/log.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ src/log.h | 52 ++++++++-------------------------- 5 files changed, 95 insertions(+), 51 deletions(-) (limited to 'src/client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 9e4473655..b75bf2de5 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1401,6 +1401,9 @@ debug_log_level (Debug log level) enum action ,none,error,warning,action,info,ve # debug.txt is only moved if this setting is positive. debug_log_size_max (Debug log file size threshold) int 50 +# Minimal level of logging to be written to chat. +chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose + # Enable IPv6 support (for both client and server). # Required for IPv6 connections to work at all. enable_ipv6 (IPv6) bool true diff --git a/src/client/game.cpp b/src/client/game.cpp index 422e17d4f..e7663a113 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -855,6 +855,7 @@ private: SoundMaker *soundmaker = nullptr; ChatBackend *chat_backend = nullptr; + LogOutputBuffer m_chat_log_buf; EventManager *eventmgr = nullptr; QuicktuneShortcutter *quicktune = nullptr; @@ -926,6 +927,7 @@ private: }; Game::Game() : + m_chat_log_buf(g_logger), m_game_ui(new GameUI()) { g_settings->registerChangedCallback("doubletap_jump", @@ -1192,6 +1194,7 @@ void Game::shutdown() chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); + m_chat_log_buf.clear(); if (client) { client->Stop(); @@ -2903,18 +2906,9 @@ void Game::processClientEvents(CameraOrientation *cam) void Game::updateChat(f32 dtime, const v2u32 &screensize) { - // Add chat log output for errors to be shown in chat - static LogOutputBuffer chat_log_error_buf(g_logger, LL_ERROR); - // Get new messages from error log buffer - while (!chat_log_error_buf.empty()) { - std::wstring error_message = utf8_to_wide(chat_log_error_buf.get()); - if (!g_settings->getBool("disable_escape_sequences")) { - error_message.insert(0, L"\x1b(c@red)"); - error_message.append(L"\x1b(c@white)"); - } - chat_backend->addMessage(L"", error_message); - } + while (!m_chat_log_buf.empty()) + chat_backend->addMessage(L"", utf8_to_wide(m_chat_log_buf.get())); // Get new messages from client std::wstring message; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 33654e213..1d0610c0f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -400,6 +400,7 @@ void set_default_settings(Settings *settings) 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"); diff --git a/src/log.cpp b/src/log.cpp index 30344b4df..54442c39b 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "gettime.h" #include "porting.h" +#include "settings.h" #include "config.h" #include "exceptions.h" #include "util/numeric.h" @@ -338,7 +339,80 @@ void FileLogOutput::setFile(const std::string &filename, s64 file_size_max) "-------------\n" << std::endl; } +void StreamLogOutput::logRaw(LogLevel lev, const std::string &line) +{ + bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) || + (Logger::color_mode == LOG_COLOR_AUTO && is_tty); + if (colored_message) { + switch (lev) { + case LL_ERROR: + // error is red + m_stream << "\033[91m"; + break; + case LL_WARNING: + // warning is yellow + m_stream << "\033[93m"; + break; + case LL_INFO: + // info is a bit dark + m_stream << "\033[37m"; + break; + case LL_VERBOSE: + // verbose is darker than info + m_stream << "\033[2m"; + break; + default: + // action is white + colored_message = false; + } + } + m_stream << line << std::endl; + + if (colored_message) { + // reset to white color + m_stream << "\033[0m"; + } +} + +void LogOutputBuffer::updateLogLevel() +{ + const std::string &conf_loglev = g_settings->get("chat_log_level"); + LogLevel log_level = Logger::stringToLevel(conf_loglev); + if (log_level == LL_MAX) { + warningstream << "Supplied unrecognized chat_log_level; " + "showing none." << std::endl; + log_level = LL_NONE; + } + + m_logger.removeOutput(this); + m_logger.addOutputMaxLevel(this, log_level); +} + +void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line) +{ + std::string color; + + if (!g_settings->getBool("disable_escape_sequences")) { + switch (lev) { + case LL_ERROR: // red + color = "\x1b(c@#F00)"; + break; + case LL_WARNING: // yellow + color = "\x1b(c@#EE0)"; + break; + case LL_INFO: // grey + color = "\x1b(c@#BBB)"; + break; + case LL_VERBOSE: // dark grey + color = "\x1b(c@#888)"; + break; + default: break; + } + } + + m_buffer.push(color.append(line)); +} //// //// *Buffer methods diff --git a/src/log.h b/src/log.h index 6350d8a86..856d3479b 100644 --- a/src/log.h +++ b/src/log.h @@ -124,39 +124,7 @@ public: #endif } - void logRaw(LogLevel lev, const std::string &line) - { - bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) || - (Logger::color_mode == LOG_COLOR_AUTO && is_tty); - if (colored_message) - switch (lev) { - case LL_ERROR: - // error is red - m_stream << "\033[91m"; - break; - case LL_WARNING: - // warning is yellow - m_stream << "\033[93m"; - break; - case LL_INFO: - // info is a bit dark - m_stream << "\033[37m"; - break; - case LL_VERBOSE: - // verbose is darker than info - m_stream << "\033[2m"; - break; - default: - // action is white - colored_message = false; - } - - m_stream << line << std::endl; - - if (colored_message) - // reset to white color - m_stream << "\033[0m"; - } + void logRaw(LogLevel lev, const std::string &line); private: std::ostream &m_stream; @@ -178,23 +146,27 @@ private: class LogOutputBuffer : public ICombinedLogOutput { public: - LogOutputBuffer(Logger &logger, LogLevel lev) : + LogOutputBuffer(Logger &logger) : m_logger(logger) { - m_logger.addOutput(this, lev); - } + updateLogLevel(); + }; - ~LogOutputBuffer() + virtual ~LogOutputBuffer() { m_logger.removeOutput(this); } - void logRaw(LogLevel lev, const std::string &line) + void updateLogLevel(); + + void logRaw(LogLevel lev, const std::string &line); + + void clear() { - m_buffer.push(line); + m_buffer = std::queue(); } - bool empty() + bool empty() const { return m_buffer.empty(); } -- cgit v1.2.3 From 36d35f2fe31a429c1510df680801940472416d45 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 14 May 2020 21:16:45 +0200 Subject: CSM: Bugfixes to camera:get_pos() and camera:get_fov() closes #9857 --- clientmods/preview/init.lua | 2 +- doc/client_lua_api.txt | 2 +- src/client/camera.cpp | 2 +- src/script/lua_api/l_camera.cpp | 59 +++++++++++++++++++++++++---------------- src/script/lua_api/l_env.cpp | 16 +++++------ 5 files changed, 47 insertions(+), 34 deletions(-) (limited to 'src/client') diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index d2440369a..089955d2f 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -79,7 +79,7 @@ core.register_on_item_use(function(itemstack, pointed_thing) return false end - local pos = vector.add(core.localplayer:get_pos(), core.camera:get_offset()) + local pos = core.camera:get_pos() local pos2 = vector.add(pos, vector.multiply(core.camera:get_look_dir(), 100)) local rc = core.raycast(pos, pos2) diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 53442d308..c9cd8ac93 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -967,7 +967,7 @@ Please do not try to access the reference until the camera is initialized, other * `get_camera_mode()` * Returns 0, 1, or 2 as described above * `get_fov()` - * Returns: + * Returns a table with X, Y, maximum and actual FOV in degrees: ```lua { diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 1a5253db4..9b311171a 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -540,7 +540,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_aspect = (f32) window_size.X / (f32) window_size.Y; m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) - m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); + m_fov_y *= core::clamp(sqrt(16./10. / m_aspect), 1.0, 1.4); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index 9c1470284..bfa60be67 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -51,6 +51,7 @@ void LuaCamera::create(lua_State *L, Camera *m) lua_setfield(L, objectstable, "camera"); } +// set_camera_mode(self, mode) int LuaCamera::l_set_camera_mode(lua_State *L) { Camera *camera = getobject(L, 1); @@ -67,17 +68,19 @@ int LuaCamera::l_set_camera_mode(lua_State *L) return 0; } +// get_camera_mode(self) int LuaCamera::l_get_camera_mode(lua_State *L) { Camera *camera = getobject(L, 1); if (!camera) return 0; - lua_pushnumber(L, (int)camera->getCameraMode()); + lua_pushinteger(L, (int)camera->getCameraMode()); return 1; } +// get_fov(self) int LuaCamera::l_get_fov(lua_State *L) { Camera *camera = getobject(L, 1); @@ -85,9 +88,9 @@ int LuaCamera::l_get_fov(lua_State *L) return 0; lua_newtable(L); - lua_pushnumber(L, camera->getFovX() * core::DEGTORAD); + lua_pushnumber(L, camera->getFovX() * core::RADTODEG); lua_setfield(L, -2, "x"); - lua_pushnumber(L, camera->getFovY() * core::DEGTORAD); + lua_pushnumber(L, camera->getFovY() * core::RADTODEG); lua_setfield(L, -2, "y"); lua_pushnumber(L, camera->getCameraNode()->getFOV() * core::RADTODEG); lua_setfield(L, -2, "actual"); @@ -96,16 +99,18 @@ int LuaCamera::l_get_fov(lua_State *L) return 1; } +// get_pos(self) int LuaCamera::l_get_pos(lua_State *L) { Camera *camera = getobject(L, 1); if (!camera) return 0; - push_v3f(L, camera->getPosition()); + push_v3f(L, camera->getPosition() / BS); return 1; } +// get_offset(self) int LuaCamera::l_get_offset(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); @@ -115,38 +120,40 @@ int LuaCamera::l_get_offset(lua_State *L) return 1; } +// get_look_dir(self) int LuaCamera::l_get_look_dir(lua_State *L) { - LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); - sanity_check(player); - - float pitch = -1.0 * player->getPitch() * core::DEGTORAD; - float yaw = (player->getYaw() + 90.) * core::DEGTORAD; - v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), - std::cos(pitch) * std::sin(yaw)); + Camera *camera = getobject(L, 1); + if (!camera) + return 0; - push_v3f(L, v); + push_v3f(L, camera->getDirection()); return 1; } +// get_look_horizontal(self) +// FIXME: wouldn't localplayer be a better place for this? int LuaCamera::l_get_look_horizontal(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); sanity_check(player); - lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD); + lua_pushnumber(L, (player->getYaw() + 90.f) * core::DEGTORAD); return 1; } +// get_look_vertical(self) +// FIXME: wouldn't localplayer be a better place for this? int LuaCamera::l_get_look_vertical(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); sanity_check(player); - lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD); + lua_pushnumber(L, -1.0f * player->getPitch() * core::DEGTORAD); return 1; } +// get_aspect_ratio(self) int LuaCamera::l_get_aspect_ratio(lua_State *L) { Camera *camera = getobject(L, 1); @@ -215,13 +222,19 @@ void LuaCamera::Register(lua_State *L) lua_pop(L, 1); } +// clang-format off const char LuaCamera::className[] = "Camera"; -const luaL_Reg LuaCamera::methods[] = {luamethod(LuaCamera, set_camera_mode), - luamethod(LuaCamera, get_camera_mode), luamethod(LuaCamera, get_fov), - luamethod(LuaCamera, get_pos), luamethod(LuaCamera, get_offset), - luamethod(LuaCamera, get_look_dir), - luamethod(LuaCamera, get_look_vertical), - luamethod(LuaCamera, get_look_horizontal), - luamethod(LuaCamera, get_aspect_ratio), - - {0, 0}}; +const luaL_Reg LuaCamera::methods[] = { + luamethod(LuaCamera, set_camera_mode), + luamethod(LuaCamera, get_camera_mode), + luamethod(LuaCamera, get_fov), + luamethod(LuaCamera, get_pos), + luamethod(LuaCamera, get_offset), + luamethod(LuaCamera, get_look_dir), + luamethod(LuaCamera, get_look_vertical), + luamethod(LuaCamera, get_look_horizontal), + luamethod(LuaCamera, get_aspect_ratio), + + {0, 0} +}; +// clang-format on diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index cabca124d..b8a8a5ce1 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -780,8 +780,8 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) #ifndef SERVER // Client API limitations - if (getClient(L)) - radius = getClient(L)->CSMClampRadius(pos, radius); + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); #endif for (int d = start_radius; d <= radius; d++) { @@ -811,9 +811,9 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) const NodeDefManager *ndef = env->getGameDef()->ndef(); #ifndef SERVER - if (getClient(L)) { - minp = getClient(L)->CSMClampPos(minp); - maxp = getClient(L)->CSMClampPos(maxp); + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); } #endif @@ -887,9 +887,9 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) const NodeDefManager *ndef = env->getGameDef()->ndef(); #ifndef SERVER - if (getClient(L)) { - minp = getClient(L)->CSMClampPos(minp); - maxp = getClient(L)->CSMClampPos(maxp); + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); } #endif -- cgit v1.2.3 From c1ce4be756e2554051a27f244303377c0a7d69a6 Mon Sep 17 00:00:00 2001 From: ANAND Date: Sun, 17 May 2020 01:12:31 +0530 Subject: Make automatic_rotate relative, allow setting rotation (#8468) automatic_rotate does not make sense if it is absolute. Make it relative. To avoid bouncing, set_rotation did not update the client when automatic_rotate was set. That's no longer necessary because the new spinning method applies the rotation on top of the current one, and the updates are necessary for set_rotation to actually transform the object. Co-authored-by: ANAND Co-authored-by: Pedro Gimeno --- doc/lua_api.txt | 1 + src/client/content_cao.cpp | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9685e8307..07758c237 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6555,6 +6555,7 @@ Player properties need to be saved manually. automatic_rotate = 0, -- Set constant rotation in radians per second, positive or negative. + -- Object rotates along the local Y-axis, and works with set_rotation. -- Set to 0 to disable constant rotation. stepheight = 0, diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index eb1dad22b..867bbf2c8 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1077,10 +1077,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) updateTextures(m_previous_texture_modifier); } } + if (!getParent() && std::fabs(m_prop.automatic_rotate) > 0.001) { - m_rotation.Y += dtime * m_prop.automatic_rotate * 180 / M_PI; - rot_translator.val_current = m_rotation; - updateNodePos(); + // This is the child node's rotation. It is only used for automatic_rotate. + v3f local_rot = node->getRotation(); + local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG * + m_prop.automatic_rotate); + node->setRotation(local_rot); } if (!getParent() && m_prop.automatic_face_movement_dir && @@ -1501,11 +1504,7 @@ void GenericCAO::processMessage(const std::string &data) m_position = readV3F32(is); m_velocity = readV3F32(is); m_acceleration = readV3F32(is); - - if (std::fabs(m_prop.automatic_rotate) < 0.001f) - m_rotation = readV3F32(is); - else - readV3F32(is); + m_rotation = readV3F32(is); m_rotation = wrapDegrees_0_360_v3f(m_rotation); bool do_interpolate = readU8(is); -- cgit v1.2.3 From be38a44ffe2355d909774923a517d8b37f932dfe Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 15 May 2020 15:12:37 +0200 Subject: Client: Add sum and average to packetcounter --- src/client/client.cpp | 14 ++++++++++++-- src/client/client.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src/client') diff --git a/src/client/client.cpp b/src/client/client.cpp index 941fc203d..c03c062c6 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -64,6 +64,14 @@ extern gui::IGUIEnvironment* guienv; Utility classes */ +u32 PacketCounter::sum() const +{ + u32 n = 0; + for (const auto &it : m_packets) + n += it.second; + return n; +} + void PacketCounter::print(std::ostream &o) const { for (const auto &it : m_packets) { @@ -357,9 +365,11 @@ void Client::step(float dtime) if(counter <= 0.0f) { counter = 30.0f; + u32 sum = m_packetcounter.sum(); + float avg = sum / counter; - infostream << "Client packetcounter (" << m_packetcounter_timer - << "s):"< m_packets; + std::map m_packets; }; class ClientScripting; -- cgit v1.2.3 From 52430d34d33f0ae3ff188a009307bf0a86048dfe Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 15 May 2020 15:42:33 +0200 Subject: content_mapblock: Move static initialization out of functions --- src/client/content_mapblock.cpp | 54 +++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'src/client') diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 9b4fd221e..bf1b4c7d6 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -512,8 +512,7 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k) return 0; } -void MapblockMeshGenerator::drawLiquidSides() -{ +namespace { struct LiquidFaceDesc { v3s16 dir; // XZ v3s16 p[2]; // XZ only; 1 means +, 0 means - @@ -521,20 +520,23 @@ void MapblockMeshGenerator::drawLiquidSides() struct UV { int u, v; }; - static const LiquidFaceDesc base_faces[4] = { + static const LiquidFaceDesc liquid_base_faces[4] = { {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}}, {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}}, {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}}, {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}}, }; - static const UV base_vertices[4] = { + static const UV liquid_base_vertices[4] = { {0, 1}, {1, 1}, {1, 0}, {0, 0} }; +} - for (const auto &face : base_faces) { +void MapblockMeshGenerator::drawLiquidSides() +{ + for (const auto &face : liquid_base_faces) { const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1]; // No face between nodes of the same liquid, unless there is node @@ -554,7 +556,7 @@ void MapblockMeshGenerator::drawLiquidSides() video::S3DVertex vertices[4]; for (int j = 0; j < 4; j++) { - const UV &vertex = base_vertices[j]; + const UV &vertex = liquid_base_vertices[j]; const v3s16 &base = face.p[vertex.u]; float v = vertex.v; @@ -1193,15 +1195,14 @@ bool MapblockMeshGenerator::isSameRail(v3s16 dir) (def2.getGroup(raillike_groupname) == raillike_group)); } -void MapblockMeshGenerator::drawRaillikeNode() -{ - static const v3s16 direction[4] = { +namespace { + static const v3s16 rail_direction[4] = { v3s16( 0, 0, 1), v3s16( 0, 0, -1), v3s16(-1, 0, 0), v3s16( 1, 0, 0), }; - static const int slope_angle[4] = {0, 180, 90, -90}; + static const int rail_slope_angle[4] = {0, 180, 90, -90}; enum RailTile { straight, @@ -1214,8 +1215,8 @@ void MapblockMeshGenerator::drawRaillikeNode() int angle; }; static const RailDesc rail_kinds[16] = { - // +x -x -z +z - //------------- + // +x -x -z +z + //------------- {straight, 0}, // . . . . {straight, 0}, // . . . +Z {straight, 0}, // . . -Z . @@ -1233,7 +1234,10 @@ void MapblockMeshGenerator::drawRaillikeNode() {junction, 270}, // +X -X -Z . { cross, 0}, // +X -X -Z +Z }; +} +void MapblockMeshGenerator::drawRaillikeNode() +{ raillike_group = nodedef->get(n).getGroup(raillike_groupname); int code = 0; @@ -1241,14 +1245,14 @@ void MapblockMeshGenerator::drawRaillikeNode() int tile_index; bool sloped = false; for (int dir = 0; dir < 4; dir++) { - bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0)); + bool rail_above = isSameRail(rail_direction[dir] + v3s16(0, 1, 0)); if (rail_above) { sloped = true; - angle = slope_angle[dir]; + angle = rail_slope_angle[dir]; } if (rail_above || - isSameRail(direction[dir]) || - isSameRail(direction[dir] + v3s16(0, -1, 0))) + isSameRail(rail_direction[dir]) || + isSameRail(rail_direction[dir] + v3s16(0, -1, 0))) code |= 1 << dir; } @@ -1276,9 +1280,8 @@ void MapblockMeshGenerator::drawRaillikeNode() drawQuad(vertices); } -void MapblockMeshGenerator::drawNodeboxNode() -{ - static const v3s16 tile_dirs[6] = { +namespace { + static const v3s16 nodebox_tile_dirs[6] = { v3s16(0, 1, 0), v3s16(0, -1, 0), v3s16(1, 0, 0), @@ -1288,7 +1291,7 @@ void MapblockMeshGenerator::drawNodeboxNode() }; // we have this order for some reason... - static const v3s16 connection_dirs[6] = { + static const v3s16 nodebox_connection_dirs[6] = { v3s16( 0, 1, 0), // top v3s16( 0, -1, 0), // bottom v3s16( 0, 0, -1), // front @@ -1296,19 +1299,22 @@ void MapblockMeshGenerator::drawNodeboxNode() v3s16( 0, 0, 1), // back v3s16( 1, 0, 0), // right }; +} +void MapblockMeshGenerator::drawNodeboxNode() +{ TileSpec tiles[6]; for (int face = 0; face < 6; face++) { // Handles facedir rotation for textures - getTile(tile_dirs[face], &tiles[face]); + getTile(nodebox_tile_dirs[face], &tiles[face]); } // locate possible neighboring nodes to connect to - int neighbors_set = 0; + u8 neighbors_set = 0; if (f->node_box.type == NODEBOX_CONNECTED) { for (int dir = 0; dir != 6; dir++) { - int flag = 1 << dir; - v3s16 p2 = blockpos_nodes + p + connection_dirs[dir]; + u8 flag = 1 << dir; + v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir]; MapNode n2 = data->m_vmanip.getNodeNoEx(p2); if (nodedef->nodeboxConnects(n, n2, flag)) neighbors_set |= flag; -- cgit v1.2.3 From 7d3972a5049324f776ab008894c34569641f0073 Mon Sep 17 00:00:00 2001 From: LoneWolfHT Date: Tue, 19 May 2020 10:10:39 -0700 Subject: Add ability to scale HUD text (#9814) Add 'size' property to HUD text elements that is used for relative font size calculations. --- doc/lua_api.txt | 2 ++ src/client/hud.cpp | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 07758c237..9c7c42436 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1356,6 +1356,8 @@ Displays text on the HUD. text. Specify `0xFFFFFF` for white text, `0xFF0000` for red, and so on. * `alignment`: The alignment of the text. * `offset`: offset in pixels from position. +* `size`: size of the text. + The player-set font size is multiplied by size.x (y value isn't used). ### `statbar` diff --git a/src/client/hud.cpp b/src/client/hud.cpp index f8f712762..4edc229b2 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -319,16 +319,21 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) floor(e->pos.Y * (float) m_screensize.Y + 0.5)); switch (e->type) { case HUD_ELEM_TEXT: { + irr::gui::IGUIFont *textfont = font; + if (e->size.X > 0) + textfont = g_fontengine->getFont( + e->size.X * g_fontengine->getDefaultFontSize()); + video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, (e->number >> 0) & 0xFF); core::rect size(0, 0, e->scale.X, text_height * e->scale.Y); std::wstring text = unescape_translate(utf8_to_wide(e->text)); - core::dimension2d textsize = font->getDimension(text.c_str()); + core::dimension2d textsize = textfont->getDimension(text.c_str()); v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2), (e->align.Y - 1.0) * (textsize.Height / 2)); v2s32 offs(e->offset.X, e->offset.Y); - font->draw(text.c_str(), size + pos + offset + offs, color); + textfont->draw(text.c_str(), size + pos + offset + offs, color); break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); -- cgit v1.2.3 From c47a680db7f3c2f241cc444a1257607492872412 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 May 2020 20:45:02 +0200 Subject: Stop wasting memory on identical textures when texture filtering is disabled --- src/client/tile.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 0fa7a4ae2..d03588b2b 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -668,7 +668,14 @@ video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id) video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *id) { - return getTexture(name + "^[applyfiltersformesh", id); + static thread_local bool filter_needed = + g_settings->getBool("texture_clean_transparent") || + ((m_setting_trilinear_filter || m_setting_bilinear_filter) && + g_settings->getS32("texture_min_size") > 1); + // Avoid duplicating texture if it won't actually change + if (filter_needed) + return getTexture(name + "^[applyfiltersformesh", id); + return getTexture(name, id); } Palette* TextureSource::getPalette(const std::string &name) @@ -1623,6 +1630,9 @@ bool TextureSource::generateImagePart(std::string part_of_name, */ else if (str_starts_with(part_of_name, "[applyfiltersformesh")) { + /* IMPORTANT: When changing this, getTextureForMesh() needs to be + * updated too. */ + // Apply the "clean transparent" filter, if configured. if (g_settings->getBool("texture_clean_transparent")) imageCleanTransparent(baseimg, 127); -- cgit v1.2.3 From 82e41378937378667cdbdda3ea9e8c1acb5822ea Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Thu, 21 May 2020 00:52:10 +0300 Subject: Cache liquid alternative IDs (#8053) --- src/client/content_mapblock.cpp | 4 ++-- src/map.cpp | 12 ++++++------ src/mapnode.cpp | 4 ++-- src/nodedef.cpp | 21 ++++++++++++++++++++- src/nodedef.h | 9 ++++++--- src/server.cpp | 4 ++-- 6 files changed, 38 insertions(+), 16 deletions(-) (limited to 'src/client') diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index bf1b4c7d6..50efd2e40 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -405,8 +405,8 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing() MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z)); MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z)); - c_flowing = nodedef->getId(f->liquid_alternative_flowing); - c_source = nodedef->getId(f->liquid_alternative_source); + c_flowing = f->liquid_alternative_flowing_id; + c_source = f->liquid_alternative_source_id; top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source); draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source); if (draw_liquid_bottom) { diff --git a/src/map.cpp b/src/map.cpp index 5f1b984a4..677cbc869 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -568,7 +568,7 @@ void Map::transformLiquids(std::map &modified_blocks, switch (liquid_type) { case LIQUID_SOURCE: liquid_level = LIQUID_LEVEL_SOURCE; - liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing); + liquid_kind = cf.liquid_alternative_flowing_id; break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); @@ -641,8 +641,8 @@ void Map::transformLiquids(std::map &modified_blocks, case LIQUID_SOURCE: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); - if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = cfnb.liquid_alternative_flowing_id; + if (cfnb.liquid_alternative_flowing_id != liquid_kind) { neutrals[num_neutrals++] = nb; } else { // Do not count bottom source, it will screw things up @@ -653,8 +653,8 @@ void Map::transformLiquids(std::map &modified_blocks, case LIQUID_FLOWING: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); - if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = cfnb.liquid_alternative_flowing_id; + if (cfnb.liquid_alternative_flowing_id != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -680,7 +680,7 @@ void Map::transformLiquids(std::map &modified_blocks, // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) // or the flowing alternative of the first of the surrounding sources (if it's air), so // it's perfectly safe to use liquid_kind here to determine the new node content. - new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source); + new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id; } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) { // liquid_kind is set properly, see above max_node_level = new_node_level = LIQUID_LEVEL_MAX; diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 24d62b504..dcf1f6d6e 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -622,10 +622,10 @@ s8 MapNode::setLevel(const NodeDefManager *nodemgr, s16 level) } if (level >= LIQUID_LEVEL_SOURCE) { rest = level - LIQUID_LEVEL_SOURCE; - setContent(nodemgr->getId(f.liquid_alternative_source)); + setContent(f.liquid_alternative_source_id); setParam2(0); } else { - setContent(nodemgr->getId(f.liquid_alternative_flowing)); + setContent(f.liquid_alternative_flowing_id); setParam2((level & LIQUID_LEVEL_MASK) | (getParam2() & ~LIQUID_LEVEL_MASK)); } } else if (f.param_type_2 == CPT2_LEVELED) { diff --git a/src/nodedef.cpp b/src/nodedef.cpp index b8211fceb..cb841e544 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -371,7 +371,9 @@ void ContentFeatures::reset() leveled_max = LEVELED_MAX; liquid_type = LIQUID_NONE; liquid_alternative_flowing = ""; + liquid_alternative_flowing_id = CONTENT_IGNORE; liquid_alternative_source = ""; + liquid_alternative_source_id = CONTENT_IGNORE; liquid_viscosity = 0; liquid_renewable = true; liquid_range = LIQUID_LEVEL_MAX+1; @@ -1444,6 +1446,10 @@ void NodeDefManager::deSerialize(std::istream &is) getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); } + + // Since liquid_alternative_flowing_id and liquid_alternative_source_id + // are not sent, resolve them client-side too. + resolveCrossrefs(); } @@ -1504,15 +1510,28 @@ void NodeDefManager::resetNodeResolveState() m_pending_resolve_callbacks.clear(); } -void NodeDefManager::mapNodeboxConnections() +static void removeDupes(std::vector &list) +{ + std::sort(list.begin(), list.end()); + auto new_end = std::unique(list.begin(), list.end()); + list.erase(new_end, list.end()); +} + +void NodeDefManager::resolveCrossrefs() { for (ContentFeatures &f : m_content_features) { + if (f.liquid_type != LIQUID_NONE) { + f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing); + f.liquid_alternative_source_id = getId(f.liquid_alternative_source); + continue; + } if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED) continue; for (const std::string &name : f.connects_to) { getIds(name, f.connects_to_ids); } + removeDupes(f.connects_to_ids); } } diff --git a/src/nodedef.h b/src/nodedef.h index 497e7ee0e..0992001e1 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -368,8 +368,10 @@ struct ContentFeatures enum LiquidType liquid_type; // If the content is liquid, this is the flowing version of the liquid. std::string liquid_alternative_flowing; + content_t liquid_alternative_flowing_id; // If the content is liquid, this is the source version of the liquid. std::string liquid_alternative_source; + content_t liquid_alternative_source_id; // Viscosity for fluid flow, ranging from 1 to 7, with // 1 giving almost instantaneous propagation and 7 being // the slowest possible @@ -428,7 +430,7 @@ struct ContentFeatures } bool sameLiquid(const ContentFeatures &f) const{ if(!isLiquid() || !f.isLiquid()) return false; - return (liquid_alternative_flowing == f.liquid_alternative_flowing); + return (liquid_alternative_flowing_id == f.liquid_alternative_flowing_id); } int getGroup(const std::string &group) const @@ -641,10 +643,11 @@ public: void resetNodeResolveState(); /*! - * Resolves the IDs to which connecting nodes connect from names. + * Resolves (caches the IDs) cross-references between nodes, + * like liquid alternatives. * Must be called after node registration has finished! */ - void mapNodeboxConnections(); + void resolveCrossrefs(); private: /*! diff --git a/src/server.cpp b/src/server.cpp index b28c30e1e..92870f972 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -412,8 +412,8 @@ void Server::init() // Perform pending node name resolutions m_nodedef->runNodeResolveCallbacks(); - // unmap node names for connected nodeboxes - m_nodedef->mapNodeboxConnections(); + // unmap node names in cross-references + m_nodedef->resolveCrossrefs(); // init the recipe hashes to speed up crafting m_craftdef->initHashes(this); -- cgit v1.2.3 From 1357ea1da25bf01acaf95d5f5419d4f83a84ed61 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 13:23:25 +0200 Subject: Cleanup of particle & particlespawner structures and code (#9893) --- src/CMakeLists.txt | 1 + src/client/clientevent.h | 45 +---- src/client/particles.cpp | 333 ++++++++++++------------------- src/client/particles.h | 58 ++---- src/network/clientpackethandler.cpp | 136 ++++--------- src/particles.cpp | 53 +++++ src/particles.h | 73 +++++++ src/script/lua_api/l_particles.cpp | 186 ++++++++--------- src/script/lua_api/l_particles_local.cpp | 149 ++++++-------- src/server.cpp | 106 ++++------ src/server.h | 42 +--- 11 files changed, 502 insertions(+), 680 deletions(-) create mode 100644 src/particles.cpp create mode 100644 src/particles.h (limited to 'src/client') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd68ceec9..3d6d1b0ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -427,6 +427,7 @@ set(common_SRCS noise.cpp objdef.cpp object_properties.cpp + particles.cpp pathfinder.cpp player.cpp porting.cpp diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 7f3984b03..9bd31efce 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -21,8 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" -#include "hud.h" -#include "skyparams.h" + +struct ParticleParameters; +struct ParticleSpawnerParameters; +struct SkyboxParams; +struct SunParams; +struct MoonParams; +struct StarParams; enum ClientEventType : u8 { @@ -77,44 +82,12 @@ struct ClientEvent } show_formspec; // struct{ //} textures_updated; + ParticleParameters *spawn_particle; struct { - v3f *pos; - v3f *vel; - v3f *acc; - f32 expirationtime; - f32 size; - bool collisiondetection; - bool collision_removal; - bool object_collision; - bool vertical; - std::string *texture; - struct TileAnimationParams animation; - u8 glow; - } spawn_particle; - struct - { - u16 amount; - f32 spawntime; - v3f *minpos; - v3f *maxpos; - v3f *minvel; - v3f *maxvel; - v3f *minacc; - v3f *maxacc; - f32 minexptime; - f32 maxexptime; - f32 minsize; - f32 maxsize; - bool collisiondetection; - bool collision_removal; - bool object_collision; + ParticleSpawnerParameters *p; u16 attached_id; - bool vertical; - std::string *texture; u64 id; - struct TileAnimationParams animation; - u8 glow; } add_particlespawner; struct { diff --git a/src/client/particles.cpp b/src/client/particles.cpp index a0e4e54eb..c2e751b4f 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -37,32 +37,31 @@ with this program; if not, write to the Free Software Foundation, Inc., Utility */ -v3f random_v3f(v3f min, v3f max) +static f32 random_f32(f32 min, f32 max) +{ + return rand() / (float)RAND_MAX * (max - min) + min; +} + +static v3f random_v3f(v3f min, v3f max) { return v3f( - rand() / (float)RAND_MAX * (max.X - min.X) + min.X, - rand() / (float)RAND_MAX * (max.Y - min.Y) + min.Y, - rand() / (float)RAND_MAX * (max.Z - min.Z) + min.Z); + random_f32(min.X, max.X), + random_f32(min.Y, max.Y), + random_f32(min.Z, max.Z)); } +/* + Particle +*/ + Particle::Particle( IGameDef *gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color ): scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), @@ -81,33 +80,28 @@ Particle::Particle( m_material.setTexture(0, texture); m_texpos = texpos; m_texsize = texsize; - m_animation = anim; + m_animation = p.animation; // Color m_base_color = color; m_color = color; // Particle related - m_pos = pos; - m_velocity = velocity; - m_acceleration = acceleration; - m_expiration = expirationtime; + m_pos = p.pos; + m_velocity = p.vel; + m_acceleration = p.acc; + m_expiration = p.expirationtime; m_player = player; - m_size = size; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; - m_vertical = vertical; - m_glow = glow; + m_size = p.size; + m_collisiondetection = p.collisiondetection; + m_collision_removal = p.collision_removal; + m_object_collision = p.object_collision; + m_vertical = p.vertical; + m_glow = p.glow; // Irrlicht stuff - m_collisionbox = aabb3f( - -size / 2, - -size / 2, - -size / 2, - size / 2, - size / 2, - size / 2); + const float c = p.size / 2; + m_collisionbox = aabb3f(-c, -c, -c, c, c, c); this->setAutomaticCulling(scene::EAC_OFF); // Init lighting @@ -255,52 +249,22 @@ void Particle::updateVertices() ParticleSpawner::ParticleSpawner( IGameDef *gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, - u8 glow, ParticleManager *p_manager ): - m_particlemanager(p_manager) + m_particlemanager(p_manager), p(p) { m_gamedef = gamedef; m_player = player; - m_amount = amount; - m_spawntime = time; - m_minpos = minpos; - m_maxpos = maxpos; - m_minvel = minvel; - m_maxvel = maxvel; - m_minacc = minacc; - m_maxacc = maxacc; - m_minexptime = minexptime; - m_maxexptime = maxexptime; - m_minsize = minsize; - m_maxsize = maxsize; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; m_attached_id = attached_id; - m_vertical = vertical; m_texture = texture; m_time = 0; - m_animation = anim; - m_glow = glow; - for (u16 i = 0; i <= m_amount; i++) - { - float spawntime = (float)rand() / (float)RAND_MAX * m_spawntime; + m_spawntimes.reserve(p.amount + 1); + for (u16 i = 0; i <= p.amount; i++) { + float spawntime = rand() / (float)RAND_MAX * p.time; m_spawntimes.push_back(spawntime); } } @@ -309,7 +273,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, const core::matrix4 *attached_absolute_pos_rot_matrix) { v3f ppos = m_player->getPosition() / BS; - v3f pos = random_v3f(m_minpos, m_maxpos); + v3f pos = random_v3f(p.minpos, p.maxpos); // Need to apply this first or the following check // will be wrong for attached spawners @@ -326,41 +290,32 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, if (pos.getDistanceFrom(ppos) > radius) return; - v3f vel = random_v3f(m_minvel, m_maxvel); - v3f acc = random_v3f(m_minacc, m_maxacc); + // Parameters for the single particle we're about to spawn + ParticleParameters pp; + pp.pos = pos; + + pp.vel = random_v3f(p.minvel, p.maxvel); + pp.acc = random_v3f(p.minacc, p.maxacc); if (attached_absolute_pos_rot_matrix) { // Apply attachment rotation - attached_absolute_pos_rot_matrix->rotateVect(vel); - attached_absolute_pos_rot_matrix->rotateVect(acc); + attached_absolute_pos_rot_matrix->rotateVect(pp.vel); + attached_absolute_pos_rot_matrix->rotateVect(pp.acc); } - float exptime = rand() / (float)RAND_MAX - * (m_maxexptime - m_minexptime) - + m_minexptime; + pp.expirationtime = random_f32(p.minexptime, p.maxexptime); + pp.size = random_f32(p.minsize, p.maxsize); - float size = rand() / (float)RAND_MAX - * (m_maxsize - m_minsize) - + m_minsize; + p.copyCommon(pp); m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, - pos, - vel, - acc, - exptime, - size, - m_collisiondetection, - m_collision_removal, - m_object_collision, - m_vertical, + pp, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - m_animation, - m_glow + v2f(1.0, 1.0) )); } @@ -381,12 +336,11 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) } } - if (m_spawntime != 0) { + if (p.time != 0) { // Spawner exists for a predefined timespan - for (std::vector::iterator i = m_spawntimes.begin(); - i != m_spawntimes.end();) { - if ((*i) <= m_time && m_amount > 0) { - --m_amount; + for (auto i = m_spawntimes.begin(); i != m_spawntimes.end(); ) { + if ((*i) <= m_time && p.amount > 0) { + --p.amount; // Pretend to, but don't actually spawn a particle if it is // attached to an unloaded object or distant from player. @@ -405,13 +359,16 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) if (unloaded) return; - for (int i = 0; i <= m_amount; i++) { + for (int i = 0; i <= p.amount; i++) { if (rand() / (float)RAND_MAX < dtime) spawnParticle(env, radius, attached_absolute_pos_rot_matrix); } } } +/* + ParticleManager +*/ ParticleManager::ParticleManager(ClientEnvironment *env) : m_env(env) @@ -479,99 +436,84 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, { switch (event->type) { case CE_DELETE_PARTICLESPAWNER: { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->delete_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->delete_particlespawner.id)->second; - m_particle_spawners.erase(event->delete_particlespawner.id); - } + deleteParticleSpawner(event->delete_particlespawner.id); // no allocated memory in delete event break; } case CE_ADD_PARTICLESPAWNER: { - { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->add_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->add_particlespawner.id)->second; - m_particle_spawners.erase(event->add_particlespawner.id); - } - } + deleteParticleSpawner(event->add_particlespawner.id); + + const ParticleSpawnerParameters &p = *event->add_particlespawner.p; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(p.texture); auto toadd = new ParticleSpawner(client, player, - event->add_particlespawner.amount, - event->add_particlespawner.spawntime, - *event->add_particlespawner.minpos, - *event->add_particlespawner.maxpos, - *event->add_particlespawner.minvel, - *event->add_particlespawner.maxvel, - *event->add_particlespawner.minacc, - *event->add_particlespawner.maxacc, - event->add_particlespawner.minexptime, - event->add_particlespawner.maxexptime, - event->add_particlespawner.minsize, - event->add_particlespawner.maxsize, - event->add_particlespawner.collisiondetection, - event->add_particlespawner.collision_removal, - event->add_particlespawner.object_collision, + p, event->add_particlespawner.attached_id, - event->add_particlespawner.vertical, texture, - event->add_particlespawner.animation, - event->add_particlespawner.glow, this); - /* delete allocated content of event */ - delete event->add_particlespawner.minpos; - delete event->add_particlespawner.maxpos; - delete event->add_particlespawner.minvel; - delete event->add_particlespawner.maxvel; - delete event->add_particlespawner.minacc; - delete event->add_particlespawner.texture; - delete event->add_particlespawner.maxacc; - - { - MutexAutoLock lock(m_spawner_list_lock); - m_particle_spawners[event->add_particlespawner.id] = toadd; - } + addParticleSpawner(event->add_particlespawner.id, toadd); + + delete event->add_particlespawner.p; break; } case CE_SPAWN_PARTICLE: { + const ParticleParameters &p = *event->spawn_particle; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(p.texture); Particle *toadd = new Particle(client, player, m_env, - *event->spawn_particle.pos, - *event->spawn_particle.vel, - *event->spawn_particle.acc, - event->spawn_particle.expirationtime, - event->spawn_particle.size, - event->spawn_particle.collisiondetection, - event->spawn_particle.collision_removal, - event->spawn_particle.object_collision, - event->spawn_particle.vertical, + p, texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - event->spawn_particle.animation, - event->spawn_particle.glow); + v2f(1.0, 1.0)); addParticle(toadd); - delete event->spawn_particle.pos; - delete event->spawn_particle.vel; - delete event->spawn_particle.acc; - delete event->spawn_particle.texture; - + delete event->spawn_particle; break; } default: break; } } +bool ParticleManager::getNodeParticleParams(const MapNode &n, + const ContentFeatures &f, ParticleParameters &p, + video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) +{ + // No particles for "airlike" nodes + if (f.drawtype == NDT_AIRLIKE) + return false; + + // Texture + u8 texid = rand() % 6; + const TileLayer &tile = f.tiles[texid].layers[0]; + p.animation.type = TAT_NONE; + + // Only use first frame of animated texture + if (tile.material_flags & MATERIAL_FLAG_ANIMATION) + *texture = (*tile.frames)[0].texture; + else + *texture = tile.texture; + + float size = (rand() % 8) / 64.0f; + p.size = BS * size; + if (tile.scale) + size /= tile.scale; + texsize = v2f(size * 2.0f, size * 2.0f); + texpos.X = (rand() % 64) / 64.0f - texsize.X; + texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + + if (tile.has_color) + *color = tile.color; + else + n.getColor(f, color); + + return true; +} + // The final burst of particles when a node is finally dug, *not* particles // spawned during the digging of a node. @@ -593,73 +535,41 @@ void ParticleManager::addDiggingParticles(IGameDef *gamedef, void ParticleManager::addNodeParticle(IGameDef *gamedef, LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f) { - // No particles for "airlike" nodes - if (f.drawtype == NDT_AIRLIKE) - return; - - // Texture - u8 texid = myrand_range(0, 5); - const TileLayer &tile = f.tiles[texid].layers[0]; + ParticleParameters p; video::ITexture *texture; - struct TileAnimationParams anim; - anim.type = TAT_NONE; + v2f texpos, texsize; + video::SColor color; - // Only use first frame of animated texture - if (tile.material_flags & MATERIAL_FLAG_ANIMATION) - texture = (*tile.frames)[0].texture; - else - texture = tile.texture; + if (!getNodeParticleParams(n, f, p, &texture, texpos, texsize, &color)) + return; - float size = (rand() % 8) / 64.0f; - float visual_size = BS * size; - if (tile.scale) - size /= tile.scale; - v2f texsize(size * 2.0f, size * 2.0f); - v2f texpos; - texpos.X = (rand() % 64) / 64.0f - texsize.X; - texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + p.expirationtime = (rand() % 100) / 100.0f; // Physics - v3f velocity( + p.vel = v3f( (rand() % 150) / 50.0f - 1.5f, (rand() % 150) / 50.0f, (rand() % 150) / 50.0f - 1.5f ); - v3f acceleration( + p.acc = v3f( 0.0f, -player->movement_gravity * player->physics_override_gravity / BS, 0.0f ); - v3f particlepos = v3f( + p.pos = v3f( (f32)pos.X + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Y + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Z + (rand() % 100) / 200.0f - 0.25f ); - video::SColor color; - if (tile.has_color) - color = tile.color; - else - n.getColor(f, &color); - Particle *toadd = new Particle( gamedef, player, m_env, - particlepos, - velocity, - acceleration, - (rand() % 100) / 100.0f, // expiration time - visual_size, - true, - false, - false, - false, + p, texture, texpos, texsize, - anim, - 0, color); addParticle(toadd); @@ -670,3 +580,20 @@ void ParticleManager::addParticle(Particle *toadd) MutexAutoLock lock(m_particle_list_lock); m_particles.push_back(toadd); } + + +void ParticleManager::addParticleSpawner(u64 id, ParticleSpawner *toadd) +{ + MutexAutoLock lock(m_spawner_list_lock); + m_particle_spawners[id] = toadd; +} + +void ParticleManager::deleteParticleSpawner(u64 id) +{ + MutexAutoLock lock(m_spawner_list_lock); + auto it = m_particle_spawners.find(id); + if (it != m_particle_spawners.end()) { + delete it->second; + m_particle_spawners.erase(it); + } +} diff --git a/src/client/particles.h b/src/client/particles.h index e7b8cbe24..7dda0e1b1 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "client/tile.h" #include "localplayer.h" -#include "tileanimation.h" +#include "../particles.h" struct ClientEvent; class ParticleManager; @@ -38,20 +38,10 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color = video::SColor(0xFFFFFFFF) ); ~Particle() = default; @@ -119,20 +109,9 @@ class ParticleSpawner public: ParticleSpawner(IGameDef* gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minp, v3f maxp, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, u8 glow, ParticleManager* p_manager); ~ParticleSpawner() = default; @@ -140,7 +119,7 @@ public: void step(float dtime, ClientEnvironment *env); bool get_expired () - { return (m_amount <= 0) && m_spawntime != 0; } + { return p.amount <= 0 && p.time != 0; } private: void spawnParticle(ClientEnvironment *env, float radius, @@ -150,27 +129,10 @@ private: float m_time; IGameDef *m_gamedef; LocalPlayer *m_player; - u16 m_amount; - float m_spawntime; - v3f m_minpos; - v3f m_maxpos; - v3f m_minvel; - v3f m_maxvel; - v3f m_minacc; - v3f m_maxacc; - float m_minexptime; - float m_maxexptime; - float m_minsize; - float m_maxsize; + ParticleSpawnerParameters p; video::ITexture *m_texture; std::vector m_spawntimes; - bool m_collisiondetection; - bool m_collision_removal; - bool m_object_collision; - bool m_vertical; u16 m_attached_id; - struct TileAnimationParams m_animation; - u8 m_glow; }; /** @@ -197,8 +159,8 @@ public: /** * This function is only used by client particle spawners * - * We don't need to check the particle spawner list because client ID will n - * ever overlap (u64) + * We don't need to check the particle spawner list because client ID will + * never overlap (u64) * @return new id */ u64 generateSpawnerId() @@ -207,9 +169,15 @@ public: } protected: + static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, + ParticleParameters &p, video::ITexture **texture, v2f &texpos, + v2f &texsize, video::SColor *color); + void addParticle(Particle* toadd); private: + void addParticleSpawner(u64 id, ParticleSpawner *toadd); + void deleteParticleSpawner(u64 id); void stepParticles(float dtime); void stepSpawners(float dtime); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 7b1b1368c..054e60c3c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -958,114 +958,56 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) std::string datastring(pkt->getString(0), pkt->getSize()); std::istringstream is(datastring, std::ios_base::binary); - v3f pos = readV3F32(is); - v3f vel = readV3F32(is); - v3f acc = readV3F32(is); - float expirationtime = readF32(is); - float size = readF32(is); - bool collisiondetection = readU8(is); - std::string texture = deSerializeLongString(is); - - bool vertical = false; - bool collision_removal = false; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - vertical = readU8(is); - collision_removal = readU8(is); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleParameters p; + p.deSerialize(is, m_proto_ver); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.object_collision = object_collision; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); m_client_event_queue.push(event); } void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) { - u16 amount; - float spawntime; - v3f minpos; - v3f maxpos; - v3f minvel; - v3f maxvel; - v3f minacc; - v3f maxacc; - float minexptime; - float maxexptime; - float minsize; - float maxsize; - bool collisiondetection; - u32 server_id; - - *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel - >> minacc >> maxacc >> minexptime >> maxexptime >> minsize - >> maxsize >> collisiondetection; - - std::string texture = pkt->readLongString(); - - *pkt >> server_id; - - bool vertical = false; - bool collision_removal = false; - u16 attached_id = 0; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - *pkt >> vertical; - *pkt >> collision_removal; - *pkt >> attached_id; + std::string datastring(pkt->getString(0), pkt->getSize()); + std::istringstream is(datastring, std::ios_base::binary); - // This is horrible but required (why are there two ways to deserialize pkts?) - std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes()); - std::istringstream is(datastring, std::ios_base::binary); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleSpawnerParameters p; + u32 server_id; + u16 attached_id = 0; + + p.amount = readU16(is); + p.time = readF32(is); + p.minpos = readV3F32(is); + p.maxpos = readV3F32(is); + p.minvel = readV3F32(is); + p.maxvel = readV3F32(is); + p.minacc = readV3F32(is); + p.maxacc = readV3F32(is); + p.minexptime = readF32(is); + p.maxexptime = readF32(is); + p.minsize = readF32(is); + p.maxsize = readF32(is); + p.collisiondetection = readU8(is); + p.texture = deSerializeLongString(is); + + server_id = readU32(is); + + p.vertical = readU8(is); + p.collision_removal = readU8(is); + + attached_id = readU16(is); + + p.animation.deSerialize(is, m_proto_ver); + p.glow = readU8(is); + p.object_collision = readU8(is); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = spawntime; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.object_collision = object_collision; - event->add_particlespawner.attached_id = attached_id; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = server_id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = attached_id; + event->add_particlespawner.id = server_id; m_client_event_queue.push(event); } diff --git a/src/particles.cpp b/src/particles.cpp new file mode 100644 index 000000000..711d189f6 --- /dev/null +++ b/src/particles.cpp @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2020 sfan5 + +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 "particles.h" +#include "util/serialize.h" + +void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const +{ + writeV3F32(os, pos); + writeV3F32(os, vel); + writeV3F32(os, acc); + writeF32(os, expirationtime); + writeF32(os, size); + writeU8(os, collisiondetection); + os << serializeLongString(texture); + writeU8(os, vertical); + writeU8(os, collision_removal); + animation.serialize(os, 6); /* NOT the protocol ver */ + writeU8(os, glow); + writeU8(os, object_collision); +} + +void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) +{ + pos = readV3F32(is); + vel = readV3F32(is); + acc = readV3F32(is); + expirationtime = readF32(is); + size = readF32(is); + collisiondetection = readU8(is); + texture = deSerializeLongString(is); + vertical = readU8(is); + collision_removal = readU8(is); + animation.deSerialize(is, 6); /* NOT the protocol ver */ + glow = readU8(is); + object_collision = readU8(is); +} diff --git a/src/particles.h b/src/particles.h new file mode 100644 index 000000000..659c1249f --- /dev/null +++ b/src/particles.h @@ -0,0 +1,73 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +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 +#include "irrlichttypes_bloated.h" +#include "tileanimation.h" + +// This file defines the particle-related structures that both the server and +// client need. The ParticleManager and rendering is in client/particles.h + +struct CommonParticleParams { + bool collisiondetection = false; + bool collision_removal = false; + bool object_collision = false; + bool vertical = false; + std::string texture; + struct TileAnimationParams animation; + u8 glow = 0; + + CommonParticleParams() { + animation.type = TAT_NONE; + } + + /* This helper is useful for copying params from + * ParticleSpawnerParameters to ParticleParameters */ + inline void copyCommon(CommonParticleParams &to) const { + to.collisiondetection = collisiondetection; + to.collision_removal = collision_removal; + to.object_collision = object_collision; + to.vertical = vertical; + to.texture = texture; + to.animation = animation; + to.glow = glow; + } +}; + +struct ParticleParameters : CommonParticleParams { + v3f pos; + v3f vel; + v3f acc; + f32 expirationtime = 1; + f32 size = 1; + + void serialize(std::ostream &os, u16 protocol_ver) const; + void deSerialize(std::istream &is, u16 protocol_ver); +}; + +struct ParticleSpawnerParameters : CommonParticleParams { + u16 amount = 1; + v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; + f32 time = 1; + f32 minexptime = 1, maxexptime = 1, minsize = 1, maxsize = 1; + + // For historical reasons no (de-)serialization methods here +}; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 340903ebf..7680aa17b 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" -#include "client/particles.h" +#include "particles.h" // add_particle({pos=, velocity=, acceleration=, expirationtime=, // size=, collisiondetection=, collision_removal=, object_collision=, @@ -40,85 +40,81 @@ with this program; if not, write to the Free Software Foundation, Inc., // glow = num int ModApiParticles::l_add_particle(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - expirationtime = size = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - std::string texture; + struct ParticleParameters p; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) // deprecated { - log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); - pos = check_v3f(L, 1); - vel = check_v3f(L, 2); - acc = check_v3f(L, 3); - expirationtime = luaL_checknumber(L, 4); - size = luaL_checknumber(L, 5); - collisiondetection = readParam(L, 6); - texture = luaL_checkstring(L, 7); + log_deprecated(L, "Deprecated add_particle call with " + "individual parameters instead of definition"); + p.pos = check_v3f(L, 1); + p.vel = check_v3f(L, 2); + p.acc = check_v3f(L, 3); + p.expirationtime = luaL_checknumber(L, 4); + p.size = luaL_checknumber(L, 5); + p.collisiondetection = readParam(L, 6); + p.texture = luaL_checkstring(L, 7); if (lua_gettop(L) == 8) // only spawn for a single player playername = luaL_checkstring(L, 8); } else if (lua_istable(L, 1)) { lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "vel"); if (lua_istable(L, -1)) { - vel = check_v3f(L, -1); + p.vel = check_v3f(L, -1); log_deprecated(L, "The use of vel is deprecated. " "Use velocity instead"); } lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel; + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acc"); if (lua_istable(L, -1)) { - acc = check_v3f(L, -1); + p.acc = check_v3f(L, -1); log_deprecated(L, "The use of acc is deprecated. " "Use acceleration instead"); } lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc; + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); - vertical = getboolfield_default(L, 1, "vertical", vertical); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - playername = getstringfield_default(L, 1, "playername", ""); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); - glow = getintfield_default(L, 1, "glow", 0); + playername = getstringfield_default(L, 1, "playername", ""); } - getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, - collisiondetection, collision_removal, object_collision, vertical, - texture, animation, glow); + + getServer(L)->spawnParticle(playername, p); return 1; } @@ -146,84 +142,82 @@ int ModApiParticles::l_add_particle(lua_State *L) // glow = num int ModApiParticles::l_add_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - u16 amount = 1; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - time = minexptime = maxexptime = minsize = maxsize = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; + ParticleSpawnerParameters p; ServerActiveObject *attached = NULL; - std::string texture; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) //deprecated { - log_deprecated(L,"Deprecated add_particlespawner call with individual parameters instead of definition"); - amount = luaL_checknumber(L, 1); - time = luaL_checknumber(L, 2); - minpos = check_v3f(L, 3); - maxpos = check_v3f(L, 4); - minvel = check_v3f(L, 5); - maxvel = check_v3f(L, 6); - minacc = check_v3f(L, 7); - maxacc = check_v3f(L, 8); - minexptime = luaL_checknumber(L, 9); - maxexptime = luaL_checknumber(L, 10); - minsize = luaL_checknumber(L, 11); - maxsize = luaL_checknumber(L, 12); - collisiondetection = readParam(L, 13); - texture = luaL_checkstring(L, 14); + log_deprecated(L, "Deprecated add_particlespawner call with " + "individual parameters instead of definition"); + p.amount = luaL_checknumber(L, 1); + p.time = luaL_checknumber(L, 2); + p.minpos = check_v3f(L, 3); + p.maxpos = check_v3f(L, 4); + p.minvel = check_v3f(L, 5); + p.maxvel = check_v3f(L, 6); + p.minacc = check_v3f(L, 7); + p.maxacc = check_v3f(L, 8); + p.minexptime = luaL_checknumber(L, 9); + p.maxexptime = luaL_checknumber(L, 10); + p.minsize = luaL_checknumber(L, 11); + p.maxsize = luaL_checknumber(L, 12); + p.collisiondetection = readParam(L, 13); + p.texture = luaL_checkstring(L, 14); if (lua_gettop(L) == 15) // only spawn for a single player playername = luaL_checkstring(L, 15); } else if (lua_istable(L, 1)) { - amount = getintfield_default(L, 1, "amount", amount); - time = getfloatfield_default(L, 1, "time", time); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos; + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos; + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel; + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel; + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc; + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc; + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", minexptime); - maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime); - minsize = getfloatfield_default(L, 1, "minsize", minsize); - maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "attached"); @@ -233,25 +227,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) attached = ObjectRef::getobject(ref); } - vertical = getboolfield_default(L, 1, "vertical", vertical); - texture = getstringfield_default(L, 1, "texture", ""); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.glow = getintfield_default(L, 1, "glow", p.glow); } - u32 id = getServer(L)->addParticleSpawner(amount, time, - minpos, maxpos, - minvel, maxvel, - minacc, maxacc, - minexptime, maxexptime, - minsize, maxsize, - collisiondetection, - collision_removal, - object_collision, - attached, - vertical, - texture, playername, - animation, glow); + u32 id = getServer(L)->addParticleSpawner(p, attached, playername); lua_pushnumber(L, id); return 1; @@ -261,7 +243,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) // player (string) is optional int ModApiParticles::l_delete_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters u32 id = luaL_checknumber(L, 1); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index a9bf55665..9595b2fab 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -32,56 +32,44 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - bool collisiondetection, vertical, collision_removal; - - struct TileAnimationParams animation; - animation.type = TAT_NONE; - - std::string texture; - - u8 glow; + ParticleParameters p; lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - - glow = getintfield_default(L, 1, "glow", 0); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); getClient(L)->pushToEventQueue(event); return 0; @@ -90,94 +78,69 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); - // Get parameters - u16 amount; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - bool collisiondetection, vertical, collision_removal; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - // TODO: Implement this when there is a way to get an objectref. - // ServerActiveObject *attached = NULL; - std::string texture; - u8 glow; + // Get parameters + ParticleSpawnerParameters p; - amount = getintfield_default(L, 1, "amount", 1); - time = getfloatfield_default(L, 1, "time", 1); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", 1); - maxexptime = getfloatfield_default(L, 1, "maxexptime", 1); - minsize = getfloatfield_default(L, 1, "minsize", 1); - maxsize = getfloatfield_default(L, 1, "maxsize", 1); - - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - // TODO: Implement this when a way to get an objectref on the client is added -// lua_getfield(L, 1, "attached"); -// if (!lua_isnil(L, -1)) { -// ObjectRef *ref = ObjectRef::checkobject(L, -1); -// lua_pop(L, 1); -// attached = ObjectRef::getobject(ref); -// } - - texture = getstringfield_default(L, 1, "texture", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = time; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.attached_id = 0; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = 0; + event->add_particlespawner.id = id; getClient(L)->pushToEventQueue(event); lua_pushnumber(L, id); diff --git a/src/server.cpp b/src/server.cpp index 92870f972..68b0131d4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1504,17 +1504,15 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms // Spawns a particle on peer with peer_id void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, bool collisiondetection, - bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) + const ParticleParameters &p) { static thread_local const float radius = g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; if (peer_id == PEER_ID_INEXISTENT) { std::vector clients = m_clients.getClientIDs(); + const v3f pos = p.pos * BS; + const float radius_sq = radius * radius; for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); @@ -1526,76 +1524,59 @@ void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, continue; // Do not send to distant clients - if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq) continue; - SendSpawnParticle(client_id, player->protocol_version, - pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(client_id, player->protocol_version, p); } return; } + assert(protocol_version != 0); NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); - pkt << pos << velocity << acceleration << expirationtime - << size << collisiondetection; - pkt.putLongString(texture); - pkt << vertical; - pkt << collision_removal; - // This is horrible but required (why are there two ways to serialize pkts?) - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + { + // NetworkPacket and iostreams are incompatible... + std::ostringstream oss(std::ios_base::binary); + p.serialize(oss, protocol_version); + pkt.putRawString(oss.str()); + } Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, bool collision_removal, - bool object_collision, u16 attached_id, bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow) + const ParticleSpawnerParameters &p, u16 attached_id, u32 id) { if (peer_id == PEER_ID_INEXISTENT) { - // This sucks and should be replaced: std::vector clients = m_clients.getClientIDs(); for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); if (!player) continue; SendAddParticleSpawner(client_id, player->protocol_version, - amount, spawntime, minpos, maxpos, - minvel, maxvel, minacc, maxacc, minexptime, maxexptime, - minsize, maxsize, collisiondetection, collision_removal, - object_collision, attached_id, vertical, texture, id, - animation, glow); + p, attached_id, id); } return; } + assert(protocol_version != 0); - NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); + NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); - pkt << amount << spawntime << minpos << maxpos << minvel << maxvel - << minacc << maxacc << minexptime << maxexptime << minsize - << maxsize << collisiondetection; + pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel + << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime + << p.minsize << p.maxsize << p.collisiondetection; - pkt.putLongString(texture); + pkt.putLongString(p.texture); - pkt << id << vertical; - pkt << collision_removal; - pkt << attached_id; - // This is horrible but required - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + pkt << id << p.vertical << p.collision_removal << attached_id; + { + std::ostringstream os(std::ios_base::binary); + p.animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + } + pkt << p.glow << p.object_collision; Send(&pkt); } @@ -1604,7 +1585,6 @@ void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id) { NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id); - // Ugly error in this packet pkt << id; if (peer_id != PEER_ID_INEXISTENT) @@ -3365,12 +3345,8 @@ void Server::notifyPlayers(const std::wstring &msg) SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg)); } -void Server::spawnParticle(const std::string &playername, v3f pos, - v3f velocity, v3f acceleration, - float expirationtime, float size, bool - collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) +void Server::spawnParticle(const std::string &playername, + const ParticleParameters &p) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3386,18 +3362,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos, proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(peer_id, proto_ver, p); } -u32 Server::addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, - float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow) +u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3417,16 +3386,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, u32 id; if (attached_id == 0) - id = m_env->addParticleSpawner(spawntime); + id = m_env->addParticleSpawner(p.time); else - id = m_env->addParticleSpawner(spawntime, attached_id); - - SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, - minpos, maxpos, minvel, maxvel, minacc, maxacc, - minexptime, maxexptime, minsize, maxsize, collisiondetection, - collision_removal, object_collision, attached_id, vertical, - texture, id, animation, glow); + id = m_env->addParticleSpawner(p.time, attached_id); + SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id); return id; } diff --git a/src/server.h b/src/server.h index 7a1de9370..27943cc29 100644 --- a/src/server.h +++ b/src/server.h @@ -27,7 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/mods.h" #include "inventorymanager.h" #include "content/subgames.h" -#include "tileanimation.h" // struct TileAnimationParams +#include "tileanimation.h" // TileAnimationParams +#include "particles.h" // ParticleParams #include "network/peerhandler.h" #include "network/address.h" #include "util/numeric.h" @@ -226,24 +227,12 @@ public: void notifyPlayer(const char *name, const std::wstring &msg); void notifyPlayers(const std::wstring &msg); + void spawnParticle(const std::string &playername, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); - - u32 addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, - bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow); + const ParticleParameters &p); + + u32 addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -453,26 +442,13 @@ private: // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - u16 attached_id, - bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow); + const ParticleSpawnerParameters &p, u16 attached_id, u32 id); void SendDeleteParticleSpawner(session_t peer_id, u32 id); // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) void SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); + const ParticleParameters &p); void SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao); void SendActiveObjectMessages(session_t peer_id, const std::string &datas, -- cgit v1.2.3 From 037422fdba9a47bd538480988fbf8aad67d66c85 Mon Sep 17 00:00:00 2001 From: Awkor Date: Sat, 23 May 2020 13:23:05 +0200 Subject: Modernize include guards and add missing ones (#9898) --- src/client/hud.h | 4 +--- src/client/render/factory.h | 2 ++ src/gui/guiButtonImage.h | 2 ++ src/gui/guiButtonItemImage.h | 2 ++ src/hud.h | 4 +--- src/script/lua_api/l_particles_local.h | 2 ++ src/util/md32_common.h | 2 ++ 7 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/client') diff --git a/src/client/hud.h b/src/client/hud.h index 6274b1a83..e2abf326c 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef CLIENT_HUD_HEADER -#define CLIENT_HUD_HEADER +#pragma once #include #include @@ -147,4 +146,3 @@ void drawItemStack( const v3s16 &angle, const v3s16 &rotation_speed); -#endif diff --git a/src/client/render/factory.h b/src/client/render/factory.h index 22738404a..e3339a836 100644 --- a/src/client/render/factory.h +++ b/src/client/render/factory.h @@ -18,6 +18,8 @@ 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 #include "core.h" diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index a948d772b..59a25b4f0 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 9cd0f6188..aad923bda 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" diff --git a/src/hud.h b/src/hud.h index bab420ed2..e015baec1 100644 --- a/src/hud.h +++ b/src/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef HUD_HEADER -#define HUD_HEADER +#pragma once #include "irrlichttypes_extrabloated.h" #include @@ -101,4 +100,3 @@ extern const EnumString es_HudElementType[]; extern const EnumString es_HudElementStat[]; extern const EnumString es_HudBuiltinElement[]; -#endif diff --git a/src/script/lua_api/l_particles_local.h b/src/script/lua_api/l_particles_local.h index 5dff153b3..d8bb2b1c6 100644 --- a/src/script/lua_api/l_particles_local.h +++ b/src/script/lua_api/l_particles_local.h @@ -18,6 +18,8 @@ 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 "lua_api/l_base.h" class ModApiParticlesLocal : public ModApiBase diff --git a/src/util/md32_common.h b/src/util/md32_common.h index 085d1d7a5..a4c2099c9 100644 --- a/src/util/md32_common.h +++ b/src/util/md32_common.h @@ -109,6 +109,8 @@ * */ +#pragma once + #if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) # error "DATA_ORDER must be defined!" #endif -- cgit v1.2.3 From 9d6e7e48d6fb1daff8fedcb2f111164bef61f1e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 14:17:03 +0200 Subject: Implement spawning particles with node texture appearance --- build/android/native/jni/Android.mk | 1 + doc/lua_api.txt | 30 ++++++++++++- src/client/particles.cpp | 75 ++++++++++++++++++++++++-------- src/client/particles.h | 4 +- src/network/clientpackethandler.cpp | 10 +++++ src/particles.cpp | 10 +++++ src/particles.h | 6 +++ src/script/lua_api/l_particles.cpp | 14 ++++++ src/script/lua_api/l_particles_local.cpp | 14 ++++++ src/server.cpp | 1 + 10 files changed, 145 insertions(+), 20 deletions(-) (limited to 'src/client') diff --git a/build/android/native/jni/Android.mk b/build/android/native/jni/Android.mk index a5cb099e6..140947e6a 100644 --- a/build/android/native/jni/Android.mk +++ b/build/android/native/jni/Android.mk @@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \ ../../../src/noise.cpp \ ../../../src/objdef.cpp \ ../../../src/object_properties.cpp \ + ../../../src/particles.cpp \ ../../../src/pathfinder.cpp \ ../../../src/player.cpp \ ../../../src/porting.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 26061eccb..5b3f61c99 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7835,6 +7835,8 @@ Used by `minetest.add_particle`. size = 1, -- Scales the visual size of the particle texture. + -- If `node` is set, size can be set to 0 to spawn a randomly-sized + -- particle (just like actual node dig particles). collisiondetection = false, -- If true collides with `walkable` nodes and, depending on the @@ -7853,6 +7855,7 @@ Used by `minetest.add_particle`. -- If true faces player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particle only on the player's client @@ -7863,6 +7866,17 @@ Used by `minetest.add_particle`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particle will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } @@ -7892,7 +7906,9 @@ Used by `minetest.add_particlespawner`. maxsize = 1, -- The particles' properties are random values between the min and max -- values. - -- pos, velocity, acceleration, expirationtime, size + -- applies to: pos, velocity, acceleration, expirationtime, size + -- If `node` is set, min and maxsize can be set to 0 to spawn + -- randomly-sized particles (just like actual node dig particles). collisiondetection = false, -- If true collide with `walkable` nodes and, depending on the @@ -7915,6 +7931,7 @@ Used by `minetest.add_particlespawner`. -- If true face player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particles only on the player's client @@ -7925,6 +7942,17 @@ Used by `minetest.add_particlespawner`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particles will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } `HTTPRequest` definition diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c2e751b4f..c78a3e71a 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -304,18 +304,37 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, } pp.expirationtime = random_f32(p.minexptime, p.maxexptime); - pp.size = random_f32(p.minsize, p.maxsize); - p.copyCommon(pp); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = + m_particlemanager->m_env->getGameDef()->ndef()->get(p.node); + if (!ParticleManager::getNodeParticleParams(p.node, f, pp, &texture, + texpos, texsize, &color, p.node_tile)) + return; + } else { + texture = m_texture; + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (p.maxsize > 0.0f) + pp.size = random_f32(p.minsize, p.maxsize); + m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, pp, - m_texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0) + texture, + texpos, + texsize, + color )); } @@ -460,17 +479,35 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, break; } case CE_SPAWN_PARTICLE: { - const ParticleParameters &p = *event->spawn_particle; - video::ITexture *texture = - client->tsrc()->getTextureForMesh(p.texture); + ParticleParameters &p = *event->spawn_particle; - Particle *toadd = new Particle(client, player, m_env, - p, - texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0)); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + f32 oldsize = p.size; + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = m_env->getGameDef()->ndef()->get(p.node); + if (!getNodeParticleParams(p.node, f, p, &texture, texpos, + texsize, &color, p.node_tile)) + texture = nullptr; + } else { + texture = client->tsrc()->getTextureForMesh(p.texture); + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (oldsize > 0.0f) + p.size = oldsize; - addParticle(toadd); + if (texture) { + Particle *toadd = new Particle(client, player, m_env, + p, texture, texpos, texsize, color); + + addParticle(toadd); + } delete event->spawn_particle; break; @@ -480,15 +517,19 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, } bool ParticleManager::getNodeParticleParams(const MapNode &n, - const ContentFeatures &f, ParticleParameters &p, - video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) + const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, + v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum) { // No particles for "airlike" nodes if (f.drawtype == NDT_AIRLIKE) return false; // Texture - u8 texid = rand() % 6; + u8 texid; + if (tilenum > 0 && tilenum <= 6) + texid = tilenum - 1; + else + texid = rand() % 6; const TileLayer &tile = f.tiles[texid].layers[0]; p.animation.type = TAT_NONE; diff --git a/src/client/particles.h b/src/client/particles.h index 7dda0e1b1..2011f0262 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -42,7 +42,7 @@ class Particle : public scene::ISceneNode video::ITexture *texture, v2f texpos, v2f texsize, - video::SColor color = video::SColor(0xFFFFFFFF) + video::SColor color ); ~Particle() = default; @@ -171,7 +171,7 @@ public: protected: static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, v2f &texpos, - v2f &texsize, video::SColor *color); + v2f &texsize, video::SColor *color, u8 tilenum = 0); void addParticle(Particle* toadd); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 054e60c3c..e000acc92 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1003,6 +1003,16 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) p.glow = readU8(is); p.object_collision = readU8(is); + // This is kinda awful + do { + u16 tmp_param0 = readU16(is); + if (is.eof()) + break; + p.node.param0 = tmp_param0; + p.node.param2 = readU8(is); + p.node_tile = readU8(is); + } while (0); + auto event = new ClientEvent(); event->type = CE_ADD_PARTICLESPAWNER; event->add_particlespawner.p = new ParticleSpawnerParameters(p); diff --git a/src/particles.cpp b/src/particles.cpp index 711d189f6..fd81238dc 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -34,6 +34,9 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const animation.serialize(os, 6); /* NOT the protocol ver */ writeU8(os, glow); writeU8(os, object_collision); + writeU16(os, node.param0); + writeU8(os, node.param2); + writeU8(os, node_tile); } void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) @@ -50,4 +53,11 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) animation.deSerialize(is, 6); /* NOT the protocol ver */ glow = readU8(is); object_collision = readU8(is); + // This is kinda awful + u16 tmp_param0 = readU16(is); + if (is.eof()) + return; + node.param0 = tmp_param0; + node.param2 = readU8(is); + node_tile = readU8(is); } diff --git a/src/particles.h b/src/particles.h index 659c1249f..6f518b771 100644 --- a/src/particles.h +++ b/src/particles.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" #include "tileanimation.h" +#include "mapnode.h" // This file defines the particle-related structures that both the server and // client need. The ParticleManager and rendering is in client/particles.h @@ -34,9 +35,12 @@ struct CommonParticleParams { std::string texture; struct TileAnimationParams animation; u8 glow = 0; + MapNode node; + u8 node_tile = 0; CommonParticleParams() { animation.type = TAT_NONE; + node.setContent(CONTENT_IGNORE); } /* This helper is useful for copying params from @@ -49,6 +53,8 @@ struct CommonParticleParams { to.texture = texture; to.animation = animation; to.glow = glow; + to.node = node; + to.node_tile = node_tile; } }; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 7680aa17b..a51c4fe20 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -111,6 +111,13 @@ int ModApiParticles::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + playername = getstringfield_default(L, 1, "playername", ""); } @@ -231,6 +238,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); } u32 id = getServer(L)->addParticleSpawner(p, attached, playername); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index 9595b2fab..cc68b13a5 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -67,6 +67,13 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + ClientEvent *event = new ClientEvent(); event->type = CE_SPAWN_PARTICLE; event->spawn_particle = new ParticleParameters(p); @@ -134,6 +141,13 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); diff --git a/src/server.cpp b/src/server.cpp index 68b0131d4..d6e545498 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1577,6 +1577,7 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, pkt.putRawString(os.str()); } pkt << p.glow << p.object_collision; + pkt << p.node.param0 << p.node.param2 << p.node_tile; Send(&pkt); } -- cgit v1.2.3 From f51cf7c68af16d0068b91d00ab2cc9abdf2b31d0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 24 May 2020 11:48:51 +0200 Subject: Fix two bugs in content_cao fixes #9889 (backface_culling with visual = "cube") fixes #9916 (crash with visual = "upright_sprite") --- src/client/content_cao.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 867bbf2c8..cdc12f041 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -713,6 +713,8 @@ void GenericCAO::addToScene(ITextureSource *tsrc) mesh->drop(); m_meshnode->setScale(m_prop.visual_size); + m_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + m_prop.backface_culling); setSceneNodeMaterial(m_meshnode); } else if (m_prop.visual == "mesh") { @@ -832,19 +834,20 @@ void GenericCAO::setNodeLight(u8 light) } if (m_enable_shaders) { - scene::ISceneNode *node = getSceneNode(); - - if (node == nullptr) - return; - if (m_prop.visual == "upright_sprite") { + if (!m_meshnode) + return; + scene::IMesh *mesh = m_meshnode->getMesh(); for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); - video::SMaterial &material = buf->getMaterial(); - material.EmissiveColor = color; + buf->getMaterial().EmissiveColor = color; } } else { + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + for (u32 i = 0; i < node->getMaterialCount(); ++i) { video::SMaterial &material = node->getMaterial(i); material.EmissiveColor = color; -- cgit v1.2.3 From a9b74f4c3966ad38c2f9a97364d3fdda0e514c93 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 24 May 2020 14:24:13 +0200 Subject: Add chat_font_size setting (#9736) Default font sizes are used when the setting value is 0 or below (clamped by Settings). --- builtin/settingtypes.txt | 4 ++++ src/client/gameui.cpp | 23 +++++++++++++---------- src/defaultsettings.cpp | 4 ++++ src/gui/guiChatConsole.cpp | 4 +++- 4 files changed, 24 insertions(+), 11 deletions(-) (limited to 'src/client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index e18de3382..c787aea2c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -903,6 +903,10 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255 # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf +# Font size of the recent chat text and chat prompt in point (pt). +# Value 0 will use the default font size. +chat_font_size (Chat font size) int 0 + # Path to save screenshots at. Can be an absolute or relative path. # The folder will be created if it doesn't already exist. screenshot_path (Screenshot folder) path screenshots diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index bbe7caeb1..c216f405d 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -76,6 +76,11 @@ void GameUI::init() m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect(0, 0, 0, 0), //false, false); // Disable word wrap as of now false, true, guiroot); + u16 chat_font_size = g_settings->getU16("chat_font_size"); + if (chat_font_size != 0) { + m_guitext_chat->setOverrideFont(g_fontengine->getFont( + chat_font_size, FM_Unspecified)); + } // Profiler text (size is updated when text is updated) m_guitext_profiler = gui::StaticText::add(guienv, L"", @@ -213,7 +218,6 @@ void GameUI::showTranslatedStatusText(const char *str) void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) { - setStaticText(m_guitext_chat, chat_text); // Update gui element size and position s32 chat_y = 5; @@ -221,16 +225,15 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) if (m_flags.show_debug) chat_y += 2 * g_fontengine->getLineHeight(); - // first pass to calculate height of text to be set const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); - s32 width = std::min(g_fontengine->getTextWidth(chat_text.c_str()) + 10, - window_size.X - 20); - m_guitext_chat->setRelativePosition(core::rect(10, chat_y, width, - chat_y + window_size.Y)); - - // now use real height of text and adjust rect according to this size - m_guitext_chat->setRelativePosition(core::rect(10, chat_y, width, - chat_y + m_guitext_chat->getTextHeight())); + + core::rect chat_size(10, chat_y, + window_size.X - 20, 0); + chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y, + m_guitext_chat->getTextHeight() + chat_y); + + m_guitext_chat->setRelativePosition(chat_size); + setStaticText(m_guitext_chat, chat_text); m_recent_chat_count = recent_chat_count; } diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1d0610c0f..5d1795003 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -321,8 +321,12 @@ void set_default_settings(Settings *settings) std::string font_size_str = std::to_string(DEFAULT_FONT_SIZE); #endif + // General font settings settings->setDefault("font_size", font_size_str); settings->setDefault("mono_font_size", font_size_str); + settings->setDefault("chat_font_size", "0"); // Default "font_size" + + // ContentDB settings->setDefault("contentdb_url", "https://content.minetest.net"); #ifdef __ANDROID__ settings->setDefault("contentdb_flag_blacklist", "nonfree, android_default"); diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index e67fae3c6..8de00c12f 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -74,7 +74,9 @@ GUIChatConsole::GUIChatConsole( m_background_color.setBlue(clamp_u8(myround(console_color.Z))); } - m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); + u16 chat_font_size = g_settings->getU16("chat_font_size"); + m_font = g_fontengine->getFont(chat_font_size != 0 ? + chat_font_size : FONT_SIZE_UNSPECIFIED, FM_Mono); if (!m_font) { errorstream << "GUIChatConsole: Unable to load mono font" << std::endl; -- cgit v1.2.3 From 5311a275235d2132443643199330953c877b794a Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 25 May 2020 19:10:23 +0200 Subject: Silence GCC warning in mapblock_mesh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes following warning: warning: ‘waving’ may be used uninitialized in this function [-Wmaybe-uninitialized] --- src/client/mapblock_mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 0a1619b3f..1020e35f5 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -888,7 +888,7 @@ static void updateFastFaceRow( v3s16 p_corrected; v3s16 face_dir_corrected; u16 lights[4] = {0, 0, 0, 0}; - u8 waving; + u8 waving = 0; TileSpec tile; // Get info of first tile -- cgit v1.2.3 From 471e567657dfd75a994a1b54d7a23cf4541a6bed Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 26 May 2020 17:38:31 +0200 Subject: Value copy / allocation optimizations mostly in server, SAO and serialize code --- src/client/game.cpp | 2 +- src/client/sky.cpp | 4 ++-- src/client/sky.h | 2 +- src/content/mods.cpp | 2 +- src/content/mods.h | 2 +- src/script/cpp_api/s_node.cpp | 2 +- src/script/cpp_api/s_node.h | 2 +- src/server.cpp | 43 ++++++++++++++++++--------------------- src/server/luaentity_sao.cpp | 15 +++++--------- src/server/player_sao.cpp | 7 ++----- src/server/serveractiveobject.cpp | 2 +- src/server/serverinventorymgr.h | 2 +- src/serverenvironment.cpp | 8 ++++---- src/serverenvironment.h | 4 ++-- src/tool.cpp | 2 +- src/util/serialize.cpp | 17 ++++++++-------- 16 files changed, 52 insertions(+), 64 deletions(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index e7663a113..cdf4da21e 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2806,7 +2806,7 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Shows the mesh skybox sky->setVisible(true); // Update mesh based skybox colours if applicable. - sky->setSkyColors(*event->set_sky); + sky->setSkyColors(event->set_sky->sky_color); sky->setHorizonTint( event->set_sky->fog_sun_tint, event->set_sky->fog_moon_tint, diff --git a/src/client/sky.cpp b/src/client/sky.cpp index d21b56fcc..2e0cbca86 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -907,9 +907,9 @@ void Sky::setStarCount(u16 star_count, bool force_update) } } -void Sky::setSkyColors(const SkyboxParams sky) +void Sky::setSkyColors(const SkyColor &sky_color) { - m_sky_params.sky_color = sky.sky_color; + m_sky_params.sky_color = sky_color; } void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, diff --git a/src/client/sky.h b/src/client/sky.h index 8637f96d4..3227e8f59 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -94,7 +94,7 @@ public: m_bgcolor = bgcolor; m_skycolor = skycolor; } - void setSkyColors(const SkyboxParams sky); + void setSkyColors(const SkyColor &sky_color); void setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint); void setInClouds(bool clouds) { m_in_clouds = clouds; } diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 676666f78..95ab0290a 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -167,7 +167,7 @@ std::map getModsInPath( return result; } -std::vector flattenMods(std::map mods) +std::vector flattenMods(const std::map &mods) { std::vector result; for (const auto &it : mods) { diff --git a/src/content/mods.h b/src/content/mods.h index 6e2506dbf..b3500fbc8 100644 --- a/src/content/mods.h +++ b/src/content/mods.h @@ -68,7 +68,7 @@ std::map getModsInPath( const std::string &path, bool part_of_modpack = false); // replaces modpack Modspecs with their content -std::vector flattenMods(std::map mods); +std::vector flattenMods(const std::map &mods); // a ModConfiguration is a subset of installed mods, expected to have // all dependencies fullfilled, so it can be used as a list of mods to diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index d93a4c3ad..e0f9bcd78 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -94,7 +94,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] = }; bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, - ServerActiveObject *puncher, PointedThing pointed) + ServerActiveObject *puncher, const PointedThing &pointed) { SCRIPTAPI_PRECHECKHEADER diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index e7c0c01d1..81b44f0f0 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -36,7 +36,7 @@ public: virtual ~ScriptApiNode() = default; bool node_on_punch(v3s16 p, MapNode node, - ServerActiveObject *puncher, PointedThing pointed); + ServerActiveObject *puncher, const PointedThing &pointed); bool node_on_dig(v3s16 p, MapNode node, ServerActiveObject *digger); void node_on_construct(v3s16 p, MapNode node); diff --git a/src/server.cpp b/src/server.cpp index 8c62584c8..6ecbd7097 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -718,34 +718,35 @@ void Server::AsyncRunStep(bool initial_step) std::unordered_map*> buffered_messages; // Get active object messages from environment + ActiveObjectMessage aom(0); + u32 aom_count = 0; for(;;) { - ActiveObjectMessage aom = m_env->getActiveObjectMessage(); - if (aom.id == 0) + if (!m_env->getActiveObjectMessage(&aom)) break; std::vector* message_list = nullptr; - std::unordered_map* >::iterator n; - n = buffered_messages.find(aom.id); + auto n = buffered_messages.find(aom.id); if (n == buffered_messages.end()) { message_list = new std::vector; buffered_messages[aom.id] = message_list; - } - else { + } else { message_list = n->second; } - message_list->push_back(aom); + message_list->push_back(std::move(aom)); + aom_count++; } - m_aom_buffer_counter->increment(buffered_messages.size()); + m_aom_buffer_counter->increment(aom_count); m_clients.lock(); const RemoteClientMap &clients = m_clients.getClientList(); // Route data to every client + std::string reliable_data, unreliable_data; for (const auto &client_it : clients) { + reliable_data.clear(); + unreliable_data.clear(); RemoteClient *client = client_it.second; PlayerSAO *player = getPlayerSAO(client->peer_id); - std::string reliable_data; - std::string unreliable_data; // Go through all objects in message buffer for (const auto &buffered_message : buffered_messages) { // If object does not exist or is not known by client, skip it @@ -770,19 +771,15 @@ void Server::AsyncRunStep(bool initial_step) client->m_known_objects.end()) continue; } - // Compose the full new data with header - std::string new_data; - // Add object id - char buf[2]; - writeU16((u8*)&buf[0], aom.id); - new_data.append(buf, 2); - // Add data - new_data += serializeString(aom.datastring); - // Add data to buffer - if (aom.reliable) - reliable_data += new_data; - else - unreliable_data += new_data; + + // Add full new data to appropriate buffer + std::string &buffer = aom.reliable ? reliable_data : unreliable_data; + char idbuf[2]; + writeU16((u8*) idbuf, aom.id); + // u16 id + // std::string data + buffer.append(idbuf, sizeof(idbuf)); + buffer.append(serializeString(aom.datastring)); } } /* diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 51e1ca90e..8174da265 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -119,8 +119,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } // If attached, check that our parent is still there. If it isn't, detach. @@ -228,16 +227,14 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_animation_sent = true; std::string str = generateUpdateAnimationCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } if (!m_animation_speed_sent) { m_animation_speed_sent = true; std::string str = generateUpdateAnimationSpeedCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } if (!m_bone_position_sent) { @@ -247,8 +244,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } @@ -256,8 +252,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_attachment_sent = true; std::string str = generateUpdateAttachmentCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index a4d0f4ce7..3ea3536e2 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -223,8 +223,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); m_env->getScriptIface()->player_event(this, "properties_changed"); } @@ -324,10 +323,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); } } diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index 8345ebd47..fdcb13bd8 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -75,7 +75,7 @@ std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const vid void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) { while (!m_messages_out.empty()) { - queue.push(m_messages_out.front()); + queue.push(std::move(m_messages_out.front())); m_messages_out.pop(); } } \ No newline at end of file diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h index d0aac4dae..ccf6d3b2e 100644 --- a/src/server/serverinventorymgr.h +++ b/src/server/serverinventorymgr.h @@ -57,4 +57,4 @@ private: ServerEnvironment *m_env = nullptr; std::unordered_map m_detached_inventories; -}; \ No newline at end of file +}; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index d485c32e8..222b4d203 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1603,14 +1603,14 @@ void ServerEnvironment::setStaticForActiveObjectsInBlock( } } -ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() +bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest) { if(m_active_object_messages.empty()) - return ActiveObjectMessage(0); + return false; - ActiveObjectMessage message = m_active_object_messages.front(); + *dest = std::move(m_active_object_messages.front()); m_active_object_messages.pop(); - return message; + return true; } void ServerEnvironment::getSelectedActiveObjects( diff --git a/src/serverenvironment.h b/src/serverenvironment.h index e2f1a3784..4b453d39a 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -289,9 +289,9 @@ public: /* Get the next message emitted by some active object. - Returns a message with id=0 if no messages are available. + Returns false if no messages are available, true otherwise. */ - ActiveObjectMessage getActiveObjectMessage(); + bool getActiveObjectMessage(ActiveObjectMessage *dest); virtual void getSelectedActiveObjects( const core::line3d &shootline_on_map, diff --git a/src/tool.cpp b/src/tool.cpp index d911c518f..22e41d28e 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -130,7 +130,7 @@ void ToolCapabilities::serializeJson(std::ostream &os) const root["punch_attack_uses"] = punch_attack_uses; Json::Value groupcaps_object; - for (auto groupcap : groupcaps) { + for (const auto &groupcap : groupcaps) { groupcap.second.toJson(groupcaps_object[groupcap.first]); } root["groupcaps"] = groupcaps_object; diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index f0e177d57..5b276668d 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -110,6 +110,7 @@ std::string serializeString(const std::string &plain) if (plain.size() > STRING_MAX_LEN) throw SerializationError("String too long for serializeString"); + s.reserve(2 + plain.size()); writeU16((u8 *)&buf[0], plain.size()); s.append(buf, 2); @@ -131,13 +132,11 @@ std::string deSerializeString(std::istream &is) if (s_size == 0) return s; - Buffer buf2(s_size); - is.read(&buf2[0], s_size); + s.resize(s_size); + is.read(&s[0], s_size); if (is.gcount() != s_size) throw SerializationError("deSerializeString: couldn't read all chars"); - s.reserve(s_size); - s.append(&buf2[0], s_size); return s; } @@ -152,6 +151,7 @@ std::string serializeWideString(const std::wstring &plain) 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); @@ -196,13 +196,14 @@ std::wstring deSerializeWideString(std::istream &is) std::string serializeLongString(const std::string &plain) { + std::string s; char buf[4]; if (plain.size() > LONG_STRING_MAX_LEN) throw SerializationError("String too long for serializeLongString"); + s.reserve(4 + plain.size()); writeU32((u8*)&buf[0], plain.size()); - std::string s; s.append(buf, 4); s.append(plain); return s; @@ -227,13 +228,11 @@ std::string deSerializeLongString(std::istream &is) "string too long: " + itos(s_size) + " bytes"); } - Buffer buf2(s_size); - is.read(&buf2[0], s_size); + s.resize(s_size); + is.read(&s[0], s_size); if ((u32)is.gcount() != s_size) throw SerializationError("deSerializeLongString: couldn't read all chars"); - s.reserve(s_size); - s.append(&buf2[0], s_size); return s; } -- cgit v1.2.3 From 34862a644256f2717923de6d35288114a84acd19 Mon Sep 17 00:00:00 2001 From: MoNTE48 Date: Wed, 11 Dec 2019 20:03:12 +0100 Subject: Add disable_jump check for the player's feet --- doc/lua_api.txt | 1 + games/devtest/mods/testnodes/properties.lua | 11 +++++++++-- src/client/localplayer.cpp | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bb9df5373..8c81b7020 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1666,6 +1666,7 @@ to games. * `2`: the node always gets the digging time 0.5 seconds (rail, sign) * `3`: the node always gets the digging time 0 seconds (torch) * `disable_jump`: Player (and possibly other things) cannot jump from node + or if their feet are in the node. Note: not supported for `new_move = false` * `fall_damage_add_percent`: damage speed = `speed * (1 + value/100)` * `falling_node`: if there is no walkable block under the node it will fall * `float`: the node will not fall through liquids diff --git a/games/devtest/mods/testnodes/properties.lua b/games/devtest/mods/testnodes/properties.lua index e169d4b08..01846a5f0 100644 --- a/games/devtest/mods/testnodes/properties.lua +++ b/games/devtest/mods/testnodes/properties.lua @@ -56,11 +56,18 @@ minetest.register_node("testnodes:attached_wallmounted", { minetest.register_node("testnodes:nojump", { description = S("Non-jumping Node"), groups = {disable_jump=1, dig_immediate=3}, - - tiles = {"testnodes_nojump_top.png", "testnodes_nojump_side.png"}, }) +-- Jump disabled plant +minetest.register_node("testnodes:nojump_walkable", { + description = S("Non-jumping Plant Node"), + drawtype = "plantlike", + groups = {disable_jump=1, dig_immediate=3}, + walkable = false, + tiles = {"testnodes_nojump_top.png"}, +}) + -- Climbable up and down with jump and sneak keys minetest.register_node("testnodes:climbable", { description = S("Climbable Node"), diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index c20c3619f..011898bcf 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -436,9 +436,11 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, Check properties of the node on which the player is standing */ const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node)); + const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0))); // Determine if jumping is possible - m_disable_jump = itemgroup_get(f.groups, "disable_jump"); + m_disable_jump = itemgroup_get(f.groups, "disable_jump") || + itemgroup_get(f1.groups, "disable_jump"); m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump; // Jump key pressed while jumping off from a bouncy block -- cgit v1.2.3 From db7c262ee85c5bcae68354280848218dbf14bf55 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 25 May 2020 23:36:45 +0200 Subject: content_cao: Do not expire visuals when not necessary fixes #6572 --- src/client/content_cao.cpp | 42 ++++++++++++++++++++++++++++++++---------- src/client/content_cao.h | 3 ++- 2 files changed, 34 insertions(+), 11 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index cdc12f041..947c1a279 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1457,13 +1457,23 @@ void GenericCAO::updateAttachments() } } -void GenericCAO::readAOMessageProperties(std::istream &is) +bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const { - // Reset object properties first - m_prop = ObjectProperties(); - - // Then read the whole new stream - m_prop.deSerialize(is); + const ObjectProperties &old = m_prop; + // Ordered to compare primitive types before std::vectors + return old.backface_culling != new_.backface_culling || + old.initial_sprite_basepos != new_.initial_sprite_basepos || + old.is_visible != new_.is_visible || + old.mesh != new_.mesh || + old.nametag != new_.nametag || + old.nametag_color != new_.nametag_color || + old.spritediv != new_.spritediv || + old.use_texture_alpha != new_.use_texture_alpha || + old.visual != new_.visual || + old.visual_size != new_.visual_size || + old.wield_item != new_.wield_item || + old.colors != new_.colors || + old.textures != new_.textures; } void GenericCAO::processMessage(const std::string &data) @@ -1473,14 +1483,21 @@ void GenericCAO::processMessage(const std::string &data) // command u8 cmd = readU8(is); if (cmd == AO_CMD_SET_PROPERTIES) { - readAOMessageProperties(is); + ObjectProperties newprops; + newprops.deSerialize(is); + + // Check what exactly changed + bool expire_visuals = visualExpiryRequired(newprops); + + // Apply changes + m_prop = std::move(newprops); m_selection_box = m_prop.selectionbox; m_selection_box.MinEdge *= BS; m_selection_box.MaxEdge *= BS; - m_tx_size.X = 1.0 / m_prop.spritediv.X; - m_tx_size.Y = 1.0 / m_prop.spritediv.Y; + m_tx_size.X = 1.0f / m_prop.spritediv.X; + m_tx_size.Y = 1.0f / m_prop.spritediv.Y; if(!m_initial_tx_basepos_set){ m_initial_tx_basepos_set = true; @@ -1500,7 +1517,12 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; - expireVisuals(); + if (expire_visuals) { + expireVisuals(); + } else { + infostream << "GenericCAO: properties updated but expiring visuals" + << " not necessary" << std::endl; + } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. // We might however get here if the server notices the object being detached before the client. diff --git a/src/client/content_cao.h b/src/client/content_cao.h index c53b81433..03a355204 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -68,7 +68,6 @@ struct SmoothTranslatorWrappedv3f : SmoothTranslator class GenericCAO : public ClientActiveObject { private: - void readAOMessageProperties(std::istream &is); // Only set at initialization std::string m_name = ""; bool m_is_player = false; @@ -131,6 +130,8 @@ private: // Settings bool m_enable_shaders = false; + bool visualExpiryRequired(const ObjectProperties &newprops) const; + public: GenericCAO(Client *client, ClientEnvironment *env); -- cgit v1.2.3 From 4c8e1c320054ee0dc5d8ec821a6b4cd69002aa09 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 26 May 2020 16:05:06 +0200 Subject: Clean up CAO nametag handling and remove deprecated AO_CMD AO_CMD_UPDATE_NAMETAG_ATTRIBUTES was deprecated in 9eee3c3f465c071bb9908749cf48be3c131a1bdf (0.4.14) --- src/activeobject.h | 3 ++- src/client/content_cao.cpp | 57 ++++++++++++++++++++++++--------------- src/client/content_cao.h | 2 ++ src/server/player_sao.cpp | 4 +-- src/server/serveractiveobject.cpp | 13 +-------- 5 files changed, 42 insertions(+), 37 deletions(-) (limited to 'src/client') diff --git a/src/activeobject.h b/src/activeobject.h index c83243f86..85e160d10 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -66,7 +66,8 @@ enum ActiveObjectCommand { AO_CMD_SET_BONE_POSITION, AO_CMD_ATTACH_TO, AO_CMD_SET_PHYSICS_OVERRIDE, - AO_CMD_UPDATE_NAMETAG_ATTRIBUTES, + AO_CMD_OBSOLETE1, + // ^ UPDATE_NAMETAG_ATTRIBUTES deprecated since 0.4.14, removed in 5.3.0 AO_CMD_SPAWN_INFANT, AO_CMD_SET_ANIMATION_SPEED }; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 947c1a279..dde31899b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -776,15 +776,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) if (node && m_matrixnode) node->setParent(m_matrixnode); - if (node && !m_prop.nametag.empty() && !m_is_local_player) { - // Add nametag - v3f pos; - pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f; - m_nametag = m_client->getCamera()->addNametag(node, - m_prop.nametag, m_prop.nametag_color, - pos); - } - + updateNametag(); updateNodePos(); updateAnimation(); updateBonePosition(); @@ -872,6 +864,38 @@ v3s16 GenericCAO::getLightPosition() return floatToInt(m_position, BS); } +void GenericCAO::updateNametag() +{ + if (m_is_local_player) // No nametag for local player + return; + + if (m_prop.nametag.empty()) { + // Delete nametag + if (m_nametag) { + m_client->getCamera()->removeNametag(m_nametag); + m_nametag = nullptr; + } + return; + } + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + + v3f pos; + pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f; + if (!m_nametag) { + // Add nametag + m_nametag = m_client->getCamera()->addNametag(node, + m_prop.nametag, m_prop.nametag_color, pos); + } else { + // Update nametag + m_nametag->nametag_text = m_prop.nametag; + m_nametag->nametag_color = m_prop.nametag_color; + m_nametag->nametag_pos = pos; + } +} + void GenericCAO::updateNodePos() { if (getParent() != NULL) @@ -1465,8 +1489,6 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const old.initial_sprite_basepos != new_.initial_sprite_basepos || old.is_visible != new_.is_visible || old.mesh != new_.mesh || - old.nametag != new_.nametag || - old.nametag_color != new_.nametag_color || old.spritediv != new_.spritediv || old.use_texture_alpha != new_.use_texture_alpha || old.visual != new_.visual || @@ -1516,6 +1538,7 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + updateNametag(); if (expire_visuals) { expireVisuals(); @@ -1694,22 +1717,14 @@ void GenericCAO::processMessage(const std::string &data) int rating = readS16(is); m_armor_groups[name] = rating; } - } else if (cmd == AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) { - // Deprecated, for backwards compatibility only. - readU8(is); // version - m_prop.nametag_color = readARGB8(is); - if (m_nametag != NULL) { - m_nametag->nametag_color = m_prop.nametag_color; - v3f pos; - pos.Y = m_prop.collisionbox.MaxEdge.Y + 0.3f; - m_nametag->nametag_pos = pos; - } } else if (cmd == AO_CMD_SPAWN_INFANT) { u16 child_id = readU16(is); u8 type = readU8(is); // maybe this will be useful later (void)type; addAttachmentChild(child_id); + } else if (cmd == AO_CMD_OBSOLETE1) { + // Don't do anything and also don't log a warning } else { warningstream << FUNCTION_NAME << ": unknown command or outdated client \"" diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 03a355204..8e2a13ea8 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -244,6 +244,8 @@ public: v3s16 getLightPosition(); + void updateNametag(); + void updateNodePos(); void step(float dtime, ClientEnvironment *env); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 3ea3536e2..9ea0743f7 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -127,9 +127,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) } msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 - // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); + int message_count = 5 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); ii != m_attachment_child_ids.end(); ++ii) { if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index fdcb13bd8..dbf25e3bc 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -61,21 +61,10 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p return os.str(); } -std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); - // parameters - writeU8(os, 1); // version for forward compatibility - writeARGB8(os, color); - return os.str(); -} - void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) { while (!m_messages_out.empty()) { queue.push(std::move(m_messages_out.front())); m_messages_out.pop(); } -} \ No newline at end of file +} -- cgit v1.2.3 From f849917bbe9c5eff51bee0a3125eabed16efb172 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Mon, 1 Jun 2020 09:22:04 -0400 Subject: imageScaleNNAA: Fix image clipping on rect- instead of image dimensions (#9896) Fixes GUI scaling filters applied on animated images and 9slice backgrounds. --- src/client/imagefilters.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client') diff --git a/src/client/imagefilters.cpp b/src/client/imagefilters.cpp index dd029628f..0fa501410 100644 --- a/src/client/imagefilters.cpp +++ b/src/client/imagefilters.cpp @@ -91,7 +91,7 @@ void imageScaleNNAA(video::IImage *src, const core::rect &srcrect, video::I u32 dy, dx; video::SColor pxl; - // Cache rectsngle boundaries. + // Cache rectangle boundaries. double sox = srcrect.UpperLeftCorner.X * 1.0; double soy = srcrect.UpperLeftCorner.Y * 1.0; double sw = srcrect.getWidth() * 1.0; @@ -107,15 +107,15 @@ void imageScaleNNAA(video::IImage *src, const core::rect &srcrect, video::I // Do some basic clipping, and for mirrored/flipped rects, // make sure min/max are in the right order. minsx = sox + (dx * sw / dim.Width); - minsx = rangelim(minsx, 0, sw); + minsx = rangelim(minsx, 0, sox + sw); maxsx = minsx + sw / dim.Width; - maxsx = rangelim(maxsx, 0, sw); + maxsx = rangelim(maxsx, 0, sox + sw); if (minsx > maxsx) SWAP(double, minsx, maxsx); minsy = soy + (dy * sh / dim.Height); - minsy = rangelim(minsy, 0, sh); + minsy = rangelim(minsy, 0, soy + sh); maxsy = minsy + sh / dim.Height; - maxsy = rangelim(maxsy, 0, sh); + maxsy = rangelim(maxsy, 0, soy + sh); if (minsy > maxsy) SWAP(double, minsy, maxsy); -- cgit v1.2.3 From a08d18acad345363780f5286300d65b39ea9c9f9 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 1 Jun 2020 19:01:47 +0200 Subject: ContentCAO: Update light of all attached entities (#9975) --- src/client/clientenvironment.cpp | 30 +++------------------------- src/client/clientobject.h | 6 +++--- src/client/content_cao.cpp | 42 ++++++++++++++-------------------------- src/client/content_cao.h | 4 +--- 4 files changed, 22 insertions(+), 60 deletions(-) (limited to 'src/client') diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 6840f2db3..44ea1e4a1 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -320,21 +320,8 @@ void ClientEnvironment::step(float dtime) // Step object cao->step(dtime, this); - if (update_lighting) { - // Update lighting - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = cao->getLightPosition(); - MapNode n = this->m_map->getNode(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_client->ndef()); - else - light = blend_light(day_night_ratio, LIGHT_SUN, 0); - - cao->updateLight(light); - } + if (update_lighting) + cao->updateLight(day_night_ratio); }; m_ao_manager.step(dtime, cb_state); @@ -402,18 +389,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) object->addToScene(m_texturesource); // Update lighting immediately - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = object->getLightPosition(); - MapNode n = m_map->getNode(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_client->ndef()); - else - light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); - - object->updateLight(light); + object->updateLight(getDayNightRatio()); return object->getId(); } diff --git a/src/client/clientobject.h b/src/client/clientobject.h index 12e0db35b..8e64b8406 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -41,10 +41,10 @@ public: virtual void addToScene(ITextureSource *tsrc) {} virtual void removeFromScene(bool permanent) {} - // 0 <= light_at_pos <= LIGHT_SUN - virtual void updateLight(u8 light_at_pos) {} - virtual void updateLightNoCheck(u8 light_at_pos) {} + + virtual void updateLight(u32 day_night_ratio) {} virtual v3s16 getLightPosition() { return v3s16(0, 0, 0); } + virtual bool getCollisionBox(aabb3f *toset) const { return false; } virtual bool getSelectionBox(aabb3f *toset) const { return false; } virtual bool collideWithObjects() const { return false; } diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index dde31899b..d0682e51e 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -181,7 +181,7 @@ public: void addToScene(ITextureSource *tsrc); void removeFromScene(bool permanent); - void updateLight(u8 light_at_pos); + void updateLight(u32 day_night_ratio); v3s16 getLightPosition(); void updateNodePos(); @@ -254,7 +254,7 @@ void TestCAO::removeFromScene(bool permanent) m_node = NULL; } -void TestCAO::updateLight(u8 light_at_pos) +void TestCAO::updateLight(u32 day_night_ratio) { } @@ -784,34 +784,22 @@ void GenericCAO::addToScene(ITextureSource *tsrc) setNodeLight(m_last_light); } -void GenericCAO::updateLight(u8 light_at_pos) +void GenericCAO::updateLight(u32 day_night_ratio) { - // Don't update light of attached one - if (getParent() != NULL) { - return; - } - - updateLightNoCheck(light_at_pos); - - // Update light of all children - for (u16 i : m_attachment_child_ids) { - ClientActiveObject *obj = m_env->getActiveObject(i); - if (obj) { - obj->updateLightNoCheck(light_at_pos); - } - } -} - -void GenericCAO::updateLightNoCheck(u8 light_at_pos) -{ - if (m_glow < 0) - return; + u8 light_at_pos = 0; + bool pos_ok; - u8 li = decode_light(light_at_pos + m_glow); + v3s16 p = getLightPosition(); + MapNode n = m_env->getMap().getNode(p, &pos_ok); + if (pos_ok) + light_at_pos = n.getLightBlend(day_night_ratio, m_client->ndef()); + else + light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); - if (li != m_last_light) { - m_last_light = li; - setNodeLight(li); + u8 light = decode_light(light_at_pos); + if (light != m_last_light) { + m_last_light = light; + setNodeLight(light); } } diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 8e2a13ea8..7693dd3d2 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -236,9 +236,7 @@ public: m_visuals_expired = true; } - void updateLight(u8 light_at_pos); - - void updateLightNoCheck(u8 light_at_pos); + void updateLight(u32 day_night_ratio); void setNodeLight(u8 light); -- cgit v1.2.3 From c1e01bc638637efa788b5698238a465406bc3f5e Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 4 Jun 2020 19:31:46 +0200 Subject: Move shared parameters sending to UnitSAO (#9968) Better header sorting by topic Make UnitSAO-specific parameters private Skip redundant recursive entity sending code (since ~5.2.0) --- src/client/content_cao.cpp | 1 + src/server/luaentity_sao.cpp | 71 ++++++++++----------------------------- src/server/player_sao.cpp | 57 +++++++++---------------------- src/server/serveractiveobject.cpp | 7 +++- src/server/unit_sao.cpp | 33 ++++++++++++++++++ src/server/unit_sao.h | 56 +++++++++++++++++++----------- 6 files changed, 110 insertions(+), 115 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d0682e51e..702d089af 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -475,6 +475,7 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit parent->addAttachmentChild(m_id); } + updateAttachments(); } diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 8174da265..d504c42ca 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -123,12 +123,11 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } // If attached, check that our parent is still there. If it isn't, detach. - if(m_attachment_parent_id && !isAttached()) - { - m_attachment_parent_id = 0; - m_attachment_bone = ""; - m_attachment_position = v3f(0,0,0); - m_attachment_rotation = v3f(0,0,0); + if (m_attachment_parent_id && !isAttached()) { + // This is handled when objects are removed from the map + warningstream << "LuaEntitySAO::step() id=" << m_id << + " is attached to nonexistent parent. This is a bug." << std::endl; + clearParentAttachment(); sendPosition(false, true); } @@ -217,43 +216,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } } - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - std::string str = generateUpdateAnimationCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - - if (!m_animation_speed_sent) { - m_animation_speed_sent = true; - std::string str = generateUpdateAnimationSpeedCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } + sendOutdatedData(); } std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) @@ -273,20 +236,19 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) msg_os << serializeLongString(getPropertyPacket()); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + 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 << serializeLongString(generateUpdateAttachmentCommand()); // 4 + int message_count = 4 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - (ii != m_attachment_child_ids.end()); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + + for (const auto &id : getAttachmentChildIds()) { + if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - // TODO after a protocol bump: only send the object initialization data - // to older clients (superfluous since this message exists) - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + msg_os << serializeLongString(obj->generateUpdateInfantCommand( + id, protocol_version)); } } @@ -294,7 +256,8 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) message_count++; writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + std::string serialized = msg_os.str(); + os.write(serialized.c_str(), serialized.size()); // return result return os.str(); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 9ea0743f7..67efed210 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -120,24 +120,26 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) msg_os << serializeLongString(getPropertyPacket()); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + 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 << serializeLongString(generateUpdateAttachmentCommand()); // 4 msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + int message_count = 5 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + + for (const auto &id : getAttachmentChildIds()) { + if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + msg_os << serializeLongString(obj->generateUpdateInfantCommand( + id, protocol_version)); } } writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + std::string serialized = msg_os.str(); + os.write(serialized.c_str(), serialized.size()); // return result return os.str(); @@ -227,10 +229,10 @@ void PlayerSAO::step(float dtime, bool send_recommended) // If attached, check that our parent is still there. If it isn't, detach. if (m_attachment_parent_id && !isAttached()) { - m_attachment_parent_id = 0; - m_attachment_bone = ""; - m_attachment_position = v3f(0.0f, 0.0f, 0.0f); - m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); + // This is handled when objects are removed from the map + warningstream << "PlayerSAO::step() id=" << m_id << + " is attached to nonexistent parent. This is a bug." << std::endl; + clearParentAttachment(); setBasePosition(m_last_good_position); m_env->getGameDef()->SendMovePlayer(m_peer_id); } @@ -290,40 +292,13 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_messages_out.emplace(getId(), false, str); } - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - if (!m_physics_override_sent) { m_physics_override_sent = true; // create message and add to list m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); } - if (!m_animation_sent) { - m_animation_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); - } + sendOutdatedData(); } std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index dbf25e3bc..3341dc008 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -57,7 +57,12 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p // parameters writeU16(os, infant_id); writeU8(os, getSendType()); - os << serializeLongString(getClientInitializationData(protocol_version)); + if (protocol_version < 38) { + // 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)); + } return os.str(); } diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index 74b0508b8..ef0e87f2c 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -88,6 +88,39 @@ void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotat *rotation = m_bone_position[bone].Y; } +// clang-format off +void UnitSAO::sendOutdatedData() +{ + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + m_animation_speed_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + } else if (!m_animation_speed_sent) { + // Animation speed is also sent when 'm_animation_sent == false' + m_animation_speed_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand()); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (const auto &bone_pos : m_bone_position) { + m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); + } +} +// clang-format on + void UnitSAO::setAttachment( int parent_id, const std::string &bone, v3f position, v3f rotation) { diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index c73115855..3cb7f0ad5 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -29,6 +29,11 @@ public: UnitSAO(ServerEnvironment *env, v3f pos); virtual ~UnitSAO() = default; + u16 getHP() const { return m_hp; } + // Use a function, if isDead can be defined by other conditions + bool isDead() const { return m_hp == 0; } + + // Rotation void setRotation(v3f rotation) { m_rotation = rotation; } const v3f &getRotation() const { return m_rotation; } v3f getRadRotation() { return m_rotation * core::DEGTORAD; } @@ -36,26 +41,28 @@ public: // Deprecated f32 getRadYawDep() const { return (m_rotation.Y + 90.) * core::DEGTORAD; } - u16 getHP() const { return m_hp; } - // Use a function, if isDead can be defined by other conditions - bool isDead() const { return m_hp == 0; } - - inline bool isAttached() const { return getParent(); } - + // Armor groups inline bool isImmortal() const { return itemgroup_get(getArmorGroups(), "immortal"); } - void setArmorGroups(const ItemGroupList &armor_groups); const ItemGroupList &getArmorGroups() const; + + // Animation void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); void setAnimationSpeed(float frame_speed); + + // Bone position void setBonePosition(const std::string &bone, v3f position, v3f rotation); void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + + // Attachments + ServerActiveObject *getParent() const; + inline bool isAttached() const { return getParent(); } void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); void getAttachment(int *parent_id, std::string *bone, v3f *position, @@ -65,10 +72,13 @@ public: void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); const std::unordered_set &getAttachmentChildIds() const; - ServerActiveObject *getParent() const; + + // Object properties ObjectProperties *accessObjectProperties(); void notifyObjectPropertiesModified(); + void sendOutdatedData(); + // Update packets std::string generateUpdateAttachmentCommand() const; std::string generateUpdateAnimationSpeedCommand() const; std::string generateUpdateAnimationCommand() const; @@ -77,21 +87,36 @@ public: const v3f &velocity, const v3f &acceleration, const v3f &rotation, bool do_interpolate, bool is_movement_end, f32 update_interval); std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - void sendPunchCommand(); static std::string generateUpdateBonePositionCommand(const std::string &bone, const v3f &position, const v3f &rotation); + void sendPunchCommand(); protected: u16 m_hp = 1; v3f m_rotation; + ItemGroupList m_armor_groups; + + // Object properties bool m_properties_sent = true; ObjectProperties m_prop; - ItemGroupList m_armor_groups; + // Stores position and rotation for each bone name + std::unordered_map> m_bone_position; + + int m_attachment_parent_id = 0; + +private: + void onAttach(int parent_id); + void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; + + // Armor groups bool m_armor_groups_sent = false; + // Animation v2f m_animation_range; float m_animation_speed = 0.0f; float m_animation_blend = 0.0f; @@ -99,20 +124,13 @@ protected: bool m_animation_sent = false; bool m_animation_speed_sent = false; - // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; + // Bone positions bool m_bone_position_sent = false; - int m_attachment_parent_id = 0; + // Attachments std::unordered_set m_attachment_child_ids; std::string m_attachment_bone = ""; v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; - -private: - void onAttach(int parent_id); - void onDetach(int parent_id); - - std::string generatePunchCommand(u16 result_hp) const; }; -- cgit v1.2.3 From 7ec0e3df35a11b66b48eababf0123170f2453a50 Mon Sep 17 00:00:00 2001 From: Maksim Date: Fri, 5 Jun 2020 21:42:33 +0200 Subject: Fix HUD scaling (#9721) This resolves HUD scaling issues on Android and desktops with custom DPI settings. --- src/client/hud.cpp | 65 ++++++++++++++++++++++++++++++++++++------------------ src/client/hud.h | 1 + 2 files changed, 44 insertions(+), 22 deletions(-) (limited to 'src/client') diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 4edc229b2..31e633bc2 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -51,6 +51,7 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, this->inventory = inventory; m_hud_scaling = g_settings->getFloat("hud_scaling"); + m_scale_factor = m_hud_scaling * RenderingEngine::getDisplayDensity(); m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE * RenderingEngine::getDisplayDensity() + 0.5f); m_hotbar_imagesize *= m_hud_scaling; @@ -213,9 +214,7 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } // Position of upper left corner of bar - v2s32 pos = screen_offset; - pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity(); - pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity(); + v2s32 pos = screen_offset * m_scale_factor; pos += upperleftpos; // Store hotbar_image in member variable, used by drawItem() @@ -320,20 +319,40 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) switch (e->type) { case HUD_ELEM_TEXT: { irr::gui::IGUIFont *textfont = font; + unsigned int font_size = g_fontengine->getDefaultFontSize(); + if (e->size.X > 0) - textfont = g_fontengine->getFont( - e->size.X * g_fontengine->getDefaultFontSize()); + font_size *= e->size.X; + + if (font_size != g_fontengine->getDefaultFontSize()) + textfont = g_fontengine->getFont(font_size); video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, (e->number >> 0) & 0xFF); - core::rect size(0, 0, e->scale.X, text_height * e->scale.Y); std::wstring text = unescape_translate(utf8_to_wide(e->text)); core::dimension2d textsize = textfont->getDimension(text.c_str()); +#ifdef __ANDROID__ + // The text size on Android is not proportional with the actual scaling + irr::gui::IGUIFont *font_scaled = font_size <= 3 ? + textfont : g_fontengine->getFont(font_size - 3); + if (e->offset.X < -20) + textsize = font_scaled->getDimension(text.c_str()); +#endif v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2), (e->align.Y - 1.0) * (textsize.Height / 2)); - v2s32 offs(e->offset.X, e->offset.Y); - textfont->draw(text.c_str(), size + pos + offset + offs, color); + core::rect size(0, 0, e->scale.X * m_scale_factor, + text_height * e->scale.Y * m_scale_factor); + v2s32 offs(e->offset.X * m_scale_factor, + e->offset.Y * m_scale_factor); +#ifdef __ANDROID__ + if (e->offset.X < -20) + font_scaled->draw(text.c_str(), size + pos + offset + offs, color); + else +#endif + { + textfont->draw(text.c_str(), size + pos + offset + offs, color); + } break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); @@ -385,8 +404,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; core::dimension2di imgsize(texture->getOriginalSize()); - v2s32 dstsize(imgsize.Width * e->scale.X, - imgsize.Height * e->scale.Y); + v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor, + imgsize.Height * e->scale.Y * m_scale_factor); if (e->scale.X < 0) dstsize.X = m_screensize.X * (e->scale.X * -0.01); if (e->scale.Y < 0) @@ -394,7 +413,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, (e->align.Y - 1.0) * dstsize.Y / 2); core::rect rect(0, 0, dstsize.X, dstsize.Y); - rect += pos + offset + v2s32(e->offset.X, e->offset.Y); + rect += pos + offset + v2s32(e->offset.X * m_scale_factor, + e->offset.Y * m_scale_factor); draw2DImageFilterScaled(driver, texture, rect, core::rect(core::position2d(0,0), imgsize), NULL, colors, true); @@ -427,12 +447,15 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, core::dimension2di dstd; if (size == v2s32()) { dstd = srcd; + dstd.Height *= m_scale_factor; + dstd.Width *= m_scale_factor; + offset.X *= m_scale_factor; + offset.Y *= m_scale_factor; } else { - float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity(); - dstd.Height = size.Y * size_factor; - dstd.Width = size.X * size_factor; - offset.X *= size_factor; - offset.Y *= size_factor; + dstd.Height = size.Y * m_scale_factor; + dstd.Width = size.X * m_scale_factor; + offset.X *= m_scale_factor; + offset.Y *= m_scale_factor; } v2s32 p = pos; @@ -555,7 +578,7 @@ void Hud::drawHotbar(u16 playeritem) { v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3); const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); - if ( (float) width / (float) window_size.X <= + if ((float) width / (float) window_size.X <= g_settings->getFloat("hud_hotbar_max_width")) { if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0); @@ -607,12 +630,10 @@ void Hud::drawSelectionMesh() // Draw 3D selection boxes video::SMaterial oldmaterial = driver->getMaterial2D(); driver->setMaterial(m_selection_material); - for (std::vector::const_iterator - i = m_selection_boxes.begin(); - i != m_selection_boxes.end(); ++i) { + for (auto & selection_box : m_selection_boxes) { aabb3f box = aabb3f( - i->MinEdge + m_selection_pos_with_offset, - i->MaxEdge + m_selection_pos_with_offset); + selection_box.MinEdge + m_selection_pos_with_offset, + selection_box.MaxEdge + m_selection_pos_with_offset); u32 r = (selectionbox_argb.getRed() * m_selection_mesh_color.getRed() / 255); diff --git a/src/client/hud.h b/src/client/hud.h index e2abf326c..6f4c54626 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -92,6 +92,7 @@ private: void drawItem(const ItemStack &item, const core::rect &rect, bool selected); float m_hud_scaling; // cached minetest setting + float m_scale_factor; v3s16 m_camera_offset; v2u32 m_screensize; v2s32 m_displaycenter; -- cgit v1.2.3 From e746607d0f8531e3a8b12cc45294aecf36217b17 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sat, 6 Jun 2020 23:25:18 +0300 Subject: Restore visual_scale support for nodeboxes (and allfaces) (#9906) --- doc/lua_api.txt | 2 +- src/client/content_mapblock.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8c81b7020..898531880 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6941,7 +6941,7 @@ Used by `minetest.register_node`. visual_scale = 1.0, -- Supported for drawtypes "plantlike", "signlike", "torchlike", - -- "firelike", "mesh". + -- "firelike", "mesh", "nodebox", "allfaces". -- For plantlike and firelike, the image will start at the bottom of the -- node. For torchlike, the image will start at the surface to which the -- node "attaches". For the other drawtypes the image will be centered diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 50efd2e40..f7d20001f 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -366,6 +366,7 @@ void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 * void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, TileSpec *tiles, int tile_count) { + bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f; f32 texture_coord_buf[24]; f32 dx1 = box.MinEdge.X; f32 dy1 = box.MinEdge.Y; @@ -373,6 +374,14 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, f32 dx2 = box.MaxEdge.X; f32 dy2 = box.MaxEdge.Y; f32 dz2 = box.MaxEdge.Z; + if (scale) { + if (!txc) { // generate texture coords before scaling + generateCuboidTextureCoords(box, texture_coord_buf); + txc = texture_coord_buf; + } + box.MinEdge *= f->visual_scale; + box.MaxEdge *= f->visual_scale; + } box.MinEdge += origin; box.MaxEdge += origin; if (!txc) { @@ -1323,7 +1332,7 @@ void MapblockMeshGenerator::drawNodeboxNode() std::vector boxes; n.getNodeBoxes(nodedef, &boxes, neighbors_set); - for (const auto &box : boxes) + for (auto &box : boxes) drawAutoLightedCuboid(box, nullptr, tiles, 6); } -- cgit v1.2.3 From 8fc9e7eb117849202b87bf3d764cd3eac6f68c74 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 7 Jun 2020 14:17:32 +0200 Subject: GenericCAO: Fix glow not working since a08d18a --- src/client/content_cao.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 702d089af..855729642 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -787,6 +787,9 @@ void GenericCAO::addToScene(ITextureSource *tsrc) void GenericCAO::updateLight(u32 day_night_ratio) { + if (m_glow < 0) + return; + u8 light_at_pos = 0; bool pos_ok; @@ -797,7 +800,7 @@ void GenericCAO::updateLight(u32 day_night_ratio) else light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); - u8 light = decode_light(light_at_pos); + u8 light = decode_light(light_at_pos + m_glow); if (light != m_last_light) { m_last_light = light; setNodeLight(light); -- cgit v1.2.3 From fe1f72ab0ac8bcc233c91eb5b2d71bd2d2574cf8 Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Sun, 7 Jun 2020 19:14:00 +0300 Subject: Recalculate mesh normals for CAOs (#10000) --- src/client/content_cao.cpp | 8 ++++++++ src/client/mesh.cpp | 20 ++++++++++++++++++++ src/client/mesh.h | 6 ++++++ 3 files changed, 34 insertions(+) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 855729642..a6ce06d20 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -726,6 +726,14 @@ void GenericCAO::addToScene(ITextureSource *tsrc) addAnimatedMeshSceneNode(mesh, m_matrixnode); m_animated_meshnode->grab(); mesh->drop(); // The scene node took hold of it + + if (!checkMeshNormals(mesh)) { + infostream << "GenericCAO: recalculating normals for mesh " + << m_prop.mesh << std::endl; + m_smgr->getMeshManipulator()-> + recalculateNormals(mesh, true, false); + } + m_animated_meshnode->animateJoints(); // Needed for some animations m_animated_meshnode->setScale(m_prop.visual_size); diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 4d73ead8a..91781373c 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -328,6 +328,26 @@ void recalculateBoundingBox(scene::IMesh *src_mesh) src_mesh->setBoundingBox(bbox); } +bool checkMeshNormals(scene::IMesh *mesh) +{ + u32 buffer_count = mesh->getMeshBufferCount(); + + for (u32 i = 0; i < buffer_count; i++) { + scene::IMeshBuffer *buffer = mesh->getMeshBuffer(i); + + // Here we intentionally check only first normal, assuming that if buffer + // has it valid, then most likely all other ones are fine too. We can + // check all of the normals to have length, but it seems like an overkill + // hurting the performance and covering only really weird broken models. + f32 length = buffer->getNormal(0).getLength(); + + if (!isfinite(length) || fabs(length) < 1e-10) + return false; + } + + return true; +} + scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer) { switch (mesh_buffer->getVertexType()) { diff --git a/src/client/mesh.h b/src/client/mesh.h index 0c4094de2..103c61e45 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -121,6 +121,12 @@ scene::IMesh* convertNodeboxesToMesh(const std::vector &boxes, */ void recalculateBoundingBox(scene::IMesh *src_mesh); +/* + Check if mesh has valid normals and return true if it does. + We assume normal to be valid when it's 0 < length < Inf. and not NaN + */ +bool checkMeshNormals(scene::IMesh *mesh); + /* Vertex cache optimization according to the Forsyth paper: http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html -- cgit v1.2.3 From 0ab580810c6ccebfd57497b9565e3e396d250d70 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 7 Jun 2020 20:43:42 +0200 Subject: Reset touching_ground when in free_move This corrects local player animation after enabling fly while standing on ground. --- src/client/localplayer.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/client') diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 011898bcf..1e7040d57 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -200,6 +200,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (noclip && free_move) { position += m_speed * dtime; setPosition(position); + + touching_ground = false; added_velocity = v3f(0.0f); // ignored return; } @@ -787,6 +789,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, if (free_move) { position += m_speed * dtime; setPosition(position); + + touching_ground = false; m_sneak_node_exists = false; added_velocity = v3f(0.0f); return; -- cgit v1.2.3 From b9f618746c0f673ea1f851bb2473fb3ca9410093 Mon Sep 17 00:00:00 2001 From: MoNTE48 Date: Thu, 16 Apr 2020 17:37:40 +0200 Subject: TouchScreenGUI: fix some bugs, cleanup --- src/client/game.cpp | 4 + src/gui/touchscreengui.cpp | 414 +++++++++++++++++---------------------------- src/gui/touchscreengui.h | 55 +++--- 3 files changed, 183 insertions(+), 290 deletions(-) (limited to 'src/client') diff --git a/src/client/game.cpp b/src/client/game.cpp index cdf4da21e..139742cec 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1173,6 +1173,10 @@ void Game::shutdown() if (formspec) formspec->quitMenu(); +#ifdef HAVE_TOUCHSCREENGUI + g_touchscreengui->hide(); +#endif + showOverlayMessage(N_("Shutting down..."), 0, 0, false); if (clouds) diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 4df5979e1..3ee9e4a64 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -34,19 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include -// Very slow button repeat frequency (in seconds) -#define SLOW_BUTTON_REPEAT (1.0f) - using namespace irr::core; -const char **touchgui_button_imagenames = (const char *[]) { +const char **button_imagenames = (const char *[]) { "jump_btn.png", "down.png", "zoom.png", "aux_btn.png" }; -const char **touchgui_joystick_imagenames = (const char *[]) { +const char **joystick_imagenames = (const char *[]) { "joystick_off.png", "joystick_bg.png", "joystick_center.png" @@ -113,15 +110,17 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case range_id: key = "rangeselect"; break; + default: + break; } - assert(key != ""); + assert(!key.empty()); return keyname_to_keycode(g_settings->get("keymap_" + key).c_str()); } TouchScreenGUI *g_touchscreengui; static void load_button_texture(button_info *btn, const char *path, - rect button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) + const rect &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) { unsigned int tid; video::ITexture *texture = guiScalingImageButton(driver, @@ -141,7 +140,7 @@ static void load_button_texture(button_info *btn, const char *path, } btn->guibutton->setDrawBorder(false); btn->guibutton->setText(L""); - } + } } AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, @@ -153,8 +152,8 @@ AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, } void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, - const char *starter_img, int button_id, v2s32 UpperLeft, - v2s32 LowerRight, autohide_button_bar_dir dir, float timeout) + const char *starter_img, int button_id, const v2s32 &UpperLeft, + const v2s32 &LowerRight, autohide_button_bar_dir dir, float timeout) { m_texturesource = tsrc; @@ -166,7 +165,7 @@ void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, irr::core::rect current_button = rect(UpperLeft.X, UpperLeft.Y, LowerRight.X, LowerRight.Y); - m_starter.guibutton = m_guienv->addButton(current_button, 0, button_id, L"", 0); + m_starter.guibutton = m_guienv->addButton(current_button, nullptr, button_id, L"", nullptr); m_starter.guibutton->grab(); m_starter.repeatcounter = -1; m_starter.keycode = KEY_OEM_8; // use invalid keycode as it's not relevant @@ -201,16 +200,14 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, } int button_size = 0; - if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) { + if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) button_size = m_lower_right.X - m_upper_left.X; - } else { + else button_size = m_lower_right.Y - m_upper_left.Y; - } irr::core::rect current_button; if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) { - int x_start = 0; int x_end = 0; @@ -227,8 +224,8 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, current_button = rect(x_start, m_upper_left.Y, x_end, m_lower_right.Y); } else { - int y_start = 0; - int y_end = 0; + double y_start = 0; + double y_end = 0; if (m_dir == AHBB_Dir_Top_Bottom) { y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size()) @@ -240,12 +237,13 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, y_start = y_end - button_size; } - current_button = rect(m_upper_left.X, y_start, m_lower_right.Y, - y_end); + current_button = rect(m_upper_left.X, y_start, + m_lower_right.Y, y_end); } - button_info *btn = new button_info(); - btn->guibutton = m_guienv->addButton(current_button, 0, button_id, caption, 0); + auto *btn = new button_info(); + btn->guibutton = m_guienv->addButton(current_button, + nullptr, button_id, caption, nullptr); btn->guibutton->grab(); btn->guibutton->setVisible(false); btn->guibutton->setEnabled(false); @@ -275,26 +273,23 @@ bool AutoHideButtonBar::isButton(const SEvent &event) { IGUIElement *rootguielement = m_guienv->getRootGUIElement(); - if (rootguielement == NULL) { + if (rootguielement == nullptr) return false; - } gui::IGUIElement *element = rootguielement->getElementFromPoint( core::position2d(event.TouchInput.X, event.TouchInput.Y)); - if (element == NULL) { + if (element == nullptr) return false; - } if (m_active) { // check for all buttons in vector - - std::vector::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { if ((*iter)->guibutton == element) { - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = (*iter)->keycode; @@ -341,7 +336,7 @@ bool AutoHideButtonBar::isButton(const SEvent &event) m_active = true; m_timeout = 0; - std::vector::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(true); @@ -355,41 +350,13 @@ bool AutoHideButtonBar::isButton(const SEvent &event) return false; } -bool AutoHideButtonBar::isReleaseButton(int eventID) -{ - std::vector::iterator id = std::find(m_starter.ids.begin(), - m_starter.ids.end(), eventID); - - if (id != m_starter.ids.end()) { - m_starter.ids.erase(id); - return true; - } - - std::vector::iterator iter = m_buttons.begin(); - - while (iter != m_buttons.end()) { - std::vector::iterator id = std::find((*iter)->ids.begin(), - (*iter)->ids.end(), eventID); - - if (id != (*iter)->ids.end()) { - (*iter)->ids.erase(id); - // TODO handle settings button release - return true; - } - ++iter; - } - - return false; -} - void AutoHideButtonBar::step(float dtime) { if (m_active) { m_timeout += dtime; - if (m_timeout > m_timeout_value) { + if (m_timeout > m_timeout_value) deactivate(); - } } } @@ -401,11 +368,11 @@ void AutoHideButtonBar::deactivate() } m_active = false; - std::vector::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { - (*iter)->guibutton->setVisible(false); - (*iter)->guibutton->setEnabled(false); + (*iter)->guibutton->setVisible(false); + (*iter)->guibutton->setEnabled(false); ++iter; } } @@ -416,7 +383,7 @@ void AutoHideButtonBar::hide() m_starter.guibutton->setVisible(false); m_starter.guibutton->setEnabled(false); - std::vector::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(false); @@ -430,7 +397,7 @@ void AutoHideButtonBar::show() m_visible = true; if (m_active) { - std::vector::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(true); @@ -450,24 +417,26 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) m_settingsbar(device, receiver), m_rarecontrolsbar(device, receiver) { - for (unsigned int i=0; i < after_last_element_id; i++) { - m_buttons[i].guibutton = 0; - m_buttons[i].repeatcounter = -1; - m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY; + for (auto &button : m_buttons) { + button.guibutton = nullptr; + button.repeatcounter = -1; + button.repeatdelay = BUTTON_REPEAT_DELAY; } m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); m_joystick_triggers_special1 = g_settings->getBool("virtual_joystick_triggers_aux"); m_screensize = m_device->getVideoDriver()->getScreenSize(); + button_size = MYMIN(m_screensize.Y / 4.5f, + porting::getDisplayDensity() * + g_settings->getFloat("hud_scaling") * 65.0f); } -void TouchScreenGUI::initButton(touch_gui_button_id id, rect button_rect, - std::wstring caption, bool immediate_release, float repeat_delay) +void TouchScreenGUI::initButton(touch_gui_button_id id, const rect &button_rect, + const std::wstring &caption, bool immediate_release, float repeat_delay) { - button_info *btn = &m_buttons[id]; - btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str()); + btn->guibutton = m_guienv->addButton(button_rect, nullptr, id, caption.c_str()); btn->guibutton->grab(); btn->repeatcounter = -1; btn->repeatdelay = repeat_delay; @@ -475,42 +444,29 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect button_rect, btn->immediate_release = immediate_release; btn->ids.clear(); - load_button_texture(btn, touchgui_button_imagenames[id], button_rect, + load_button_texture(btn, button_imagenames[id], button_rect, m_texturesource, m_device->getVideoDriver()); } -button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, rect button_rect, - int texture_id, bool visible) +button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, + const rect &button_rect, int texture_id, bool visible) { - button_info *btn = new button_info(); - btn->guibutton = m_guienv->addButton(button_rect, 0, id, L"O"); + auto *btn = new button_info(); + btn->guibutton = m_guienv->addButton(button_rect, nullptr, id, L"O"); btn->guibutton->setVisible(visible); btn->guibutton->grab(); btn->ids.clear(); - load_button_texture(btn, touchgui_joystick_imagenames[texture_id], button_rect, - m_texturesource, m_device->getVideoDriver()); + load_button_texture(btn, joystick_imagenames[texture_id], + button_rect, m_texturesource, m_device->getVideoDriver()); return btn; } -static int getMaxControlPadSize(float density) { - return 200 * density * g_settings->getFloat("hud_scaling"); -} - -int TouchScreenGUI::getGuiButtonSize() -{ - u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3, - getMaxControlPadSize(porting::getDisplayDensity())); - - return control_pad_size / 3; -} - void TouchScreenGUI::init(ISimpleTextureSource *tsrc) { assert(tsrc); - u32 button_size = getGuiButtonSize(); m_visible = true; m_texturesource = tsrc; @@ -608,35 +564,31 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc) m_rarecontrolsbar.addButton(chat_id, L"Chat", "chat_btn.png"); m_rarecontrolsbar.addButton(inventory_id, L"inv", "inventory_btn.png"); m_rarecontrolsbar.addButton(drop_id, L"drop", "drop_btn.png"); - } touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) { IGUIElement *rootguielement = m_guienv->getRootGUIElement(); - if (rootguielement != NULL) { + if (rootguielement != nullptr) { gui::IGUIElement *element = rootguielement->getElementFromPoint(core::position2d(x, y)); - if (element) { - for (unsigned int i=0; i < after_last_element_id; i++) { - if (element == m_buttons[i].guibutton) { + if (element) + for (unsigned int i = 0; i < after_last_element_id; i++) + if (element == m_buttons[i].guibutton) return (touch_gui_button_id) i; - } - } - } } + return after_last_element_id; } -touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) +touch_gui_button_id TouchScreenGUI::getButtonID(size_t eventID) { - for (unsigned int i=0; i < after_last_element_id; i++) { + for (unsigned int i = 0; i < after_last_element_id; i++) { button_info *btn = &m_buttons[i]; - std::vector::iterator id = - std::find(btn->ids.begin(), btn->ids.end(), eventID); + auto id = std::find(btn->ids.begin(), btn->ids.end(), eventID); if (id != btn->ids.end()) return (touch_gui_button_id) i; @@ -648,55 +600,30 @@ touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) bool TouchScreenGUI::isHUDButton(const SEvent &event) { // check if hud item is pressed - for (std::map >::iterator iter = m_hud_rects.begin(); - iter != m_hud_rects.end(); ++iter) { - if (iter->second.isPointInside( - v2s32(event.TouchInput.X, - event.TouchInput.Y) - )) { - if ( iter->first < 8) { - SEvent *translated = new SEvent(); - memset(translated, 0, sizeof(SEvent)); - translated->EventType = irr::EET_KEY_INPUT_EVENT; - translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first); - translated->KeyInput.Control = false; - translated->KeyInput.Shift = false; - translated->KeyInput.PressedDown = true; - m_receiver->OnEvent(*translated); - m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key; - delete translated; - return true; - } + for (auto &hud_rect : m_hud_rects) { + if (hud_rect.second.isPointInside(v2s32(event.TouchInput.X, + event.TouchInput.Y))) { + auto *translated = new SEvent(); + memset(translated, 0, sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + hud_rect.first); + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + translated->KeyInput.PressedDown = true; + m_receiver->OnEvent(*translated); + m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key; + delete translated; + return true; } } return false; } -bool TouchScreenGUI::isReleaseHUDButton(int eventID) -{ - std::map::iterator iter = m_hud_ids.find(eventID); - - if (iter != m_hud_ids.end()) { - SEvent *translated = new SEvent(); - memset(translated, 0, sizeof(SEvent)); - translated->EventType = irr::EET_KEY_INPUT_EVENT; - translated->KeyInput.Key = iter->second; - translated->KeyInput.PressedDown = false; - translated->KeyInput.Control = false; - translated->KeyInput.Shift = false; - m_receiver->OnEvent(*translated); - m_hud_ids.erase(iter); - delete translated; - return true; - } - return false; -} - void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, - int eventID, bool action) + size_t eventID, bool action) { button_info *btn = &m_buttons[button]; - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = btn->keycode; @@ -717,16 +644,16 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, translated->KeyInput.Key = btn->keycode; m_receiver->OnEvent(*translated); } + // remove event if ((!action) || (btn->immediate_release)) { - - std::vector::iterator pos = - std::find(btn->ids.begin(), btn->ids.end(), eventID); + auto pos = std::find(btn->ids.begin(), btn->ids.end(), eventID); // has to be in touch list assert(pos != btn->ids.end()); btn->ids.erase(pos); - if (btn->ids.size() > 0) { return; } + if (!btn->ids.empty()) + return; translated->KeyInput.PressedDown = false; btn->repeatcounter = -1; @@ -735,30 +662,21 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, delete translated; } - -void TouchScreenGUI::handleReleaseEvent(int evt_id) +void TouchScreenGUI::handleReleaseEvent(size_t evt_id) { touch_gui_button_id button = getButtonID(evt_id); - // handle button events + if (button != after_last_element_id) { + // handle button events handleButtonEvent(button, evt_id, false); - } - // handle hud button events - else if (isReleaseHUDButton(evt_id)) { - // nothing to do here - } else if (m_settingsbar.isReleaseButton(evt_id)) { - // nothing to do here - } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) { - // nothing to do here - } - // handle the point used for moving view - else if (evt_id == m_move_id) { + } else if (evt_id == m_move_id) { + // handle the point used for moving view m_move_id = -1; // if this pointer issued a mouse event issue symmetric release here if (m_move_sent_as_mouse_event) { - SEvent *translated = new SEvent; + auto *translated = new SEvent; memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_move_downlocation.X; @@ -769,32 +687,31 @@ void TouchScreenGUI::handleReleaseEvent(int evt_id) translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP; m_receiver->OnEvent(*translated); delete translated; - } - else { + } else { // do double tap detection doubleTapDetection(); } } + // handle joystick else if (evt_id == m_joystick_id) { m_joystick_id = -1; // reset joystick - for (unsigned int i = 0; i < 4; i ++) + for (unsigned int i = 0; i < 4; i++) m_joystick_status[i] = false; applyJoystickStatus(); m_joystick_btn_off->guibutton->setVisible(true); m_joystick_btn_bg->guibutton->setVisible(false); m_joystick_btn_center->guibutton->setVisible(false); - } - else { + } else { infostream << "TouchScreenGUI::translateEvent released unknown button: " << evt_id << std::endl; } - for (std::vector::iterator iter = m_known_ids.begin(); + for (auto iter = m_known_ids.begin(); iter != m_known_ids.end(); ++iter) { if (iter->id == evt_id) { m_known_ids.erase(iter); @@ -806,18 +723,19 @@ void TouchScreenGUI::handleReleaseEvent(int evt_id) void TouchScreenGUI::translateEvent(const SEvent &event) { if (!m_visible) { - infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl; + infostream + << "TouchScreenGUI::translateEvent got event but not visible!" + << std::endl; return; } - if (event.EventType != EET_TOUCH_INPUT_EVENT) { + if (event.EventType != EET_TOUCH_INPUT_EVENT) return; - } if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { - - /* add to own copy of eventlist ... - * android would provide this information but irrlicht guys don't + /* + * Add to own copy of event list... + * android would provide this information but Irrlicht guys don't * wanna design a efficient interface */ id_status toadd; @@ -826,7 +744,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) toadd.Y = event.TouchInput.Y; m_known_ids.push_back(toadd); - int eventID = event.TouchInput.ID; + size_t eventID = event.TouchInput.ID; touch_gui_button_id button = getButtonID(event.TouchInput.X, event.TouchInput.Y); @@ -846,21 +764,19 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } else if (m_rarecontrolsbar.isButton(event)) { m_settingsbar.deactivate(); // already handled in isSettingsBarButton() - } - // handle non button events - else { + } else { + // handle non button events m_settingsbar.deactivate(); m_rarecontrolsbar.deactivate(); - u32 button_size = getGuiButtonSize(); - s32 dxj = event.TouchInput.X - button_size * 5 / 2; - s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2; + s32 dxj = event.TouchInput.X - button_size * 5.0f / 2.0f; + s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5.0f / 2.0f; /* Select joystick when left 1/3 of screen dragged or * when joystick tapped (fixed joystick position) */ if ((m_fixed_joystick && dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5) || - (!m_fixed_joystick && event.TouchInput.X < m_screensize.X / 3)) { + (!m_fixed_joystick && event.TouchInput.X < m_screensize.X / 3.0f)) { // If we don't already have a starting point for joystick make this the one. if (m_joystick_id == -1) { m_joystick_id = event.TouchInput.ID; @@ -871,14 +787,14 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_joystick_btn_center->guibutton->setVisible(true); // If it's a fixed joystick, don't move the joystick "button". - if (!m_fixed_joystick) { + if (!m_fixed_joystick) m_joystick_btn_bg->guibutton->setRelativePosition(v2s32( - event.TouchInput.X - button_size * 3 / 2, - event.TouchInput.Y - button_size * 3 / 2)); - } + event.TouchInput.X - button_size * 3.0f / 2.0f, + event.TouchInput.Y - button_size * 3.0f / 2.0f)); + m_joystick_btn_center->guibutton->setRelativePosition(v2s32( - event.TouchInput.X - button_size / 2, - event.TouchInput.Y - button_size / 2)); + event.TouchInput.X - button_size / 2.0f, + event.TouchInput.Y - button_size / 2.0f)); } } else { // If we don't already have a moving point make this the moving one. @@ -895,17 +811,15 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y); } else if (event.TouchInput.Event == ETIE_LEFT_UP) { - verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl; + verbosestream + << "Up event for pointerid: " << event.TouchInput.ID << std::endl; handleReleaseEvent(event.TouchInput.ID); - } - else { + } else { assert(event.TouchInput.Event == ETIE_MOVED); - int move_idx = event.TouchInput.ID; if (m_pointerpos[event.TouchInput.ID] == - v2s32(event.TouchInput.X, event.TouchInput.Y)) { + v2s32(event.TouchInput.X, event.TouchInput.Y)) return; - } if (m_move_id != -1) { if ((event.TouchInput.ID == m_move_id) && @@ -928,9 +842,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y; // adapt to similar behaviour as pc screen - double d = g_settings->getFloat("mouse_sensitivity") * 4; - double old_yaw = m_camera_yaw_change; - double old_pitch = m_camera_pitch; + double d = g_settings->getFloat("mouse_sensitivity") * 3.0f; m_camera_yaw_change -= dx * d; m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180); @@ -942,8 +854,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) ->getRayFromScreenCoordinates(v2s32(X, Y)); m_pointerpos[event.TouchInput.ID] = v2s32(X, Y); } - } - else if ((event.TouchInput.ID == m_move_id) && + } else if ((event.TouchInput.ID == m_move_id) && (m_move_sent_as_mouse_event)) { m_shootline = m_device ->getSceneManager() @@ -954,7 +865,6 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } if (m_joystick_id != -1 && event.TouchInput.ID == m_joystick_id) { - u32 button_size = getGuiButtonSize(); s32 X = event.TouchInput.X; s32 Y = event.TouchInput.Y; @@ -967,8 +877,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) double distance_sq = dx * dx + dy * dy; - s32 dxj = event.TouchInput.X - button_size * 5 / 2; - s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2; + s32 dxj = event.TouchInput.X - button_size * 5.0f / 2.0f; + s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5.0f / 2.0f; bool inside_joystick = (dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5); if (m_joystick_has_really_moved || @@ -986,8 +896,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) angle = fmod(angle + 180 + 22.5, 360); // reset state before applying - for (unsigned int i = 0; i < 5; i ++) - m_joystick_status[i] = false; + for (bool & joystick_status : m_joystick_status) + joystick_status = false; if (distance <= m_touchscreen_threshold) { // do nothing @@ -1016,8 +926,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) if (distance > button_size) { m_joystick_status[j_special1] = true; // move joystick "button" - s32 ndx = (s32) button_size * dx / distance - (s32) button_size / 2; - s32 ndy = (s32) button_size * dy / distance - (s32) button_size / 2; + s32 ndx = button_size * dx / distance - button_size / 2.0f; + s32 ndy = button_size * dy / distance - button_size / 2.0f; if (m_fixed_joystick) { m_joystick_btn_center->guibutton->setRelativePosition(v2s32( button_size * 5 / 2 + ndx, @@ -1028,64 +938,54 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_pointerpos[event.TouchInput.ID].Y + ndy)); } } else { - m_joystick_btn_center->guibutton->setRelativePosition(v2s32( - X - button_size / 2, Y - button_size / 2)); + m_joystick_btn_center->guibutton->setRelativePosition( + v2s32(X - button_size / 2, Y - button_size / 2)); } } } - if (m_move_id == -1 && m_joystick_id == -1) { + if (m_move_id == -1 && m_joystick_id == -1) handleChangedButton(event); - } } } void TouchScreenGUI::handleChangedButton(const SEvent &event) { for (unsigned int i = 0; i < after_last_element_id; i++) { - - if (m_buttons[i].ids.empty()) { + if (m_buttons[i].ids.empty()) continue; - } - for (std::vector::iterator iter = m_buttons[i].ids.begin(); - iter != m_buttons[i].ids.end(); ++iter) { + for (auto iter = m_buttons[i].ids.begin(); + iter != m_buttons[i].ids.end(); ++iter) { if (event.TouchInput.ID == *iter) { - int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - if (current_button_id == i) { + if (current_button_id == i) continue; - } // remove old button handleButtonEvent((touch_gui_button_id) i, *iter, false); - if (current_button_id == after_last_element_id) { + if (current_button_id == after_last_element_id) return; - } + handleButtonEvent((touch_gui_button_id) current_button_id, *iter, true); return; - } } } int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - if (current_button_id == after_last_element_id) { + if (current_button_id == after_last_element_id) return; - } button_info *btn = &m_buttons[current_button_id]; if (std::find(btn->ids.begin(), btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) - { handleButtonEvent((touch_gui_button_id) current_button_id, event.TouchInput.ID, true); - } - } bool TouchScreenGUI::doubleTapDetection() @@ -1102,14 +1002,15 @@ bool TouchScreenGUI::doubleTapDetection() return false; double distance = sqrt( - (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) + - (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y)); - + (m_key_events[0].x - m_key_events[1].x) * + (m_key_events[0].x - m_key_events[1].x) + + (m_key_events[0].y - m_key_events[1].y) * + (m_key_events[0].y - m_key_events[1].y)); if (distance > (20 + m_touchscreen_threshold)) return false; - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_key_events[0].x; @@ -1129,21 +1030,20 @@ bool TouchScreenGUI::doubleTapDetection() m_receiver->OnEvent(*translated); translated->MouseInput.ButtonStates = 0; - translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP; + translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP; verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl; m_receiver->OnEvent(*translated); delete translated; return true; - } void TouchScreenGUI::applyJoystickStatus() { - for (unsigned int i = 0; i < 5; i ++) { + for (unsigned int i = 0; i < 5; i++) { if (i == 4 && !m_joystick_triggers_special1) continue; - SEvent translated{}; + SEvent translated; translated.EventType = irr::EET_KEY_INPUT_EVENT; translated.KeyInput.Key = id2keycode(m_joystick_names[i]); translated.KeyInput.PressedDown = false; @@ -1158,50 +1058,48 @@ void TouchScreenGUI::applyJoystickStatus() TouchScreenGUI::~TouchScreenGUI() { - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - if (btn->guibutton) { - btn->guibutton->drop(); - btn->guibutton = NULL; + for (auto &button : m_buttons) { + if (button.guibutton) { + button.guibutton->drop(); + button.guibutton = nullptr; } } if (m_joystick_btn_off->guibutton) { m_joystick_btn_off->guibutton->drop(); - m_joystick_btn_off->guibutton = NULL; + m_joystick_btn_off->guibutton = nullptr; } if (m_joystick_btn_bg->guibutton) { m_joystick_btn_bg->guibutton->drop(); - m_joystick_btn_bg->guibutton = NULL; + m_joystick_btn_bg->guibutton = nullptr; } if (m_joystick_btn_center->guibutton) { m_joystick_btn_center->guibutton->drop(); - m_joystick_btn_center->guibutton = NULL; + m_joystick_btn_center->guibutton = nullptr; } } void TouchScreenGUI::step(float dtime) { // simulate keyboard repeats - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - - if (btn->ids.size() > 0) { - btn->repeatcounter += dtime; + for (auto &button : m_buttons) { + if (!button.ids.empty()) { + button.repeatcounter += dtime; // in case we're moving around digging does not happen if (m_move_id != -1) m_move_has_really_moved = true; - if (btn->repeatcounter < btn->repeatdelay) continue; + if (button.repeatcounter < button.repeatdelay) + continue; - btn->repeatcounter = 0; + button.repeatcounter = 0; SEvent translated; memset(&translated, 0, sizeof(SEvent)); translated.EventType = irr::EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = btn->keycode; + translated.KeyInput.Key = button.keycode; translated.KeyInput.PressedDown = false; m_receiver->OnEvent(translated); @@ -1264,22 +1162,18 @@ void TouchScreenGUI::registerHudItem(int index, const rect &rect) void TouchScreenGUI::Toggle(bool visible) { m_visible = visible; - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - if (btn->guibutton) { - btn->guibutton->setVisible(visible); - } + for (auto &button : m_buttons) { + if (button.guibutton) + button.guibutton->setVisible(visible); } - if (m_joystick_btn_off->guibutton) { + if (m_joystick_btn_off->guibutton) m_joystick_btn_off->guibutton->setVisible(visible); - } // clear all active buttons if (!visible) { - while (m_known_ids.size() > 0) { + while (!m_known_ids.empty()) handleReleaseEvent(m_known_ids.begin()->id); - } m_settingsbar.hide(); m_rarecontrolsbar.hide(); diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 2a3f24a3f..761d33207 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -80,21 +80,22 @@ typedef enum } autohide_button_bar_dir; #define MIN_DIG_TIME_MS 500 -#define MAX_TOUCH_COUNT 64 #define BUTTON_REPEAT_DELAY 0.2f - #define SETTINGS_BAR_Y_OFFSET 5 #define RARE_CONTROLS_BAR_Y_OFFSET 5 -extern const char **touchgui_button_imagenames; -extern const char **touchgui_joystick_imagenames; +// Very slow button repeat frequency +#define SLOW_BUTTON_REPEAT 1.0f + +extern const char **button_imagenames; +extern const char **joystick_imagenames; struct button_info { float repeatcounter; float repeatdelay; irr::EKEY_CODE keycode; - std::vector ids; + std::vector ids; IGUIButton *guibutton = nullptr; bool immediate_release; @@ -109,8 +110,8 @@ public: AutoHideButtonBar(IrrlichtDevice *device, IEventReceiver *receiver); void init(ISimpleTextureSource *tsrc, const char *starter_img, int button_id, - v2s32 UpperLeft, v2s32 LowerRight, autohide_button_bar_dir dir, - float timeout); + const v2s32 &UpperLeft, const v2s32 &LowerRight, + autohide_button_bar_dir dir, float timeout); ~AutoHideButtonBar(); @@ -125,9 +126,6 @@ public: // detect settings bar button events bool isButton(const SEvent &event); - // handle released hud buttons - bool isReleaseButton(int eventID); - // step handler void step(float dtime); @@ -182,7 +180,7 @@ public: double getPitch() { return m_camera_pitch; } - /*! + /* * Returns a line which describes what the player is pointing at. * The starting point and looking direction are significant, * the line should be scaled to match its length to the actual distance @@ -206,9 +204,10 @@ private: IEventReceiver *m_receiver; ISimpleTextureSource *m_texturesource; v2u32 m_screensize; + s32 button_size; double m_touchscreen_threshold; std::map> m_hud_rects; - std::map m_hud_ids; + std::map m_hud_ids; bool m_visible; // is the gui visible // value in degree @@ -220,7 +219,7 @@ private: forward_id, backward_id, left_id, right_id, special1_id}; bool m_joystick_status[5] = {false, false, false, false, false}; - /*! + /* * A line starting at the camera and pointing towards the * selected object. * The line ends on the camera's far plane. @@ -248,23 +247,24 @@ private: touch_gui_button_id getButtonID(s32 x, s32 y); // gui button by eventID - touch_gui_button_id getButtonID(int eventID); + touch_gui_button_id getButtonID(size_t eventID); // check if a button has changed void handleChangedButton(const SEvent &event); // initialize a button - void initButton(touch_gui_button_id id, rect button_rect, - std::wstring caption, bool immediate_release, + void initButton(touch_gui_button_id id, const rect &button_rect, + const std::wstring &caption, bool immediate_release, float repeat_delay = BUTTON_REPEAT_DELAY); // initialize a joystick button - button_info *initJoystickButton(touch_gui_button_id id, rect button_rect, - int texture_id, bool visible = true); + button_info *initJoystickButton(touch_gui_button_id id, + const rect &button_rect, int texture_id, + bool visible = true); struct id_status { - int id; + size_t id; int X; int Y; }; @@ -273,27 +273,21 @@ private: std::vector m_known_ids; // handle a button event - void handleButtonEvent(touch_gui_button_id bID, int eventID, bool action); + void handleButtonEvent(touch_gui_button_id bID, size_t eventID, bool action); // handle pressed hud buttons bool isHUDButton(const SEvent &event); - // handle released hud buttons - bool isReleaseHUDButton(int eventID); - // handle double taps bool doubleTapDetection(); // handle release event - void handleReleaseEvent(int evt_id); + void handleReleaseEvent(size_t evt_id); // apply joystick status void applyJoystickStatus(); - // get size of regular gui control button - int getGuiButtonSize(); - - // doubleclick detection variables + // double-click detection variables struct key_event { u64 down_time; @@ -302,9 +296,9 @@ private: }; // array for saving last known position of a pointer - v2s32 m_pointerpos[MAX_TOUCH_COUNT]; + std::map m_pointerpos; - // array for doubletap detection + // array for double tap detection key_event m_key_events[2]; // settings bar @@ -313,4 +307,5 @@ private: // rare controls bar AutoHideButtonBar m_rarecontrolsbar; }; + extern TouchScreenGUI *g_touchscreengui; -- cgit v1.2.3 From 09e285f38cd96b4278b921ab82c5266082bb1a0d Mon Sep 17 00:00:00 2001 From: hecktest <42101236+hecktest@users.noreply.github.com> Date: Tue, 9 Jun 2020 19:36:47 +0200 Subject: Fix player-to-object attachment teleport bug (#10008) Fixes two bugs: * The camera offset was not applied to an object while detaching, briefly placing the irrlicht matrixnode in world space. * When attaching, the matrixnode's absolute position was evaluated without evaluating the parent first, resulting in zeroed positions. --- src/client/content_cao.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index a6ce06d20..79ecccb6f 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -921,7 +921,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_animated_meshnode->animateJoints(); updateBonePosition(); } - + // Handle model animations and update positions instantly to prevent lags if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); @@ -1403,7 +1403,7 @@ void GenericCAO::updateBonePosition() bone->setRotation(it.second.Y); } } - + // search through bones to find mistakenly rotated bones due to bug in Irrlicht for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) { irr::scene::IBoneSceneNode *bone = m_animated_meshnode->getJointNode(i); @@ -1427,7 +1427,7 @@ void GenericCAO::updateBonePosition() // and update the bones transformation. v3f bone_rot = bone->getRelativeTransformation().getRotationDegrees(); float offset = fabsf(bone_rot.X - bone->getRotation().X); - if (offset > 179.9f && offset < 180.1f) { + if (offset > 179.9f && offset < 180.1f) { bone->setRotation(bone_rot); bone->updateAbsolutePosition(); } @@ -1454,15 +1454,17 @@ void GenericCAO::updateAttachments() if (!parent) { // Detach or don't attach if (m_matrixnode) { + v3s16 camera_offset = m_env->getCameraOffset(); v3f old_pos = getPosition(); m_matrixnode->setParent(m_smgr->getRootSceneNode()); - getPosRotMatrix().setTranslation(old_pos); + getPosRotMatrix().setTranslation(old_pos - intToFloat(camera_offset, BS)); m_matrixnode->updateAbsolutePosition(); } } else // Attach { + parent->updateAttachments(); scene::ISceneNode *parent_node = parent->getSceneNode(); scene::IAnimatedMeshSceneNode *parent_animated_mesh_node = parent->getAnimatedMeshSceneNode(); @@ -1472,6 +1474,7 @@ void GenericCAO::updateAttachments() if (m_matrixnode && parent_node) { m_matrixnode->setParent(parent_node); + parent_node->updateAbsolutePosition(); getPosRotMatrix().setTranslation(m_attachment_position); //setPitchYawRoll(getPosRotMatrix(), m_attachment_rotation); // use Irrlicht eulers instead -- cgit v1.2.3 From fe3e69eb4029626cd7ef3f7a1c2beaec13ba7364 Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Tue, 9 Jun 2020 22:38:09 +0300 Subject: Fix broken coloring of wielditems (#9969) Fixes a regression that appeared in 5.3.0-dev. --- client/shaders/object_shader/opengl_fragment.glsl | 4 +++- client/shaders/object_shader/opengl_vertex.glsl | 7 ++++++- src/client/wieldmesh.cpp | 10 +++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/client') diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 32f3e974e..0534dc049 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -145,8 +145,10 @@ void main(void) vec4 col = vec4(color.rgb, base.a); + col.rgb *= gl_Color.rgb; + col.rgb *= emissiveColor.rgb * vIDiff; - + #ifdef ENABLE_TONE_MAPPING col = applyToneMapping(col); #endif diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 488089392..59171145f 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -38,7 +38,12 @@ void main(void) lightVec = sunPosition - worldPosition; eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - vIDiff = directional_ambient(normalize(gl_Normal)); + + // This is intentional comparison with zero without any margin. + // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector + vIDiff = length(gl_Normal) == 0.0 + ? 1.0 + : directional_ambient(normalize(gl_Normal)); gl_FrontColor = gl_BackColor = gl_Color; } diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 997eb1b5b..8cd3e29a9 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -467,7 +467,11 @@ void WieldMeshSceneNode::setColor(video::SColor c) bc.getGreen() * green / 255, bc.getBlue() * blue / 255); scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - colorizeMeshBuffer(buf, &buffercolor); + + if (m_enable_shaders) + setMeshBufferColor(buf, buffercolor); + else + colorizeMeshBuffer(buf, &buffercolor); } } @@ -481,9 +485,9 @@ void WieldMeshSceneNode::setNodeLightColor(video::SColor color) video::SMaterial &material = m_meshnode->getMaterial(i); material.EmissiveColor = color; } - } else { - setColor(color); } + + setColor(color); } void WieldMeshSceneNode::render() -- cgit v1.2.3 From 64380cf92e8d0000f75ec372e3e1d09bdccd12e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 10 Jun 2020 10:58:13 +0200 Subject: Fix build error on Ubuntu 16.04 --- src/client/content_mapblock.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client') diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index f7d20001f..3d06584c4 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "content_mapblock.h" #include "util/numeric.h" #include "util/directiontables.h" -- cgit v1.2.3 From 80d9e9c1834fdc78588ffe2842766c0177227786 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 10 Jun 2020 11:46:14 +0200 Subject: Fix build error on Ubuntu 16.04 (again) --- src/client/mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 91781373c..68832849e 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -341,7 +341,7 @@ bool checkMeshNormals(scene::IMesh *mesh) // hurting the performance and covering only really weird broken models. f32 length = buffer->getNormal(0).getLength(); - if (!isfinite(length) || fabs(length) < 1e-10) + if (!std::isfinite(length) || std::fabs(length) < 1e-10f) return false; } -- cgit v1.2.3 From f89794108c49a7a9e992afb9431ae244e4a4fef1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 10 Jun 2020 16:53:23 +0200 Subject: content_cao: Do not expire visuals for texture updates --- src/client/content_cao.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 79ecccb6f..644a71e6e 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1487,18 +1487,22 @@ void GenericCAO::updateAttachments() bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const { const ObjectProperties &old = m_prop; + /* Visuals do not need to be expired for: + * - nametag props: handled by updateNametag() + * - textures: handled by updateTextures() + * - sprite props: handled by updateTexturePos() + * - glow: handled by updateLight() + * - any other properties that do not change appearance + */ // Ordered to compare primitive types before std::vectors return old.backface_culling != new_.backface_culling || - old.initial_sprite_basepos != new_.initial_sprite_basepos || old.is_visible != new_.is_visible || old.mesh != new_.mesh || - old.spritediv != new_.spritediv || old.use_texture_alpha != new_.use_texture_alpha || old.visual != new_.visual || old.visual_size != new_.visual_size || old.wield_item != new_.wield_item || - old.colors != new_.colors || - old.textures != new_.textures; + old.colors != new_.colors; } void GenericCAO::processMessage(const std::string &data) @@ -1513,6 +1517,7 @@ void GenericCAO::processMessage(const std::string &data) // Check what exactly changed bool expire_visuals = visualExpiryRequired(newprops); + bool textures_changed = m_prop.textures != newprops.textures; // Apply changes m_prop = std::move(newprops); @@ -1541,13 +1546,18 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; - updateNametag(); if (expire_visuals) { expireVisuals(); } else { infostream << "GenericCAO: properties updated but expiring visuals" << " not necessary" << std::endl; + if (textures_changed) { + // don't update while punch texture modifier is active + if (m_reset_textures_timer < 0) + updateTextures(m_current_texture_modifier); + } + updateNametag(); } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. -- cgit v1.2.3 From 3f0cbbc372d5f359af18f5cbad37a1b165d3df4e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 7 Jun 2020 19:58:09 +0200 Subject: Use multiple light positions for CAO lighting --- src/client/clientobject.h | 1 - src/client/content_cao.cpp | 42 ++++++++++++++++++++++++------------------ src/client/content_cao.h | 5 ++++- 3 files changed, 28 insertions(+), 20 deletions(-) (limited to 'src/client') diff --git a/src/client/clientobject.h b/src/client/clientobject.h index 8e64b8406..ecd8059ef 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -43,7 +43,6 @@ public: virtual void removeFromScene(bool permanent) {} virtual void updateLight(u32 day_night_ratio) {} - virtual v3s16 getLightPosition() { return v3s16(0, 0, 0); } virtual bool getCollisionBox(aabb3f *toset) const { return false; } virtual bool getSelectionBox(aabb3f *toset) const { return false; } diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 644a71e6e..eec4e3de0 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -182,7 +182,6 @@ public: void addToScene(ITextureSource *tsrc); void removeFromScene(bool permanent); void updateLight(u32 day_night_ratio); - v3s16 getLightPosition(); void updateNodePos(); void step(float dtime, ClientEnvironment *env); @@ -258,11 +257,6 @@ void TestCAO::updateLight(u32 day_night_ratio) { } -v3s16 TestCAO::getLightPosition() -{ - return floatToInt(m_position, BS); -} - void TestCAO::updateNodePos() { if (!m_node) @@ -799,13 +793,20 @@ void GenericCAO::updateLight(u32 day_night_ratio) return; u8 light_at_pos = 0; - bool pos_ok; - - v3s16 p = getLightPosition(); - MapNode n = m_env->getMap().getNode(p, &pos_ok); - if (pos_ok) - light_at_pos = n.getLightBlend(day_night_ratio, m_client->ndef()); - else + bool pos_ok = false; + + v3s16 pos[3]; + u16 npos = getLightPosition(pos); + for (u16 i = 0; i < npos; i++) { + bool this_ok; + MapNode n = m_env->getMap().getNode(pos[i], &this_ok); + if (this_ok) { + u8 this_light = n.getLightBlend(day_night_ratio, m_client->ndef()); + light_at_pos = MYMAX(light_at_pos, this_light); + pos_ok = true; + } + } + if (!pos_ok) light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0); u8 light = decode_light(light_at_pos + m_glow); @@ -856,12 +857,17 @@ void GenericCAO::setNodeLight(u8 light) } } -v3s16 GenericCAO::getLightPosition() +u16 GenericCAO::getLightPosition(v3s16 *pos) { - if (m_is_player) - return floatToInt(m_position + v3f(0, 0.5 * BS, 0), BS); - - return floatToInt(m_position, BS); + const auto &box = m_prop.collisionbox; + pos[0] = floatToInt(m_position + box.MinEdge * BS, BS); + pos[1] = floatToInt(m_position + box.MaxEdge * BS, BS); + + // Skip center pos if it falls into the same node as Min or MaxEdge + if ((box.MaxEdge - box.MinEdge).getLengthSQ() < 3.0f) + return 2; + pos[2] = floatToInt(m_position + box.getCenter() * BS, BS); + return 3; } void GenericCAO::updateNametag() diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 7693dd3d2..699148c52 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -240,7 +240,10 @@ public: void setNodeLight(u8 light); - v3s16 getLightPosition(); + /* Get light position(s). + * returns number of positions written into pos[], which must have space + * for at least 3 vectors. */ + u16 getLightPosition(v3s16 *pos); void updateNametag(); -- cgit v1.2.3 From 2424dfe007e451bb02f87884c2b272cf307d6e7c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 13 Jun 2020 19:03:26 +0200 Subject: Server pushing media at runtime (#9961) --- doc/lua_api.txt | 14 +++ src/client/client.cpp | 12 ++- src/client/client.h | 9 +- src/client/clientmedia.cpp | 10 +- src/client/clientmedia.h | 5 + src/client/filecache.cpp | 8 ++ src/client/filecache.h | 1 + src/filesys.cpp | 6 ++ src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 46 +++++++++ src/network/networkprotocol.h | 9 ++ src/network/serveropcodes.cpp | 2 +- src/script/lua_api/l_server.cpp | 22 +++- src/script/lua_api/l_server.h | 3 + src/server.cpp | 195 +++++++++++++++++++++++------------- src/server.h | 4 + 16 files changed, 263 insertions(+), 85 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ed060c4ad..cb968958f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5217,6 +5217,20 @@ Server * Returns a code (0: successful, 1: no such player, 2: player is connected) * `minetest.remove_player_auth(name)`: remove player authentication data * Returns boolean indicating success (false if player nonexistant) +* `minetest.dynamic_add_media(filepath)` + * Adds the file at the given path to the media sent to clients by the server + on startup and also pushes this file to already connected clients. + The file must be a supported image, sound or model format. It must not be + modified, deleted, moved or renamed after calling this function. + The list of dynamically added media is not persisted. + * Returns boolean indicating success (duplicate files count as error) + * The media will be ready to use (in e.g. entity textures, sound_play) + immediately after calling this function. + Old clients that lack support for this feature will not see the media + unless they reconnect to the server. + * Since media transferred this way does not use client caching or HTTP + transfers, dynamic media should not be used with big files or performance + will suffer. Bans ---- diff --git a/src/client/client.cpp b/src/client/client.cpp index c03c062c6..34f97a9de 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -670,11 +670,9 @@ void Client::step(float dtime) } } -bool Client::loadMedia(const std::string &data, const std::string &filename) +bool Client::loadMedia(const std::string &data, const std::string &filename, + bool from_media_push) { - // Silly irrlicht's const-incorrectness - Buffer data_rw(data.c_str(), data.size()); - std::string name; const char *image_ext[] = { @@ -690,6 +688,9 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) io::IFileSystem *irrfs = RenderingEngine::get_filesystem(); video::IVideoDriver *vdrv = RenderingEngine::get_video_driver(); + // Silly irrlicht's const-incorrectness + Buffer data_rw(data.c_str(), data.size()); + // Create an irrlicht memory file io::IReadFile *rfile = irrfs->createMemoryReadFile( *data_rw, data_rw.getSize(), "_tempreadfile"); @@ -727,7 +728,6 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) ".x", ".b3d", ".md2", ".obj", NULL }; - name = removeStringEnd(filename, model_ext); if (!name.empty()) { verbosestream<<"Client: Storing model into memory: " @@ -744,6 +744,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, translate_ext); if (!name.empty()) { + if (from_media_push) + return false; TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); g_client_translations->loadTranslation(data); diff --git a/src/client/client.h b/src/client/client.h index 3b1095ac2..733634db1 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -222,6 +222,7 @@ public: void handleCommand_FormspecPrepend(NetworkPacket *pkt); void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); + void handleCommand_MediaPush(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -376,7 +377,8 @@ public: // The following set of functions is used by ClientMediaDownloader // Insert a media file appropriately into the appropriate manager - bool loadMedia(const std::string &data, const std::string &filename); + bool loadMedia(const std::string &data, const std::string &filename, + bool from_media_push = false); // Send a request for conventional media transfer void request_media(const std::vector &file_requests); @@ -488,6 +490,7 @@ private: Camera *m_camera = nullptr; Minimap *m_minimap = nullptr; bool m_minimap_disabled_by_server = false; + // Server serialization version u8 m_server_ser_ver; @@ -529,7 +532,6 @@ private: AuthMechanism m_chosen_auth_mech; void *m_auth_data = nullptr; - bool m_access_denied = false; bool m_access_denied_reconnect = false; std::string m_access_denied_reason = ""; @@ -538,7 +540,10 @@ private: bool m_nodedef_received = false; bool m_activeobjects_received = false; bool m_mods_loaded = false; + ClientMediaDownloader *m_media_downloader; + // Set of media filenames pushed by server at runtime + std::unordered_set m_media_pushed_files; // time_of_day speed approximation for old protocol bool m_time_of_day_set = false; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 6da99bbbf..8cd3b6bcc 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -35,6 +35,15 @@ static std::string getMediaCacheDir() return porting::path_cache + DIR_DELIM + "media"; } +bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &filedata) +{ + FileCache media_cache(getMediaCacheDir()); + std::string sha1_hex = hex_encode(raw_hash); + if (!media_cache.exists(sha1_hex)) + return media_cache.update(sha1_hex, filedata); + return true; +} + /* ClientMediaDownloader */ @@ -559,7 +568,6 @@ bool ClientMediaDownloader::checkAndLoad( return true; } - /* Minetest Hashset File Format diff --git a/src/client/clientmedia.h b/src/client/clientmedia.h index 92831082c..5a918535b 100644 --- a/src/client/clientmedia.h +++ b/src/client/clientmedia.h @@ -33,6 +33,11 @@ struct HTTPFetchResult; #define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS' #define MTHASHSET_FILE_NAME "index.mth" +// Store file into media cache (unless it exists already) +// Validating the hash is responsibility of the caller +bool clientMediaUpdateCache(const std::string &raw_hash, + const std::string &filedata); + class ClientMediaDownloader { public: diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index 3d1b302a8..46bbe4059 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -82,8 +82,16 @@ bool FileCache::update(const std::string &name, const std::string &data) std::string path = m_dir + DIR_DELIM + name; return updateByPath(path, data); } + bool FileCache::load(const std::string &name, std::ostream &os) { std::string path = m_dir + DIR_DELIM + name; return loadByPath(path, os); } + +bool FileCache::exists(const std::string &name) +{ + std::string path = m_dir + DIR_DELIM + name; + std::ifstream fis(path.c_str(), std::ios_base::binary); + return fis.good(); +} diff --git a/src/client/filecache.h b/src/client/filecache.h index 96e4c8ba1..ea6afc4b2 100644 --- a/src/client/filecache.h +++ b/src/client/filecache.h @@ -33,6 +33,7 @@ public: bool update(const std::string &name, const std::string &data); bool load(const std::string &name, std::ostream &os); + bool exists(const std::string &name); private: std::string m_dir; diff --git a/src/filesys.cpp b/src/filesys.cpp index f61b39b94..0bc351669 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -691,6 +691,12 @@ std::string AbsolutePath(const std::string &path) const char *GetFilenameFromPath(const char *path) { const char *filename = strrchr(path, DIR_DELIM_CHAR); + // Consistent with IsDirDelimiter this function handles '/' too + if (DIR_DELIM_CHAR != '/') { + const char *tmp = strrchr(path, '/'); + if (tmp && tmp > filename) + filename = tmp; + } return filename ? filename + 1 : path; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 0f20047c0..f812a08a1 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -68,7 +68,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A { "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B - null_command_handler, + { "TOCLIENT_MEDIA_PUSH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MediaPush }, // 0x2C null_command_handler, null_command_handler, { "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index e000acc92..5934eaf8c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" +#include "util/sha1.h" #include "tileanimation.h" #include "gettext.h" #include "skyparams.h" @@ -1471,6 +1472,51 @@ void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt) player->addVelocity(added_vel); } +void Client::handleCommand_MediaPush(NetworkPacket *pkt) +{ + std::string raw_hash, filename, filedata; + bool cached; + + *pkt >> raw_hash >> filename >> cached; + filedata = pkt->readLongString(); + + if (raw_hash.size() != 20 || filedata.empty() || filename.empty() || + !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + throw PacketError("Illegal filename, data or hash"); + } + + verbosestream << "Server pushes media file \"" << filename << "\" with " + << filedata.size() << " bytes of data (cached=" << cached + << ")" << std::endl; + + if (m_media_pushed_files.count(filename) != 0) { + // Silently ignore for synchronization purposes + return; + } + + // Compute and check checksum of data + std::string computed_hash; + { + SHA1 ctx; + ctx.addBytes(filedata.c_str(), filedata.size()); + unsigned char *buf = ctx.getDigest(); + computed_hash.assign((char*) buf, 20); + free(buf); + } + if (raw_hash != computed_hash) { + verbosestream << "Hash of file data mismatches, ignoring." << std::endl; + return; + } + + // Actually load media + loadMedia(filedata, filename, true); + m_media_pushed_files.insert(filename); + + // Cache file for the next time when this client joins the same server + if (cached) + clientMediaUpdateCache(raw_hash, filedata); +} + /* * Mod channels */ diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ab924f1db..fd683eac9 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -323,6 +323,15 @@ enum ToClientCommand v3f added_vel */ + TOCLIENT_MEDIA_PUSH = 0x2C, + /* + std::string raw_hash + std::string filename + bool should_be_cached + u32 len + char filedata[len] + */ + // (oops, there is some gap here) TOCLIENT_CHAT_MESSAGE = 0x2F, diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 6ee4ff256..2fc3197c2 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -167,7 +167,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A { "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B - null_command_factory, // 0x2C + { "TOCLIENT_MEDIA_PUSH", 0, true }, // 0x2C (sent over channel 1 too) null_command_factory, // 0x2D null_command_factory, // 0x2E { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index b6754938e..6f934bb9d 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "cpp_api/s_base.h" +#include "cpp_api/s_security.h" #include "server.h" #include "environment.h" #include "remoteplayer.h" @@ -412,9 +413,6 @@ int ModApiServer::l_get_modnames(lua_State *L) std::vector modlist; getServer(L)->getModNames(modlist); - // Take unsorted items from mods_unsorted and sort them into - // mods_sorted; not great performance but the number of mods on a - // server will likely be small. std::sort(modlist.begin(), modlist.end()); // Package them up for Lua @@ -474,6 +472,23 @@ int ModApiServer::l_sound_fade(lua_State *L) return 0; } +// dynamic_add_media(filepath) +int ModApiServer::l_dynamic_add_media(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Reject adding media before the server has started up + if (!getEnv(L)) + throw LuaError("Dynamic media cannot be added before server has started up"); + + std::string filepath = readParam(L, 1); + CHECK_SECURE_PATH(L, filepath.c_str(), false); + + bool ok = getServer(L)->dynamicAddMedia(filepath); + lua_pushboolean(L, ok); + return 1; +} + // is_singleplayer() int ModApiServer::l_is_singleplayer(lua_State *L) { @@ -538,6 +553,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); API_FCT(sound_fade); + API_FCT(dynamic_add_media); API_FCT(get_player_information); API_FCT(get_player_privs); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 3aa1785a2..938bfa8ef 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -70,6 +70,9 @@ private: // sound_fade(handle, step, gain) static int l_sound_fade(lua_State *L); + // dynamic_add_media(filepath) + static int l_dynamic_add_media(lua_State *L); + // get_player_privs(name, text) static int l_get_player_privs(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 6ecbd7097..fe2bb3840 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2405,9 +2405,87 @@ bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos) return true; } +bool Server::addMediaFile(const std::string &filename, + const std::string &filepath, std::string *filedata_to, + std::string *digest_to) +{ + // If name contains illegal characters, ignore the file + if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + infostream << "Server: ignoring illegal file name: \"" + << filename << "\"" << std::endl; + return false; + } + // If name is not in a supported format, ignore it + const char *supported_ext[] = { + ".png", ".jpg", ".bmp", ".tga", + ".pcx", ".ppm", ".psd", ".wal", ".rgb", + ".ogg", + ".x", ".b3d", ".md2", ".obj", + // Custom translation file format + ".tr", + NULL + }; + if (removeStringEnd(filename, supported_ext).empty()) { + infostream << "Server: ignoring unsupported file extension: \"" + << filename << "\"" << std::endl; + return false; + } + // 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; + return false; + } else if (filedata.empty()) { + errorstream << "Server::addMediaFile(): Empty file \"" + << filepath << "\"" << std::endl; + return false; + } + + SHA1 sha1; + sha1.addBytes(filedata.c_str(), filedata.length()); + + unsigned char *digest = sha1.getDigest(); + std::string sha1_base64 = base64_encode(digest, 20); + std::string sha1_hex = hex_encode((char*) digest, 20); + if (digest_to) + *digest_to = std::string((char*) digest, 20); + free(digest); + + // Put in list + m_media[filename] = MediaInfo(filepath, sha1_base64); + verbosestream << "Server: " << sha1_hex << " is " << filename + << std::endl; + + if (filedata_to) + *filedata_to = std::move(filedata); + return true; +} + void Server::fillMediaCache() { - infostream<<"Server: Calculating media file checksums"< paths; @@ -2419,80 +2497,15 @@ void Server::fillMediaCache() for (const std::string &mediapath : paths) { std::vector dirlist = fs::GetDirListing(mediapath); for (const fs::DirListNode &dln : dirlist) { - if (dln.dir) // Ignode dirs - continue; - std::string filename = dln.name; - // If name contains illegal characters, ignore the file - if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { - infostream<<"Server: ignoring illegal file name: \"" - << filename << "\"" << std::endl; - continue; - } - // If name is not in a supported format, ignore it - const char *supported_ext[] = { - ".png", ".jpg", ".bmp", ".tga", - ".pcx", ".ppm", ".psd", ".wal", ".rgb", - ".ogg", - ".x", ".b3d", ".md2", ".obj", - // Custom translation file format - ".tr", - NULL - }; - if (removeStringEnd(filename, supported_ext).empty()){ - infostream << "Server: ignoring unsupported file extension: \"" - << filename << "\"" << std::endl; + if (dln.dir) // Ignore dirs continue; - } - // Ok, attempt to load the file and add to cache - std::string filepath; - filepath.append(mediapath).append(DIR_DELIM).append(filename); - - // Read data - std::ifstream fis(filepath.c_str(), std::ios_base::binary); - if (!fis.good()) { - errorstream << "Server::fillMediaCache(): Could not open \"" - << filename << "\" for reading" << std::endl; - continue; - } - std::ostringstream tmp_os(std::ios_base::binary); - bool bad = false; - for(;;) { - char buf[1024]; - fis.read(buf, 1024); - std::streamsize len = fis.gcount(); - tmp_os.write(buf, len); - if (fis.eof()) - break; - if (!fis.good()) { - bad = true; - break; - } - } - if(bad) { - errorstream<<"Server::fillMediaCache(): Failed to read \"" - << filename << "\"" << std::endl; - continue; - } - if(tmp_os.str().length() == 0) { - errorstream << "Server::fillMediaCache(): Empty file \"" - << filepath << "\"" << std::endl; - continue; - } - - SHA1 sha1; - sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length()); - - unsigned char *digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest, 20); - std::string sha1_hex = hex_encode((char*)digest, 20); - free(digest); - - // Put in list - m_media[filename] = MediaInfo(filepath, sha1_base64); - verbosestream << "Server: " << sha1_hex << " is " << filename - << std::endl; + std::string filepath = mediapath; + filepath.append(DIR_DELIM).append(dln.name); + addMediaFile(dln.name, filepath); } } + + infostream << "Server: " << m_media.size() << " media files collected" << std::endl; } void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code) @@ -3428,6 +3441,44 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } +bool Server::dynamicAddMedia(const std::string &filepath) +{ + std::string filename = fs::GetFilenameFromPath(filepath.c_str()); + if (m_media.find(filename) != m_media.end()) { + errorstream << "Server::dynamicAddMedia(): file \"" << filename + << "\" already exists in media cache" << std::endl; + return false; + } + + // Load the file and add it to our media cache + std::string filedata, raw_hash; + bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash); + if (!ok) + return false; + + // Push file to existing clients + NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0); + pkt << raw_hash << filename << (bool) true; + pkt.putLongString(filedata); + + auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent); + for (session_t client_id : client_ids) { + /* + The network layer only guarantees ordered delivery inside a channel. + Since the very next packet could be one that uses the media, we have + to push the media over ALL channels to ensure it is processed before + it is used. + In practice this means we have to send it twice: + - channel 1 (HUD) + - channel 0 (everything else: e.g. play_sound, object messages) + */ + m_clients.send(client_id, 1, &pkt, true); + m_clients.send(client_id, 0, &pkt, true); + } + + return true; +} + // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, diff --git a/src/server.h b/src/server.h index 27943cc29..f44716531 100644 --- a/src/server.h +++ b/src/server.h @@ -236,6 +236,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); + bool dynamicAddMedia(const std::string &filepath); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); @@ -435,6 +437,8 @@ private: // Sends blocks to clients (locks env and con on its own) void SendBlocks(float dtime); + bool addMediaFile(const std::string &filename, const std::string &filepath, + std::string *filedata = nullptr, std::string *digest = nullptr); void fillMediaCache(); void sendMediaAnnouncement(session_t peer_id, const std::string &lang_code); void sendRequestedMedia(session_t peer_id, -- cgit v1.2.3 From e7e065f553b430173e9112ad55c7046cfc02f2c5 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 13 Jun 2020 22:46:20 +0200 Subject: Exposing the zoom key to Lua API (#9903) Co-authored-by: Raul Ferriz --- doc/lua_api.txt | 7 ++++--- src/client/game.cpp | 5 +++-- src/network/serverpackethandler.cpp | 1 + src/script/lua_api/l_object.cpp | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cb968958f..07e9698e8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6106,13 +6106,14 @@ object you are working with still exists. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up. + keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false}` + sneak=true, aux1=false, down=false, up=false, zoom=false}` + * The `zoom` field is available since 5.3 * `get_player_control_bits()`: returns integer with bit packed player pressed keys. * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB + 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) diff --git a/src/client/game.cpp b/src/client/game.cpp index 139742cec..069c482ca 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2490,7 +2490,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); - u32 keypress_bits = + u32 keypress_bits = ( ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | @@ -2499,7 +2499,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); #ifdef ANDROID diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index fed3b6f85..b3008bb50 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -501,6 +501,7 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, player->control.sneak = (keyPressed & 64); player->control.LMB = (keyPressed & 128); player->control.RMB = (keyPressed & 256); + player->control.zoom = (keyPressed & 512); if (playersao->checkMovementCheat()) { // Call callbacks diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0a9f3117b..e7394133a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1459,6 +1459,8 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "LMB"); lua_pushboolean(L, control.RMB); lua_setfield(L, -2, "RMB"); + lua_pushboolean(L, control.zoom); + lua_setfield(L, -2, "zoom"); return 1; } -- cgit v1.2.3 From 3a6dfda358112e2fb5d6f887a1a1e936eacddadd Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Tue, 16 Jun 2020 22:48:31 +0300 Subject: Make shading of CAOs optional (#10033) --- client/shaders/object_shader/opengl_vertex.glsl | 4 ++++ doc/lua_api.txt | 3 +++ games/devtest/mods/testentities/visuals.lua | 12 +++++++++++- src/client/content_cao.cpp | 15 +++++++++++---- src/client/mesh.cpp | 2 +- src/client/shader.cpp | 6 +++++- src/client/tile.h | 2 ++ src/object_properties.cpp | 6 ++++++ src/object_properties.h | 1 + src/script/common/c_content.cpp | 3 +++ 10 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src/client') diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 59171145f..968a07e22 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -39,11 +39,15 @@ void main(void) lightVec = sunPosition - worldPosition; eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; +#if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA) + vIDiff = 1.0; +#else // This is intentional comparison with zero without any margin. // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector vIDiff = length(gl_Normal) == 0.0 ? 1.0 : directional_ambient(normalize(gl_Normal)); +#endif gl_FrontColor = gl_BackColor = gl_Color; } diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 07e9698e8..11c5cfb91 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6644,6 +6644,9 @@ Player properties need to be saved manually. damage_texture_modifier = "^[brighten", -- Texture modifier to be applied for a short duration when object is hit + + shaded = true, + -- Setting this to 'false' disables diffuse lighting of entity } Entity definition diff --git a/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index 314f51657..83f361f16 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -55,6 +55,17 @@ minetest.register_entity("testentities:mesh", { }, }) +minetest.register_entity("testentities:mesh_unshaded", { + initial_properties = { + visual = "mesh", + mesh = "testnodes_pyramid.obj", + textures = { + "testnodes_mesh_stripes2.png" + }, + shaded = false, + }, +}) + -- Advanced visual tests -- A test entity for testing animated and yaw-modulated sprites @@ -71,4 +82,3 @@ minetest.register_entity("testentities:yawsprite", { self.object:set_sprite({x=0, y=0}, 1, 0, true) end, }) - diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index eec4e3de0..5352c35f4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -577,10 +577,16 @@ void GenericCAO::addToScene(ITextureSource *tsrc) if (m_enable_shaders) { IShaderSource *shader_source = m_client->getShaderSource(); - u32 shader_id = shader_source->getShader( - "object_shader", - (m_prop.use_texture_alpha) ? TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC, - NDT_NORMAL); + MaterialType material_type; + + if (m_prop.shaded && m_prop.glow == 0) + material_type = (m_prop.use_texture_alpha) ? + TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC; + else + material_type = (m_prop.use_texture_alpha) ? + TILE_MATERIAL_PLAIN_ALPHA : TILE_MATERIAL_PLAIN; + + u32 shader_id = shader_source->getShader("object_shader", material_type, NDT_NORMAL); m_material_type = shader_source->getShaderInfo(shader_id).material; } else { m_material_type = (m_prop.use_texture_alpha) ? @@ -1504,6 +1510,7 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const return old.backface_culling != new_.backface_culling || old.is_visible != new_.is_visible || old.mesh != new_.mesh || + old.shaded != new_.shaded || old.use_texture_alpha != new_.use_texture_alpha || old.visual != new_.visual || old.visual_size != new_.visual_size || diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 68832849e..e1ec22068 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -341,7 +341,7 @@ bool checkMeshNormals(scene::IMesh *mesh) // hurting the performance and covering only really weird broken models. f32 length = buffer->getNormal(0).getLength(); - if (!std::isfinite(length) || std::fabs(length) < 1e-10f) + if (!std::isfinite(length) || length < 1e-10f) return false; } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index eda415ce6..ee6079f7a 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -537,11 +537,13 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.base_material = video::EMT_SOLID; break; case TILE_MATERIAL_ALPHA: + case TILE_MATERIAL_PLAIN_ALPHA: case TILE_MATERIAL_LIQUID_TRANSPARENT: case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_BASIC: + case TILE_MATERIAL_PLAIN: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: case TILE_MATERIAL_WAVING_LIQUID_BASIC: @@ -644,9 +646,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp "TILE_MATERIAL_WAVING_LIQUID_BASIC", "TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT", "TILE_MATERIAL_WAVING_LIQUID_OPAQUE", + "TILE_MATERIAL_PLAIN", + "TILE_MATERIAL_PLAIN_ALPHA", }; - for (int i = 0; i < 10; i++){ + for (int i = 0; i < 12; i++){ shaders_header += "#define "; shaders_header += materialTypes[i]; shaders_header += " "; diff --git a/src/client/tile.h b/src/client/tile.h index 533df676e..52e0a2b2b 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -150,6 +150,8 @@ enum MaterialType{ TILE_MATERIAL_WAVING_LIQUID_BASIC, TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT, TILE_MATERIAL_WAVING_LIQUID_OPAQUE, + TILE_MATERIAL_PLAIN, + TILE_MATERIAL_PLAIN_ALPHA }; // Material flags diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 6ff344dce..8d51bcbfa 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -69,6 +69,7 @@ std::string ObjectProperties::dump() os << ", zoom_fov=" << zoom_fov; os << ", use_texture_alpha=" << use_texture_alpha; os << ", damage_texture_modifier=" << damage_texture_modifier; + os << ", shaded=" << shaded; return os.str(); } @@ -116,6 +117,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeF32(os, zoom_fov); writeU8(os, use_texture_alpha); os << serializeString(damage_texture_modifier); + writeU8(os, shaded); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -170,5 +172,9 @@ void ObjectProperties::deSerialize(std::istream &is) use_texture_alpha = readU8(is); try { damage_texture_modifier = deSerializeString(is); + u8 tmp = readU8(is); + if (is.eof()) + throw SerializationError(""); + shaded = tmp; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index f7848f5a2..f010c1daf 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -61,6 +61,7 @@ struct ObjectProperties float eye_height = 1.625f; float zoom_fov = 0.0f; bool use_texture_alpha = false; + bool shaded = true; ObjectProperties(); std::string dump(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 2157d087d..3dfd7ce61 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -327,6 +327,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); getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } @@ -411,6 +412,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "zoom_fov"); lua_pushboolean(L, prop->use_texture_alpha); lua_setfield(L, -2, "use_texture_alpha"); + lua_pushboolean(L, prop->shaded); + 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"); } -- cgit v1.2.3 From 495f7198ef8336048fd3a03a9705fe45dbd57574 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 18 Jun 2020 15:11:54 +0200 Subject: content_cao: Fix behavior of legacy "textures" field for wielditems --- src/client/content_cao.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 5352c35f4..7f573b5a1 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1506,6 +1506,9 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const * - glow: handled by updateLight() * - any other properties that do not change appearance */ + + bool uses_legacy_texture = new_.wield_item.empty() && + (new_.visual == "wielditem" || new_.visual == "item"); // Ordered to compare primitive types before std::vectors return old.backface_culling != new_.backface_culling || old.is_visible != new_.is_visible || @@ -1515,7 +1518,8 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const old.visual != new_.visual || old.visual_size != new_.visual_size || old.wield_item != new_.wield_item || - old.colors != new_.colors; + old.colors != new_.colors || + (uses_legacy_texture && old.textures != new_.textures); } void GenericCAO::processMessage(const std::string &data) -- cgit v1.2.3 From 57df895cf93d79293a8b47802f9523bafcaa330f Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 19 Jun 2020 19:29:47 +0200 Subject: ParticleSpawner: Fix crash when attaching to invisible entity --- src/client/content_cao.h | 7 ++++--- src/client/particles.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 699148c52..974ff9a1e 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -188,10 +188,11 @@ public: return m_matrixnode->getRelativeTransformationMatrix(); } - inline const core::matrix4 &getAbsolutePosRotMatrix() const + inline const core::matrix4 *getAbsolutePosRotMatrix() const { - assert(m_matrixnode); - return m_matrixnode->getAbsoluteTransformation(); + if (!m_matrixnode) + return nullptr; + return &m_matrixnode->getAbsoluteTransformation(); } inline f32 getStepHeight() const diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c78a3e71a..7acd996dc 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -349,7 +349,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) const core::matrix4 *attached_absolute_pos_rot_matrix = nullptr; if (m_attached_id) { if (GenericCAO *attached = dynamic_cast(env->getActiveObject(m_attached_id))) { - attached_absolute_pos_rot_matrix = &attached->getAbsolutePosRotMatrix(); + attached_absolute_pos_rot_matrix = attached->getAbsolutePosRotMatrix(); } else { unloaded = true; } -- cgit v1.2.3 From 7be082f9a8bb6bd46c226d7ef4c42f0fd9fe7314 Mon Sep 17 00:00:00 2001 From: hecktest <42101236+hecktest@users.noreply.github.com> Date: Fri, 26 Jun 2020 00:06:29 +0200 Subject: Fix bone-attached entities (#10015) --- src/client/content_cao.cpp | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 7f573b5a1..4f949f6b0 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -162,6 +162,15 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, matrix.setTextureScale(txs, tys); } +// Evaluate transform chain recursively; irrlicht does not do this for us +static void updatePositionRecursive(scene::ISceneNode *node) +{ + scene::ISceneNode *parent = node->getParent(); + if (parent) + updatePositionRecursive(parent); + node->updateAbsolutePosition(); +} + /* TestCAO */ @@ -929,11 +938,6 @@ void GenericCAO::updateNodePos() void GenericCAO::step(float dtime, ClientEnvironment *env) { - if (m_animated_meshnode) { - m_animated_meshnode->animateJoints(); - updateBonePosition(); - } - // Handle model animations and update positions instantly to prevent lags if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); @@ -1143,6 +1147,18 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) rot_translator.val_current = m_rotation; updateNodePos(); } + + if (m_animated_meshnode) { + // Everything must be updated; the whole transform + // chain as well as the animated mesh node. + // Otherwise, bone attachments would be relative to + // a position that's one frame old. + if (m_matrixnode) + updatePositionRecursive(m_matrixnode); + m_animated_meshnode->updateAbsolutePosition(); + m_animated_meshnode->animateJoints(); + updateBonePosition(); + } } void GenericCAO::updateTexturePos() @@ -1444,6 +1460,18 @@ void GenericCAO::updateBonePosition() bone->updateAbsolutePosition(); } } + // The following is needed for set_bone_pos to propagate to + // attached objects correctly. + // Irrlicht ought to do this, but doesn't when using EJUOR_CONTROL. + for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) { + auto bone = m_animated_meshnode->getJointNode(i); + // Look for the root bone. + if (bone && bone->getParent() == m_animated_meshnode) { + // Update entire skeleton. + bone->updateAbsolutePositionOfAllChildren(); + break; + } + } } void GenericCAO::updateAttachments() -- cgit v1.2.3 From dc6318b84aa8c079330b2adc711113f7d4b55961 Mon Sep 17 00:00:00 2001 From: Paramat Date: Sun, 5 Jul 2020 23:45:21 +0100 Subject: Apply camera smoothing to 'air stepheight' (#10025) Recent changes to collision code have changed the behaviour of the 'touching_ground' bool in movement code. This had the effect of disabling camera smoothing when 'air stepheight' occurred when jumping onto a node while pressing forwards against the node, causing an unpleasant sharp camera movement. Rewrite the conditions for camera smoothing such that it is applied when jumping. --- src/client/camera.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/client') diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 9b311171a..abc55e4b7 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -342,9 +342,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r if (player->getParent()) player_position = player->getParent()->getPosition(); - if(player->touching_ground && - player_position.Y > old_player_position.Y) - { + // Smooth the camera movement when the player instantly moves upward due to stepheight. + // To smooth the 'not touching_ground' stepheight, smoothing is necessary when jumping + // or swimming (for when moving from liquid to land). + // Disable smoothing if climbing or flying, to avoid upwards offset of player model + // when seen in 3rd person view. + bool flying = g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly"); + if (player_position.Y > old_player_position.Y && !player->is_climbing && !flying) { f32 oldy = old_player_position.Y; f32 newy = player_position.Y; f32 t = std::exp(-23 * frametime); @@ -607,14 +611,11 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r const bool walking = movement_XZ && player->touching_ground; const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid; const bool climbing = movement_Y && player->is_climbing; - if ((walking || swimming || climbing) && - (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) { + if ((walking || swimming || climbing) && !flying) { // Start animation m_view_bobbing_state = 1; m_view_bobbing_speed = MYMIN(speed.getLength(), 70); - } - else if (m_view_bobbing_state == 1) - { + } else if (m_view_bobbing_state == 1) { // Stop animation m_view_bobbing_state = 2; m_view_bobbing_speed = 60; -- cgit v1.2.3 From ebb721a476ada0350b73fe44efa66850397b9e96 Mon Sep 17 00:00:00 2001 From: TheTermos <55103816+TheTermos@users.noreply.github.com> Date: Fri, 3 Jul 2020 17:21:42 +0200 Subject: Fix player controls only being applied for the first move --- src/client/client.cpp | 5 +---- src/client/clientenvironment.cpp | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client') diff --git a/src/client/client.cpp b/src/client/client.cpp index 34f97a9de..65e5b3d8c 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -459,12 +459,9 @@ void Client::step(float dtime) /* Handle environment */ - // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - player->applyControl(dtime, &m_env); - // Step environment + // Step environment (also handles player controls) m_env.step(dtime); m_sound->step(dtime); diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 44ea1e4a1..895b0193c 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -216,6 +216,9 @@ void ClientEnvironment::step(float dtime) */ { + // Control local player + lplayer->applyControl(dtime_part, this); + // Apply physics if (!free_move && !is_climbing) { // Gravity -- cgit v1.2.3 From c6422e087257d661a30ee2cd281ccec91f2f90ca Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Jul 2020 12:11:44 +0200 Subject: Remove std::shared_ptr use in TileLayer (#10090) --- src/client/tile.h | 3 +-- src/nodedef.cpp | 14 +++++++++++++- src/nodedef.h | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src/client') diff --git a/src/client/tile.h b/src/client/tile.h index 52e0a2b2b..e1d6c348f 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include #include "util/numeric.h" #include "config.h" @@ -284,7 +283,7 @@ struct TileLayer //! If true, the tile has its own color. bool has_color = false; - std::shared_ptr> frames = nullptr; + std::vector *frames = nullptr; /*! * The color of the tile, or if the tile does not own diff --git a/src/nodedef.cpp b/src/nodedef.cpp index a84338752..e5cd2c2a7 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -317,6 +317,18 @@ ContentFeatures::ContentFeatures() reset(); } +ContentFeatures::~ContentFeatures() +{ +#ifndef SERVER + for (u16 j = 0; j < 6; j++) { + delete tiles[j].layers[0].frames; + delete tiles[j].layers[1].frames; + } + for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) + delete special_tiles[j].layers[0].frames; +#endif +} + void ContentFeatures::reset() { /* @@ -662,7 +674,7 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, } else { std::ostringstream os(std::ios::binary); if (!layer->frames) { - layer->frames = std::make_shared>(); + layer->frames = new std::vector(); } layer->frames->resize(frame_count); diff --git a/src/nodedef.h b/src/nodedef.h index 0992001e1..cf03abaae 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -409,7 +409,7 @@ struct ContentFeatures */ ContentFeatures(); - ~ContentFeatures() = default; + ~ContentFeatures(); void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); -- cgit v1.2.3 From d80def5bbf58c9ddf1ab3d62e2e456b8dc824c98 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Fri, 10 Jul 2020 12:12:19 +0200 Subject: Bump animation frame count from u8 to u16 (#10054) --- src/client/tile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client') diff --git a/src/client/tile.h b/src/client/tile.h index e1d6c348f..49c46f749 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -271,7 +271,7 @@ struct TileLayer u32 texture_id = 0; u16 animation_frame_length_ms = 0; - u8 animation_frame_count = 1; + u16 animation_frame_count = 1; u8 material_type = TILE_MATERIAL_BASIC; u8 material_flags = -- cgit v1.2.3 From 2bec83eec0dc2de2d6b8fb0b827e94807ed9b0b8 Mon Sep 17 00:00:00 2001 From: v-rob Date: Sun, 12 Jul 2020 00:48:50 -0700 Subject: Add FormSpec font styling options (#9763) * Add FormSpec font styling options * Change multiplication to stof * Remove extraneous check --- doc/lua_api.txt | 18 +++++++++++++++++ src/client/fontengine.cpp | 15 ++++++++++++++ src/client/fontengine.h | 3 +++ src/gui/StyleSpec.h | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/gui/guiButton.cpp | 1 + src/gui/guiFormSpecMenu.cpp | 28 +++++++++++++++++--------- src/gui/guiFormSpecMenu.h | 2 +- 7 files changed, 105 insertions(+), 11 deletions(-) (limited to 'src/client') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4e6983be8..d3a367b27 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2747,6 +2747,18 @@ Some types may inherit styles from parent types. button's content when set. * bgimg_pressed - background image when pressed. Defaults to bgimg when not provided. * This is deprecated, use states instead. + * font - Sets font type. This is a comma separated list of options. Valid options: + * Main font type options. These cannot be combined with each other: + * `normal`: Default font + * `mono`: Monospaced font + * Font modification options. If used without a main font type, `normal` is used: + * `bold`: Makes font bold. + * `italic`: Makes font italic. + Default `normal`. + * font_size - Sets font size. Default is user-set. Can have multiple values: + * ``: Sets absolute font size to `number`. + * `+`/`-`: Offsets default font size by `number` points. + * `*`: Multiplies default font size by `number`, similar to CSS `em`. * border - boolean, draw border. Set to false to hide the bevelled button pane. Default true. * content_offset - 2d vector, shifts the position of the button's content without resizing it. * noclip - boolean, set to true to allow the element to exceed formspec bounds. @@ -2758,11 +2770,15 @@ Some types may inherit styles from parent types. * scrollbar * noclip - boolean, set to true to allow the element to exceed formspec bounds. * table, textlist + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. * field, pwdfield, textarea * border - set to false to hide the textbox background and border. Default true. + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. * image @@ -2771,6 +2787,8 @@ Some types may inherit styles from parent types. * item_image * noclip - boolean, set to true to allow the element to exceed formspec bounds. Default to false. * label, vertlabel + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * image_button (additional properties) * fgimg - standard image. Defaults to none. diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 61d52cc2f..a55420846 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -186,6 +186,21 @@ unsigned int FontEngine::getDefaultFontSize() return m_default_size[m_currentMode]; } +unsigned int FontEngine::getFontSize(FontMode mode) +{ + if (m_currentMode == FM_Simple) { + if (mode == FM_Mono || mode == FM_SimpleMono) + return m_default_size[FM_SimpleMono]; + else + return m_default_size[FM_Simple]; + } + + if (mode == FM_Unspecified) + return m_default_size[FM_Standard]; + + return m_default_size[mode]; +} + /******************************************************************************/ void FontEngine::readSettings() { diff --git a/src/client/fontengine.h b/src/client/fontengine.h index 53f14c45f..865b2d3ff 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -124,6 +124,9 @@ public: /** get default font size */ unsigned int getDefaultFontSize(); + /** get font size for a specific mode */ + unsigned int getFontSize(FontMode mode); + /** initialize font engine */ void initialize(Settings* main_settings, gui::IGUIEnvironment* env); diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 3e842e826..67caf4f7b 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -18,9 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "client/tile.h" // ITextureSource +#include "client/fontengine.h" #include "debug.h" #include "irrlichttypes_extrabloated.h" #include "util/string.h" +#include #include #pragma once @@ -46,6 +48,8 @@ public: ALPHA, CONTENT_OFFSET, PADDING, + FONT, + FONT_SIZE, NUM_PROPERTIES, NONE }; @@ -98,6 +102,10 @@ public: return CONTENT_OFFSET; } else if (name == "padding") { return PADDING; + } else if (name == "font") { + return FONT; + } else if (name == "font_size") { + return FONT_SIZE; } else { return NONE; } @@ -225,6 +233,47 @@ public: return vec; } + gui::IGUIFont *getFont() const + { + FontSpec spec(FONT_SIZE_UNSPECIFIED, FM_Standard, false, false); + + const std::string &font = properties[FONT]; + const std::string &size = properties[FONT_SIZE]; + + if (font.empty() && size.empty()) + return nullptr; + + std::vector modes = split(font, ','); + + for (size_t i = 0; i < modes.size(); i++) { + if (modes[i] == "normal") + spec.mode = FM_Standard; + else if (modes[i] == "mono") + spec.mode = FM_Mono; + else if (modes[i] == "bold") + spec.bold = true; + else if (modes[i] == "italic") + spec.italic = true; + } + + if (!size.empty()) { + int calc_size = 1; + + if (size[0] == '*') { + std::string new_size = size.substr(1); // Remove '*' (invalid for stof) + calc_size = stof(new_size) * g_fontengine->getFontSize(spec.mode); + } else if (size[0] == '+' || size[0] == '-') { + calc_size = stoi(size) + g_fontengine->getFontSize(spec.mode); + } else { + calc_size = stoi(size); + } + + spec.size = (unsigned)std::min(std::max(calc_size, 1), 999); + } + + return g_fontengine->getFont(spec); + } + video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc, video::ITexture *def) const { diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 6732a9233..e0d6337cd 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -788,6 +788,7 @@ void GUIButton::setFromStyle(const StyleSpec& style) setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); setDrawBorder(style.getBool(StyleSpec::BORDER, true)); setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); + setOverrideFont(style.getFont()); if (style.isNotDefault(StyleSpec::BGIMG)) { video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 9618e2134..db89d2c43 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -24,8 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "guiFormSpecMenu.h" -#include "guiScrollBar.h" -#include "guiTable.h" #include "constants.h" #include "gamedef.h" #include "client/keycode.h" @@ -64,9 +62,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiEditBoxWithScrollbar.h" #include "guiInventoryList.h" #include "guiItemImage.h" -#include "guiScrollBar.h" #include "guiScrollContainer.h" -#include "guiTable.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" @@ -1482,6 +1478,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + e->setOverrideFont(style.getFont()); irr::SEvent evt; evt.EventType = EET_KEY_INPUT_EVENT; @@ -1565,6 +1562,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, if (style.get(StyleSpec::BGCOLOR, "") == "transparent") { e->setDrawBackground(false); } + e->setOverrideFont(style.getFont()); e->drop(); } @@ -1778,6 +1776,11 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) std::vector lines = split(text, '\n'); + auto style = getDefaultStyleForElement("label", ""); + gui::IGUIFont *font = style.getFont(); + if (!font) + font = m_font; + for (unsigned int i = 0; i != lines.size(); i++) { std::wstring wlabel_colors = translate_string( utf8_to_wide(unescape_string(lines[i]))); @@ -1799,7 +1802,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) rect = core::rect( pos.X, pos.Y, - pos.X + m_font->getDimension(wlabel_plain.c_str()).Width, + pos.X + font->getDimension(wlabel_plain.c_str()).Width, pos.Y + imgsize.Y); } else { @@ -1821,7 +1824,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) rect = core::rect( pos.X, pos.Y - m_btn_height, - pos.X + m_font->getDimension(wlabel_plain.c_str()).Width, + pos.X + font->getDimension(wlabel_plain.c_str()).Width, pos.Y + m_btn_height); } @@ -1837,9 +1840,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); - auto style = getDefaultStyleForElement("label", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + e->setOverrideFont(font); m_fields.push_back(spec); @@ -1867,6 +1870,11 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen MY_CHECKPOS("vertlabel",1); + auto style = getDefaultStyleForElement("vertlabel", "", "label"); + gui::IGUIFont *font = style.getFont(); + if (!font) + font = m_font; + v2s32 pos; core::rect rect; @@ -1880,7 +1888,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen // isn't quite tall enough and cuts off the text. rect = core::rect(pos.X, pos.Y, pos.X + imgsize.X, - pos.Y + font_line_height(m_font) * + pos.Y + font_line_height(font) * (text.length() + 1)); } else { @@ -1892,7 +1900,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen rect = core::rect( pos.X, pos.Y+((imgsize.Y/2) - m_btn_height), pos.X+15, pos.Y + - font_line_height(m_font) * + font_line_height(font) * (text.length() + 1) + ((imgsize.Y/2) - m_btn_height)); } @@ -1917,9 +1925,9 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen rect, false, false, data->current_parent, spec.fid); e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + e->setOverrideFont(font); m_fields.push_back(spec); diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 19026bd34..5a6b467aa 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "modalMenu.h" #include "guiInventoryList.h" +#include "guiScrollBar.h" #include "guiTable.h" #include "network/networkprotocol.h" #include "client/joystick_controller.h" @@ -37,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., class InventoryManager; class ISimpleTextureSource; class Client; -class GUIScrollBar; class TexturePool; class GUIScrollContainer; -- cgit v1.2.3 From 4fa1e03f6844a24fc4b37f22e7264957b2a71d06 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 14 Jul 2020 19:10:37 +0200 Subject: Cleanup ClientLauncher structure (#10160) Remove duplicated variables and unify the startup data into a new (inherited) struct. --- src/client/clientlauncher.cpp | 218 ++++++++++++++++++++++-------------------- src/client/clientlauncher.h | 23 +---- src/client/game.cpp | 102 +++++++------------- src/client/game.h | 13 +-- src/client/inputhandler.h | 10 ++ src/gameparams.h | 19 ++++ src/main.cpp | 12 ++- 7 files changed, 197 insertions(+), 200 deletions(-) (limited to 'src/client') diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index f18915a55..ce16797e6 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -56,6 +56,20 @@ bool isMenuActive() // Passed to menus to allow disconnecting and exiting MainGameCallback *g_gamecallback = nullptr; +#if 0 +// This can be helpful for the next code cleanup +static void dump_start_data(const GameStartData &data) +{ + std::cout << + "\ndedicated " << (int)data.is_dedicated_server << + "\nport " << data.socket_port << + "\nworld_path " << data.world_spec.path << + "\nworld game " << data.world_spec.gameid << + "\ngame path " << data.game_spec.path << + "\nplayer name " << data.name << + "\naddress " << data.address << std::endl; +} +#endif ClientLauncher::~ClientLauncher() { @@ -74,9 +88,16 @@ ClientLauncher::~ClientLauncher() } -bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) +bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) { - init_args(game_params, cmd_args); + /* This function is called when a client must be started. + * Covered cases: + * - Singleplayer (address but map provided) + * - Join server (no map but address provided) + * - Local server (for main menu only) + */ + + init_args(start_data, cmd_args); // List video modes if requested if (list_video_modes) @@ -216,7 +237,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) core::rect(0, 0, 10000, 10000)); bool game_has_run = launch_game(error_message, reconnect_requested, - game_params, cmd_args); + start_data, cmd_args); // Reset the reconnect_requested flag reconnect_requested = false; @@ -241,13 +262,6 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) break; } - if (current_playername.length() > PLAYERNAME_SIZE-1) { - error_message = gettext("Player name too long."); - playername = current_playername.substr(0, PLAYERNAME_SIZE-1); - g_settings->set("name", playername); - continue; - } - RenderingEngine::get_video_driver()->setTextureCreationFlag( video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map")); @@ -258,18 +272,11 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) the_game( kill, - random_input, input, - worldspec.path, - current_playername, - current_password, - current_address, - current_port, + start_data, error_message, chat_backend, - &reconnect_requested, - gamespec, - simple_singleplayer_mode + &reconnect_requested ); RenderingEngine::get_scene_manager()->clear(); @@ -311,31 +318,27 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) return retval; } -void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args) +void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_args) { - skip_main_menu = cmd_args.getFlag("go"); - // FIXME: This is confusing (but correct) - - /* If world_path is set then override it unless skipping the main menu using - * the --go command line param. Else, give preference to the address - * supplied on the command line - */ - address = g_settings->get("address"); - if (!game_params.world_path.empty() && !skip_main_menu) - address = ""; - else if (cmd_args.exists("address")) - address = cmd_args.get("address"); + start_data.address = g_settings->get("address"); + if (cmd_args.exists("address")) { + // Join a remote server + start_data.address = cmd_args.get("address"); + start_data.world_path.clear(); + } + if (!start_data.world_path.empty()) { + // Start a singleplayer instance + start_data.address = ""; + } - playername = g_settings->get("name"); + start_data.name = g_settings->get("name"); if (cmd_args.exists("name")) - playername = cmd_args.get("name"); + start_data.name = cmd_args.get("name"); list_video_modes = cmd_args.getFlag("videomodes"); - use_freetype = g_settings->getBool("freetype"); - random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); } @@ -375,28 +378,20 @@ void ClientLauncher::init_input() } bool ClientLauncher::launch_game(std::string &error_message, - bool reconnect_requested, GameParams &game_params, + bool reconnect_requested, GameStartData &start_data, const Settings &cmd_args) { - // Initialize menu data - MainMenuData menudata; - menudata.address = address; - menudata.name = playername; - menudata.password = password; - menudata.port = itos(game_params.socket_port); - menudata.script_data.errormessage = error_message; - menudata.script_data.reconnect_requested = reconnect_requested; - + // Prepare and check the start data to launch a game + std::string error_message_lua = error_message; error_message.clear(); if (cmd_args.exists("password")) - menudata.password = cmd_args.get("password"); - + start_data.password = cmd_args.get("password"); if (cmd_args.exists("password-file")) { std::ifstream passfile(cmd_args.get("password-file")); if (passfile.good()) { - getline(passfile, menudata.password); + getline(passfile, start_data.password); } else { error_message = gettext("Provided password file " "failed to open: ") @@ -407,88 +402,112 @@ bool ClientLauncher::launch_game(std::string &error_message, } // If a world was commanded, append and select it - if (!game_params.world_path.empty()) { - worldspec.gameid = getWorldGameId(game_params.world_path, true); - worldspec.name = _("[--world parameter]"); + // This is provieded by "get_world_from_cmdline()", main.cpp + if (!start_data.world_path.empty()) { + auto &spec = start_data.world_spec; - if (worldspec.gameid.empty()) { // Create new - worldspec.gameid = g_settings->get("default_game"); - worldspec.name += " [new]"; + spec.path = start_data.world_path; + spec.gameid = getWorldGameId(spec.path, true); + spec.name = _("[--world parameter]"); + + if (spec.gameid.empty()) { // Create new + spec.gameid = g_settings->get("default_game"); + spec.name += " [new]"; } - worldspec.path = game_params.world_path; } /* 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) + MainMenuData menudata; + menudata.address = start_data.address; + menudata.name = start_data.name; + menudata.password = start_data.password; + menudata.port = itos(start_data.socket_port); + menudata.script_data.errormessage = error_message_lua; + menudata.script_data.reconnect_requested = reconnect_requested; + main_menu(&menudata); // Skip further loading if there was an exit signal. if (*porting::signal_handler_killstatus()) return false; - address = menudata.address; + if (!menudata.script_data.errormessage.empty()) { + /* The calling function will pass this back into this function upon the + * next iteration (if any) causing it to be displayed by the GUI + */ + error_message = menudata.script_data.errormessage; + return false; + } + int newport = stoi(menudata.port); if (newport != 0) - game_params.socket_port = newport; - - simple_singleplayer_mode = menudata.simple_singleplayer_mode; + start_data.socket_port = newport; + // Update world information using main menu data std::vector worldspecs = getAvailableWorlds(); - if (menudata.selected_world >= 0 - && menudata.selected_world < (int)worldspecs.size()) { + int world_index = menudata.selected_world; + if (world_index >= 0 && world_index < (int)worldspecs.size()) { g_settings->set("selected_world_path", - worldspecs[menudata.selected_world].path); - worldspec = worldspecs[menudata.selected_world]; + worldspecs[world_index].path); + start_data.world_spec = worldspecs[world_index]; } + + start_data.name = menudata.name; + start_data.password = menudata.password; + start_data.address = std::move(menudata.address); + server_name = menudata.servername; + server_description = menudata.serverdescription; + + start_data.local_server = !menudata.simple_singleplayer_mode && + start_data.address.empty(); } - if (!menudata.script_data.errormessage.empty()) { - /* The calling function will pass this back into this function upon the - * next iteration (if any) causing it to be displayed by the GUI - */ - error_message = menudata.script_data.errormessage; + if (!RenderingEngine::run()) return false; - } - if (menudata.name.empty() && !simple_singleplayer_mode) { + if (!start_data.isSinglePlayer() && start_data.name.empty()) { error_message = gettext("Please choose a name!"); errorstream << error_message << std::endl; return false; } - playername = menudata.name; - password = menudata.password; - - current_playername = playername; - current_password = password; - current_address = address; - current_port = game_params.socket_port; - // If using simple singleplayer mode, override - if (simple_singleplayer_mode) { - assert(!skip_main_menu); - current_playername = "singleplayer"; - current_password = ""; - current_address = ""; - current_port = myrand_range(49152, 65535); + if (start_data.isSinglePlayer()) { + start_data.name = "singleplayer"; + start_data.password = ""; + start_data.socket_port = myrand_range(49152, 65535); } else { - g_settings->set("name", playername); - if (!address.empty()) { + g_settings->set("name", start_data.name); + if (!start_data.address.empty()) { ServerListSpec server; - server["name"] = menudata.servername; - server["address"] = menudata.address; - server["port"] = menudata.port; - server["description"] = menudata.serverdescription; + server["name"] = server_name; + server["address"] = start_data.address; + server["port"] = itos(start_data.socket_port); + server["description"] = server_description; ServerList::insert(server); } } + if (start_data.name.length() > PLAYERNAME_SIZE - 1) { + error_message = gettext("Player name too long."); + start_data.name.resize(PLAYERNAME_SIZE); + g_settings->set("name", start_data.name); + return false; + } + + auto &worldspec = start_data.world_spec; infostream << "Selected world: " << worldspec.name << " [" << worldspec.path << "]" << std::endl; - if (current_address.empty()) { // If local game + if (start_data.address.empty()) { + // For singleplayer and local server if (worldspec.path.empty()) { error_message = gettext("No world selected and no address " "provided. Nothing to do."); @@ -504,8 +523,8 @@ bool ClientLauncher::launch_game(std::string &error_message, } // Load gamespec for required game - gamespec = findWorldSubgame(worldspec.path); - if (!gamespec.isValid() && !game_params.game_spec.isValid()) { + start_data.game_spec = findWorldSubgame(worldspec.path); + if (!start_data.game_spec.isValid()) { error_message = gettext("Could not find or load game \"") + worldspec.gameid + "\""; errorstream << error_message << std::endl; @@ -515,15 +534,7 @@ bool ClientLauncher::launch_game(std::string &error_message, if (porting::signal_handler_killstatus()) return true; - if (game_params.game_spec.isValid() && - game_params.game_spec.id != worldspec.gameid) { - warningstream << "Overriding gamespec from \"" - << worldspec.gameid << "\" to \"" - << game_params.game_spec.id << "\"" << std::endl; - gamespec = game_params.game_spec; - } - - if (!gamespec.isValid()) { + if (!start_data.game_spec.isValid()) { error_message = gettext("Invalid gamespec."); error_message += " (world.gameid=" + worldspec.gameid + ")"; errorstream << error_message << std::endl; @@ -531,6 +542,7 @@ bool ClientLauncher::launch_game(std::string &error_message, } } + start_data.world_path = start_data.world_spec.path; return true; } diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 2702895d6..b280d8e6b 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -32,15 +32,15 @@ public: ~ClientLauncher(); - bool run(GameParams &game_params, const Settings &cmd_args); + bool run(GameStartData &start_data, const Settings &cmd_args); -protected: - void init_args(GameParams &game_params, const Settings &cmd_args); +private: + void init_args(GameStartData &start_data, const Settings &cmd_args); bool init_engine(); void init_input(); bool launch_game(std::string &error_message, bool reconnect_requested, - GameParams &game_params, const Settings &cmd_args); + GameStartData &start_data, const Settings &cmd_args); void main_menu(MainMenuData *menudata); @@ -48,23 +48,8 @@ protected: bool list_video_modes = false; bool skip_main_menu = false; - bool use_freetype = false; bool random_input = false; - std::string address = ""; - std::string playername = ""; - std::string password = ""; InputHandler *input = nullptr; MyEventReceiver *receiver = nullptr; gui::IGUISkin *skin = nullptr; - gui::IGUIFont *font = nullptr; - SubgameSpec gamespec; - WorldSpec worldspec; - bool simple_singleplayer_mode = false; - - // These are set up based on the menu and other things - // TODO: Are these required since there's already playername, password, etc - std::string current_playername = "inv£lid"; - std::string current_password = ""; - std::string current_address = "does-not-exist"; - int current_port = 0; }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 069c482ca..5f3ff5649 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -34,11 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clouds.h" #include "config.h" #include "content_cao.h" +#include "content/subgames.h" #include "client/event_manager.h" #include "fontengine.h" #include "itemdef.h" #include "log.h" #include "filesys.h" +#include "gameparams.h" #include "gettext.h" #include "gui/guiChatConsole.h" #include "gui/guiConfirmRegistration.h" @@ -669,19 +671,11 @@ public: ~Game(); bool startup(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - // If address is "", local server is used and address is updated - std::string *address, - u16 port, + const GameStartData &game_params, std::string &error_message, bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode); + ChatBackend *chat_backend); void run(); void shutdown(); @@ -691,21 +685,18 @@ protected: void extendedResourceCleanup(); // Basic initialisation - bool init(const std::string &map_dir, std::string *address, - u16 port, - const SubgameSpec &gamespec); + bool init(const std::string &map_dir, const std::string &address, + u16 port, const SubgameSpec &gamespec); bool initSound(); bool createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address); + const SubgameSpec &gamespec, u16 port); // Client creation - bool createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port); + bool createClient(const GameStartData &start_data); bool initGui(); // Client connection - bool connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, + bool connectToServer(const GameStartData &start_data, bool *connect_ok, bool *aborted); bool getServerContent(bool *aborted); @@ -885,7 +876,6 @@ private: bool *reconnect_requested; scene::ISceneNode *skybox; - bool random_input; bool simple_singleplayer_mode; /* End 'cache' */ @@ -1017,28 +1007,21 @@ Game::~Game() } bool Game::startup(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - std::string *address, // can change if simple_singleplayer_mode - u16 port, + const GameStartData &start_data, std::string &error_message, bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, - bool simple_singleplayer_mode) + ChatBackend *chat_backend) { + // "cache" this->device = RenderingEngine::get_raw_device(); this->kill = kill; this->error_message = &error_message; this->reconnect_requested = reconnect; - this->random_input = random_input; this->input = input; this->chat_backend = chat_backend; - this->simple_singleplayer_mode = simple_singleplayer_mode; + this->simple_singleplayer_mode = start_data.isSinglePlayer(); input->keycache.populate(); @@ -1059,10 +1042,12 @@ bool Game::startup(bool *kill, g_client_translations->clear(); - if (!init(map_dir, address, port, gamespec)) + // address can change if simple_singleplayer_mode + if (!init(start_data.world_spec.path, start_data.address, + start_data.socket_port, start_data.game_spec)) return false; - if (!createClient(playername, password, address, port)) + if (!createClient(start_data)) return false; RenderingEngine::initialize(client, hud); @@ -1221,7 +1206,7 @@ void Game::shutdown() bool Game::init( const std::string &map_dir, - std::string *address, + const std::string &address, u16 port, const SubgameSpec &gamespec) { @@ -1245,8 +1230,8 @@ bool Game::init( return false; // Create a server if not connecting to an existing one - if (address->empty()) { - if (!createSingleplayerServer(map_dir, gamespec, port, address)) + if (address.empty()) { + if (!createSingleplayerServer(map_dir, gamespec, port)) return false; } @@ -1281,7 +1266,7 @@ bool Game::initSound() } bool Game::createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address) + const SubgameSpec &gamespec, u16 port) { showOverlayMessage(N_("Creating server..."), 0, 5); @@ -1314,8 +1299,7 @@ bool Game::createSingleplayerServer(const std::string &map_dir, return true; } -bool Game::createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port) +bool Game::createClient(const GameStartData &start_data) { showOverlayMessage(N_("Creating client..."), 0, 10); @@ -1330,8 +1314,7 @@ bool Game::createClient(const std::string &playername, g_touchscreengui->hide(); } #endif - if (!connectToServer(playername, password, address, port, - &could_connect, &connect_aborted)) + if (!connectToServer(start_data, &could_connect, &connect_aborted)) return false; if (!could_connect) { @@ -1463,8 +1446,7 @@ bool Game::initGui() return true; } -bool Game::connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, +bool Game::connectToServer(const GameStartData &start_data, bool *connect_ok, bool *connection_aborted) { *connect_ok = false; // Let's not be overly optimistic @@ -1473,10 +1455,10 @@ bool Game::connectToServer(const std::string &playername, showOverlayMessage(N_("Resolving address..."), 0, 15); - Address connect_address(0, 0, 0, 0, port); + Address connect_address(0, 0, 0, 0, start_data.socket_port); try { - connect_address.Resolve(address->c_str()); + connect_address.Resolve(start_data.address.c_str()); if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY //connect_address.Resolve("localhost"); @@ -1503,7 +1485,8 @@ bool Game::connectToServer(const std::string &playername, return false; } - client = new Client(playername.c_str(), password, *address, + client = new Client(start_data.name.c_str(), + start_data.password, start_data.address, *draw_control, texture_src, shader_src, itemdef_manager, nodedef_manager, sound, eventmgr, connect_address.isIPv6(), m_game_ui.get()); @@ -1574,13 +1557,13 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, + &g_menumgr, client, start_data.name, start_data.password, connection_aborted, texture_src))->drop(); } } else { wait_time += dtime; // Only time out if we aren't waiting for the server we started - if (!address->empty() && wait_time > 10) { + if (!start_data.isSinglePlayer() && wait_time > 10) { *error_message = "Connection timed out."; errorstream << *error_message << std::endl; break; @@ -2399,10 +2382,10 @@ void Game::checkZoomEnabled() void Game::updateCameraDirection(CameraOrientation *cam, float dtime) { if ((device->isWindowActive() && device->isWindowFocused() - && !isMenuActive()) || random_input) { + && !isMenuActive()) || input->isRandom()) { #ifndef __ANDROID__ - if (!random_input) { + if (!input->isRandom()) { // Mac OSX gets upset if this is set every frame if (device->getCursorControl()->isVisible()) device->getCursorControl()->setVisible(false); @@ -3349,7 +3332,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } // formspec in meta - if (meta && !meta->getString("formspec").empty() && !random_input + if (meta && !meta->getString("formspec").empty() && !input->isRandom() && !isKeyDown(KeyType::SNEAK)) { // on_rightclick callbacks are called anyway if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) @@ -4260,19 +4243,11 @@ void Game::showPauseMenu() /****************************************************************************/ void the_game(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - const std::string &address, // If empty local server is created - u16 port, - + const GameStartData &start_data, std::string &error_message, ChatBackend &chat_backend, - bool *reconnect_requested, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode) + bool *reconnect_requested) // Used for local game { Game game; @@ -4280,14 +4255,11 @@ void the_game(bool *kill, * is created then this is updated and we don't want to change the value * passed to us by the calling function */ - std::string server_address = address; try { - if (game.startup(kill, random_input, input, map_dir, - playername, password, &server_address, port, error_message, - reconnect_requested, &chat_backend, gamespec, - simple_singleplayer_mode)) { + if (game.startup(kill, input, start_data, error_message, + reconnect_requested, &chat_backend)) { game.run(); } diff --git a/src/client/game.h b/src/client/game.h index 69e6eed0b..d04153271 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class InputHandler; class ChatBackend; /* to avoid having to include chat.h */ struct SubgameSpec; +struct GameStartData; struct Jitter { f32 max, min, avg, counter, max_sample, min_sample, max_fraction; @@ -41,16 +42,10 @@ struct CameraOrientation { f32 camera_pitch; // "up/down" }; + void the_game(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - const std::string &address, // If "", local server is used - u16 port, + const GameStartData &start_data, std::string &error_message, ChatBackend &chat_backend, - bool *reconnect_requested, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode); + bool *reconnect_requested); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 9be2a2ed4..fc7998f20 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -219,6 +219,11 @@ public: virtual ~InputHandler() = default; + virtual bool isRandom() const + { + return false; + } + virtual bool isKeyDown(GameKeyType k) = 0; virtual bool wasKeyDown(GameKeyType k) = 0; virtual bool cancelPressed() = 0; @@ -372,6 +377,11 @@ class RandomInputHandler : public InputHandler public: RandomInputHandler() = default; + bool isRandom() const + { + return true; + } + virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; } virtual bool wasKeyDown(GameKeyType k) { return false; } virtual bool cancelPressed() { return false; } diff --git a/src/gameparams.h b/src/gameparams.h index 28794a649..70b0ffcde 100644 --- a/src/gameparams.h +++ b/src/gameparams.h @@ -23,10 +23,29 @@ with this program; if not, write to the Free Software Foundation, Inc., struct SubgameSpec; +// Information provided from "main" struct GameParams { + GameParams() = default; + u16 socket_port; std::string world_path; SubgameSpec game_spec; bool is_dedicated_server; }; + +// Information processed by main menu +struct GameStartData : GameParams +{ + GameStartData() = default; + + bool isSinglePlayer() const { return address.empty() && !local_server; } + + std::string name; + std::string password; + std::string address; + bool local_server; + + // "world_path" must be kept in sync! + WorldSpec world_spec; +}; diff --git a/src/main.cpp b/src/main.cpp index b3b17c2d1..af6d307dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -197,7 +197,7 @@ int main(int argc, char *argv[]) } #endif - GameParams game_params; + GameStartData game_params; #ifdef SERVER porting::attachOrCreateConsole(); game_params.is_dedicated_server = true; @@ -604,10 +604,14 @@ static bool game_configure(GameParams *game_params, const Settings &cmd_args) static void game_configure_port(GameParams *game_params, const Settings &cmd_args) { - if (cmd_args.exists("port")) + if (cmd_args.exists("port")) { game_params->socket_port = cmd_args.getU16("port"); - else - game_params->socket_port = g_settings->getU16("port"); + } else { + if (game_params->is_dedicated_server) + game_params->socket_port = g_settings->getU16("port"); + else + game_params->socket_port = g_settings->getU16("remote_port"); + } if (game_params->socket_port == 0) game_params->socket_port = DEFAULT_SERVER_PORT; -- cgit v1.2.3 From 88ffd641243ead70d82623d54822421c72893240 Mon Sep 17 00:00:00 2001 From: LoneWolfHT Date: Tue, 14 Jul 2020 10:12:17 -0700 Subject: Add object crosshair, disable entity selectionboxes by default (#9523) Adds new object crosshair base pack texture --- builtin/settingtypes.txt | 5 ++++- doc/texture_packs.txt | 7 ++++++- minetest.conf.example | 4 ++-- src/client/game.cpp | 3 +++ src/client/hud.cpp | 39 ++++++++++++++++++++++++++++++++++----- src/client/hud.h | 4 ++++ src/client/render/core.cpp | 1 + src/defaultsettings.cpp | 2 +- 8 files changed, 55 insertions(+), 10 deletions(-) (limited to 'src/client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index c787aea2c..c0620542d 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -741,9 +741,11 @@ selectionbox_color (Selection box color) string (0,0,0) selectionbox_width (Selection box width) int 2 1 5 # Crosshair color (R,G,B). +# Also controls the object crosshair color crosshair_color (Crosshair color) string (255,255,255) # Crosshair alpha (opaqueness, between 0 and 255). +# Also controls the object crosshair color crosshair_alpha (Crosshair alpha) int 255 0 255 # Maximum number of recent chat messages to show @@ -817,7 +819,8 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc autoscale_mode (Autoscaling mode) enum disable disable,enable,force # Show entity selection boxes -show_entity_selectionbox (Show entity selection boxes) bool true +# A restart is required after changing this. +show_entity_selectionbox (Show entity selection boxes) bool false [*Menus] diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 94151f1a4..e7a7dfd3c 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -72,7 +72,12 @@ by texture packs. All existing fallback textures can be found in the directory * `crosshair.png` * the crosshair texture in the center of the screen. The settings `crosshair_color` and `crosshair_alpha` are used to create a cross - when no texture was found + when no texture is found. + +* `object_crosshair.png` + * the crosshair seen when pointing at an object. The settings + `crosshair_color` and `crosshair_alpha` are used to create a cross + when no texture is found. * `halo.png`: used for the node highlighting mesh diff --git a/minetest.conf.example b/minetest.conf.example index a5f98ee5e..520125713 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -955,8 +955,9 @@ # autoscale_mode = disable # Show entity selection boxes +# A restart is required after changing this. # type: bool -# show_entity_selectionbox = true +# show_entity_selectionbox = false ## Menus @@ -3374,4 +3375,3 @@ # so see a full list at https://content.minetest.net/help/content_flags/ # type: string # contentdb_flag_blacklist = nonfree, desktop_default - diff --git a/src/client/game.cpp b/src/client/game.cpp index 5f3ff5649..42d60b21c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3176,11 +3176,14 @@ PointedThing Game::updatePointedThing( const NodeDefManager *nodedef = map.getNodeDefManager(); runData.selected_object = NULL; + hud->pointing_at_object = false; RaycastState s(shootline, look_for_object, liquids_pointable); PointedThing result; env.continueRaycast(&s, &result); if (result.type == POINTEDTHING_OBJECT) { + hud->pointing_at_object = true; + runData.selected_object = client->getEnv().getActiveObject(result.object_id); aabb3f selection_box; if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() && diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 31e633bc2..2b347c1e0 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -41,6 +41,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/touchscreengui.h" #endif +#define OBJECT_CROSSHAIR_LINE_SIZE 8 +#define CROSSHAIR_LINE_SIZE 10 + Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, Inventory *inventory) { @@ -76,6 +79,7 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b); use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png"); + use_object_crosshair_image = tsrc->isKnownSourceImage("object_crosshair.png"); m_selection_boxes.clear(); m_halo_boxes.clear(); @@ -601,6 +605,31 @@ void Hud::drawHotbar(u16 playeritem) { void Hud::drawCrosshair() { + if (pointing_at_object) { + if (use_object_crosshair_image) { + video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png"); + v2u32 size = object_crosshair->getOriginalSize(); + v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2), + m_displaycenter.Y - (size.Y / 2)); + driver->draw2DImage(object_crosshair, lsize, + core::rect(0, 0, size.X, size.Y), + nullptr, crosshair_argb, true); + } else { + driver->draw2DLine( + m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb); + driver->draw2DLine( + m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + -OBJECT_CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb); + } + + return; + } + if (use_crosshair_image) { video::ITexture *crosshair = tsrc->getTexture("crosshair.png"); v2u32 size = crosshair->getOriginalSize(); @@ -608,12 +637,12 @@ void Hud::drawCrosshair() m_displaycenter.Y - (size.Y / 2)); driver->draw2DImage(crosshair, lsize, core::rect(0, 0, size.X, size.Y), - 0, crosshair_argb, true); + nullptr, crosshair_argb, true); } else { - driver->draw2DLine(m_displaycenter - v2s32(10, 0), - m_displaycenter + v2s32(10, 0), crosshair_argb); - driver->draw2DLine(m_displaycenter - v2s32(0, 10), - m_displaycenter + v2s32(0, 10), crosshair_argb); + driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0), + m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb); + driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb); } } diff --git a/src/client/hud.h b/src/client/hud.h index 6f4c54626..ba34d479d 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -45,12 +45,16 @@ public: video::SColor crosshair_argb; video::SColor selectionbox_argb; + bool use_crosshair_image = false; + bool use_object_crosshair_image = false; std::string hotbar_image = ""; bool use_hotbar_image = false; std::string hotbar_selected_image = ""; bool use_hotbar_selected_image = false; + bool pointing_at_object = false; + Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, Inventory *inventory); ~Hud(); diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index bf5aa6c2c..92a7137ea 100644 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@ -86,6 +86,7 @@ void RenderingCore::drawHUD() if (show_hud) { if (draw_crosshair) hud->drawCrosshair(); + hud->drawHotbar(client->getEnv().getLocalPlayer()->getWieldIndex()); hud->drawLuaElements(camera->getOffset()); camera->drawNametags(); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index abb6593b7..07bf0ebb8 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -225,7 +225,7 @@ void set_default_settings(Settings *settings) settings->setDefault("desynchronize_mapblock_texture_animation", "true"); settings->setDefault("hud_hotbar_max_width", "1.0"); settings->setDefault("enable_local_map_saving", "false"); - settings->setDefault("show_entity_selectionbox", "true"); + 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"); -- cgit v1.2.3