diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml index 0eca700c8f4be..ce725a23bc2b5 100644 --- a/AppImageBuilder.yml +++ b/AppImageBuilder.yml @@ -7,8 +7,8 @@ AppDir: id: district53 name: District53 icon: minetest - version: 3.0.2 - exec: usr/local/bin/district53 + version: 5.9.0.001 + exec: usr/bin/District53 exec_args: $@ runtime: env: @@ -17,20 +17,24 @@ AppDir: apt: arch: amd64 sources: - - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main universe key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32' - - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main universe - - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main universe - - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy main universe + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920D1991BC93C' + include: - libc6 - libcurl3-gnutls + - libcurl4 - libfreetype6 - libgl1 - libjpeg-turbo8 - libjsoncpp1 - - libleveldb1v5 + - libleveldb1d - libopenal1 - libpng16-16 - libsqlite3-0 @@ -41,6 +45,9 @@ AppDir: - zlib1g - libluajit-5.1-dev - libssl-dev + - libmysqlclient-dev + - libfuse2 + - libjsoncpp25 files: exclude: @@ -52,6 +59,9 @@ AppDir: AppImage: update-information: None - #sign-key: None - sign-key: 0236A9D6C1366CDA + sign-key: D85EFD1CF2B8800C88B6A2DEEA8DF9DC979031BE arch: x86_64 + +script: | + cmake --install . + cp ../irrlicht./lib/Linux/libIrrlichtMt.so.1.9.0.15 AppDir/usr/lib/x86_64-linux-gnu/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cefa36107f7e..a6cf964ae177c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ add_subdirectory(lib/bip39/src) set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.") + if(ANDROID) # currently manually provided elseif(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "") @@ -432,3 +433,5 @@ if(BUILD_DOCUMENTATION) ) endif() endif() + + diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 73e762553665f..702a3d4872504 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -225,16 +225,16 @@ local function get_formspec(tabview, name, tabdata) -- Reset y so that the text fields always start at the same position, -- regardless of whether some of the checkboxes are hidden. - y = 0.2 + 4 * yo + 0.35 + y = 0.2 + 4 * yo retval = retval .. "field[0," .. y .. ";4.5,0.75;te_playername;" .. fgettext("Name") .. ";" .. core.formspec_escape(current_name) .. "]" - y = y + 1.15 + 0.25 + y = y + 1.15 retval = retval .. "pwdfield[0," .. y .. ";4.5,0.75;te_passwd;" .. fgettext("Password") .. "]" - y = y + 1.15 + 0.25 + y = y + 1.15 local bind_addr = core.settings:get("bind_address") if bind_addr ~= nil and bind_addr ~= "" then @@ -372,8 +372,21 @@ local function main_button_handler(this, fields, name, tabdata) end if core.settings:get_bool("enable_server") then - gamedata.playername = fields["te_playername"] - gamedata.password = fields["te_passwd"] + addresslistmgr.get_addresses() + local addressinfo = addresslistmgr.get_address(fields["te_playername"]) + + local sxpaddrvalidate = core.validate_sxp_password(addressinfo.encrypted_key, addressinfo.address, addressinfo.iv, fields.te_passwd) + if sxpaddrvalidate ~= 1 then + print("Invalid Password") + gamedata.errormessage = fgettext_ne("Invalid Password") + return true; + end + + gamedata.playername = addressinfo.address + gamedata.aliasname = gamedata.playername + gamedata.encrypted_key = addressinfo.encrypted_key + gamedata.iv = addressinfo.iv + gamedata.password = fields.te_passwd gamedata.port = fields["te_serverport"] gamedata.address = "" diff --git a/client/addresslist/addresslist.json b/client/addresslist/addresslist.json index 218895aff78d1..1b8cc6a80a9a6 100644 --- a/client/addresslist/addresslist.json +++ b/client/addresslist/addresslist.json @@ -1 +1 @@ -[{"address":"SVHj6VUDdfAFpXpqxYt5rDT7uCxi8nHNnn","encrypted_key":"y9n/SjA887+g8IaTI1rSXe5pRYV6V7S+fbOYmAg1XfaP7ARWbP+2fQDN+zNhOuH+fi6Bj81BdTUqiQ8imLTC4KV/Cs2ybqpeCGOQi6ECZClbJVwJQdP1rGL2q7ZySh03UoySKz5ziuoetKN1zZu7lsWP0EDD6025Qr5FAeIkalfDs1wSmhuBskTtd6YzNaxlFjB4yiTnRbvlGEmEuZnKdg","iv":"AgmkNBozlIZvOL5J"}] \ No newline at end of file +[{"address":"SZ1texxo4rwhLzm4zQEeMU4r7S8NkGAc81","encrypted_key":"tasCgyAkrqPWzekilfKxBfobpPPkgtiJrPlmKnZmI1Fh4M//FhB43Bg+p237uOOpq86SOoDNL3G0/vIlC0SeaRyvjlgNtAulvFMi5qxqK1NxxlAX50snDCmVwOehJCIkJvRJpXxI/WtDZGCV6VrtFulPnR7JeqCPSiNK4+mEAi12RDeu8NAbWCicQSB8iaOdWKNNa5ii7grHiAVZSmZ6mQ","iv":"2yivO5364kl0MSjJ"}] \ No newline at end of file diff --git a/ignore.conf b/ignore.conf index c1eb7c69e85d7..8baaadc752277 100644 --- a/ignore.conf +++ b/ignore.conf @@ -16,3 +16,25 @@ x64 /build /worlds/flowers /worlds/minecloneworld +/debug.txt +/minetest.dmp +/lib/cpp-crypto/extern/arduinojson/ArduinoJson-prefix/src/ArduinoJson-stamp/Debug +/lib/cpp-crypto/extern/arduinojson/ArduinoJson-prefix/src/ArduinoJson-stamp/MinSizeRel +/lib/cpp-crypto/extern/arduinojson/ArduinoJson-prefix/src/ArduinoJson-stamp/Release +/lib/cpp-crypto/extern/arduinojson/ArduinoJson-prefix/src/ArduinoJson-stamp/RelWithDebInfo +/lib/cpp-crypto/extern/bcl/BCL-prefix/src/BCL-stamp/Debug +/lib/cpp-crypto/extern/bcl/BCL-prefix/src/BCL-stamp/MinSizeRel +/lib/cpp-crypto/extern/bcl/BCL-prefix/src/BCL-stamp/Release +/lib/cpp-crypto/extern/bcl/BCL-prefix/src/BCL-stamp/RelWithDebInfo +/lib/cpp-crypto/extern/bip66/BIP66-prefix/src/BIP66-stamp/Debug +/lib/cpp-crypto/extern/bip66/BIP66-prefix/src/BIP66-stamp/MinSizeRel +/lib/cpp-crypto/extern/bip66/BIP66-prefix/src/BIP66-stamp/Release +/lib/cpp-crypto/extern/bip66/BIP66-prefix/src/BIP66-stamp/RelWithDebInfo +/lib/cpp-crypto/extern/uecc/UECC-prefix/src/UECC-stamp/Debug +/lib/cpp-crypto/extern/uecc/UECC-prefix/src/UECC-stamp/MinSizeRel +/lib/cpp-crypto/extern/uecc/UECC-prefix/src/UECC-stamp/Release +/lib/cpp-crypto/extern/uecc/UECC-prefix/src/UECC-stamp/RelWithDebInfo +/games/capturetheflag +/worlds/ctf +/games/solarcraft_game +/worlds/world diff --git a/lib/bip39/CMakeLists.txt b/lib/bip39/CMakeLists.txt index 56d97afbf3e0c..b87c972622a42 100644 --- a/lib/bip39/CMakeLists.txt +++ b/lib/bip39/CMakeLists.txt @@ -13,8 +13,8 @@ if (MSVC) -DNOMINMAX ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") endif() # clone submodules diff --git a/lib/cpp-crypto/CMakeLists.txt b/lib/cpp-crypto/CMakeLists.txt index 5091c36629f85..20b000c2e0363 100644 --- a/lib/cpp-crypto/CMakeLists.txt +++ b/lib/cpp-crypto/CMakeLists.txt @@ -20,8 +20,8 @@ if (MSVC) -DNOMINMAX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") else() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g") endif() diff --git a/lib/cpp-crypto/examples/cmake_example/CMakeLists.txt b/lib/cpp-crypto/examples/cmake_example/CMakeLists.txt index 04f89004b7745..d2711a3c965e6 100644 --- a/lib/cpp-crypto/examples/cmake_example/CMakeLists.txt +++ b/lib/cpp-crypto/examples/cmake_example/CMakeLists.txt @@ -12,8 +12,8 @@ if (MSVC) -DNOMINMAX ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") endif() # clone submodules - Uncomment when used outside of the Ark cpp-crypto source tree diff --git a/minetest.dmp b/minetest.dmp new file mode 100644 index 0000000000000..359109ebd7bed Binary files /dev/null and b/minetest.dmp differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c9f556c44e8c..4259c0a40affb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,5 @@ project(minetest) -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - INCLUDE(CheckTypeSize) INCLUDE(CheckIncludeFiles) INCLUDE(CheckLibraryExists) @@ -811,22 +809,22 @@ if(MSVC) # Visual Studio set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _WIN32_WINNT=0x0601 /D WIN32_LEAN_AND_MEAN") # EHa enables SEH exceptions (used for catching segfaults) - set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MT /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0") + set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0") if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:SSE") endif() set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") - set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MTd /Zi /Ob0 /O1 /RTC1") + set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1") # Debug build doesn't catch exceptions by itself # Add some optimizations because otherwise it's VERY slow - set(CMAKE_CXX_FLAGS_DEBUG "/MTd /Zi /Ob0 /Od /RTC1") + set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1") # Flags for C files (sqlite) # /MD = dynamically link to MSVCRxxx.dll - set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MT") + set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MD") # Flags that cannot be shared between cl and clang-cl # https://clang.llvm.org/docs/UsersManual.html#clang-cl @@ -1027,6 +1025,23 @@ elseif (USE_GETTEXT) add_custom_target(translations ALL COMMENT "mo update" DEPENDS ${MO_FILES}) endif() -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "District53") -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "District53") \ No newline at end of file +if(ANDROID) + set(sysname Android) +elseif(APPLE) + set(sysname OSX) +elseif(MSVC) + set(sysname Win32-VisualStudio) +elseif(WIN32) + set(sysname Win32-gcc) +else() + set(sysname Linux) +endif() + +if (WIN32) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${IRRLICHTMT_BUILD_DIR}/bin/${sysname}/$" + "$/") +endif() diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 84ede83241e47..03685e99acdff 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -75,5 +75,8 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsshadercallbacks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsScreenQuad.cpp ${CMAKE_CURRENT_SOURCE_DIR}/memoryManager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mesh_buffer_handler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/clientmap_norender.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/player_ai.cpp PARENT_SCOPE ) diff --git a/src/client/client.cpp b/src/client/client.cpp index 18cc58e6f7f22..09e5e2b4cec39 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -53,6 +53,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "shader.h" #include "gettext.h" #include "clientmap.h" +#include "clientmap_norender.h" #include "clientmedia.h" #include "version.h" #include "database/database-files.h" @@ -99,6 +100,7 @@ void PacketCounter::print(std::ostream& o) const Client::Client( const char *playername, const std::string &password, + const std::string ai_class, MapDrawControl &control, IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, @@ -117,12 +119,12 @@ Client::Client( m_sound(sound), m_event(event), m_rendering_engine(rendering_engine), - m_mesh_update_manager(std::make_unique(this)), m_env( - new ClientMap(this, rendering_engine, control, 666), - tsrc, this + rendering_engine ? (Map*)new ClientMap(this, rendering_engine, control, 666) : (Map*)new ClientMapNoRender(this, control, 666), + rendering_engine ? true : false, + tsrc, + this ), - m_particle_manager(std::make_unique(&m_env)), m_allow_login_or_register(allow_login_or_register), m_server_ser_ver(SER_FMT_VER_INVALID), m_last_chat_message_sent(time(NULL)), @@ -133,22 +135,34 @@ Client::Client( m_game_ui(game_ui), m_modchannel_mgr(new ModChannelMgr()) { + if (rendering_engine) { + m_mesh_update_manager = std::make_unique(this); + m_particle_manager = std::make_unique(&m_env); + m_mesh_buffer_handler = std::make_unique(m_mesh_update_manager.get(), (video::CNullDriver*)m_rendering_engine->get_video_driver()); + + if (g_settings->getBool("enable_minimap")) { + m_minimap = new Minimap(this); + } + } + // Add local player - m_env.setLocalPlayer(new LocalPlayer(this, playername)); + LocalPlayer* localplayer = new LocalPlayer(this, playername); + localplayer->setPlayerAI(ai_class); + m_env.setLocalPlayer(localplayer); // Make the mod storage database and begin the save for later m_mod_storage_database = new ModStorageDatabaseSQLite3(porting::path_user + DIR_DELIM + "client"); m_mod_storage_database->beginSave(); - if (g_settings->getBool("enable_minimap")) { - m_minimap = new Minimap(this); - } - m_cache_save_interval = g_settings->getU16("server_map_save_interval"); m_mesh_grid = { g_settings->getU16("client_mesh_chunk") }; } +FpsControl& Client::getFpsControl() { + return fps_control; +} + void Client::migrateModStorage() { std::string mod_storage_dir = porting::path_user + DIR_DELIM + "client"; @@ -316,8 +330,11 @@ void Client::Stop() m_shutdown = true; if (m_mods_loaded) m_script->on_shutdown(); + //request all client managed threads to stop - m_mesh_update_manager->stop(); + if (m_mesh_buffer_handler) + m_mesh_buffer_handler->stop(); + // Save local server map if (m_localdb) { infostream << "Local map saving ended." << std::endl; @@ -330,7 +347,7 @@ void Client::Stop() bool Client::isShutdown() { - return m_shutdown || !m_mesh_update_manager->isRunning(); + return m_shutdown && (m_mesh_update_manager && !m_mesh_update_manager->isRunning()) && (m_mesh_buffer_handler && !m_mesh_buffer_handler->isRunning()); } Client::~Client() @@ -341,15 +358,19 @@ Client::~Client() deleteAuthData(); - m_mesh_update_manager->stop(); - m_mesh_update_manager->wait(); + if (m_mesh_buffer_handler) { + m_mesh_buffer_handler->stop(); + m_mesh_buffer_handler->wait(); + } - MeshUpdateResult r; - while (m_mesh_update_manager->getNextResult(r)) { - for (auto block : r.map_blocks) - if (block) - block->refDrop(); - delete r.mesh; + if (m_mesh_update_manager) { + MeshUpdateResult r; + while (m_mesh_update_manager->getNextResult(r)) { + for (auto block : r.map_blocks) + if (block) + block->refDrop(); + //delete r.mesh; + } } delete m_inventory_from_server; @@ -359,10 +380,12 @@ Client::~Client() delete m_detached_inventorie.second; } - // cleanup 3d model meshes on client shutdown - m_rendering_engine->cleanupMeshCache(); + if (m_rendering_engine) { + // cleanup 3d model meshes on client shutdown + m_rendering_engine->cleanupMeshCache(); - guiScalingCacheClear(); + guiScalingCacheClear(); + } delete m_minimap; m_minimap = nullptr; @@ -478,6 +501,9 @@ void Client::step(float dtime) g_settings->getS32("client_mapblock_limit"), &deleted_blocks); + if (!deleted_blocks.empty()) + m_mesh_buffer_handler->removeBlocks(deleted_blocks.data(), deleted_blocks.size()); + /* Send info to server NOTE: This loop is intentionally iterated the way it is. @@ -576,12 +602,11 @@ void Client::step(float dtime) /* Replace updated meshes */ - { + if (m_rendering_engine) { int num_processed_meshes = 0; - std::vector blocks_to_ack; bool force_update_shadows = false; - MeshUpdateResult r; - while (m_mesh_update_manager->getNextResult(r)) + auto mesh_update_results = m_mesh_buffer_handler->getMeshUpdateResults(); + for (auto& r : mesh_update_results) { num_processed_meshes++; @@ -600,7 +625,7 @@ void Client::step(float dtime) if (block) { // Delete the old mesh - delete block->mesh; + //delete block->mesh; block->mesh = nullptr; block->solid_sides = r.solid_sides; @@ -615,21 +640,16 @@ void Client::step(float dtime) if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) is_empty = false; - if (is_empty) - delete r.mesh; - else { + if (!is_empty) { // Replace with the new mesh block->mesh = r.mesh; if (r.urgent) force_update_shadows = true; - - if (block->mesh) - mapBlockMesh_changed.push_back(block); } } } else { - delete r.mesh; + //delete r.mesh; r.mesh = nullptr; } @@ -646,25 +666,11 @@ void Client::step(float dtime) } } - for (auto p : r.ack_list) { - if (blocks_to_ack.size() == 255) { - sendGotBlocks(blocks_to_ack); - blocks_to_ack.clear(); - } - - blocks_to_ack.emplace_back(p); - } - for (auto block : r.map_blocks) if (block) block->refDrop(); } - if (blocks_to_ack.size() > 0) { - // Acknowledge block(s) - sendGotBlocks(blocks_to_ack); - } - if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); @@ -792,6 +798,9 @@ void Client::step(float dtime) bool Client::loadMedia(const std::string& data, const std::string& filename, bool from_media_push) { + if (!m_rendering_engine) + return true; + std::string name; const char* image_ext[] = { @@ -1052,7 +1061,7 @@ void Client::Send(NetworkPacket* pkt) } // Will fill up 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1 bytes -void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt, bool camera_inverted) +void writePlayerPos(LocalPlayer *myplayer, f32 wantedRange, f32 cameraFOV, NetworkPacket *pkt, bool camera_inverted) { v3f pf = myplayer->getPosition() * 100; v3f sf = myplayer->getSpeed() * 100; @@ -1060,9 +1069,9 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket * s32 yaw = myplayer->getYaw() * 100; u32 keyPressed = myplayer->control.getKeysPressed(); // scaled by 80, so that pi can fit into a u8 - u8 fov = std::fmin(255.0f, clientMap->getCameraFov() * 80.0f); + u8 fov = std::fmin(255.0f, cameraFOV * 80.0f); u8 wanted_range = std::fmin(255.0f, - std::ceil(clientMap->getWantedRange() * (1.0f / MAP_BLOCKSIZE))); + std::ceil(wantedRange * (1.0f / MAP_BLOCKSIZE))); v3s32 position(pf.X, pf.Y, pf.Z); v3s32 speed(sf.X, sf.Y, sf.Z); @@ -1115,7 +1124,14 @@ void Client::interact(InteractAction action, const PointedThing& pointed) pkt.putLongString(tmp_os.str()); - writePlayerPos(myplayer, &m_env.getClientMap(), &pkt, m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT); + Map* map = (Map*)&m_env.getMap(); + bool isClientMap = m_env.isClientMap(); +#define clientMap (isClientMap ? (ClientMap*)map : nullptr) + + f32 wantedRange = isClientMap ? clientMap->getWantedRange() : 120.f; + f32 fov = isClientMap ? clientMap->getCameraFov() : M_PI; + + writePlayerPos(myplayer, wantedRange, fov, &pkt, m_camera ? m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT : false); Send(&pkt); } @@ -1237,14 +1253,16 @@ void Client::sendDeletedBlocks(std::vector& blocks) Send(&pkt); } -void Client::sendGotBlocks(const std::vector& blocks) +void Client::sendGotBlocks(const std::vector& blocks) { - NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6 * blocks.size()); + /*NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + (6 + sizeof(u16)) * blocks.size()); pkt << (u8)blocks.size(); - for (const v3s16& block : blocks) - pkt << block; + for (const SendGotBlocksData& block : blocks) { + pkt << block.pos; + pkt << block.modified_version; + } - Send(&pkt); + Send(&pkt);*/ } void Client::sendRemovedSounds(const std::vector &soundList) @@ -1416,13 +1434,19 @@ void Client::sendPlayerPos() if (m_activeobjects_received && player->isDead()) return; - ClientMap &map = m_env.getClientMap(); - u8 camera_fov = std::fmin(255.0f, map.getCameraFov() * 80.0f); + Map *map = (Map*)&m_env.getMap(); + bool isClientMap = m_env.isClientMap(); +#define clientMap (isClientMap ? (ClientMap*)map : nullptr) + + f32 wantedRange = isClientMap ? clientMap->getWantedRange() : 120.f; + f32 fov = isClientMap ? clientMap->getCameraFov() : M_PI; + + u8 camera_fov = std::fmin(255.0f, fov * 80.0f); u8 wanted_range = std::fmin(255.0f, - std::ceil(map.getWantedRange() * (1.0f / MAP_BLOCKSIZE))); + std::ceil(wantedRange * (1.0f / MAP_BLOCKSIZE))); u32 keyPressed = player->control.getKeysPressed(); - bool camera_inverted = m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT; + bool camera_inverted = m_camera ? m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT : false; if ( player->last_position == player->getPosition() && @@ -1446,7 +1470,7 @@ void Client::sendPlayerPos() NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1 + 1); - writePlayerPos(player, &map, &pkt, camera_inverted); + writePlayerPos(player, wantedRange, camera_fov, &pkt, camera_inverted); Send(&pkt); } @@ -1591,7 +1615,7 @@ bool Client::updateWieldedItem() scene::ISceneManager* Client::getSceneManager() { - return m_rendering_engine->get_scene_manager(); + return m_rendering_engine ? m_rendering_engine->get_scene_manager() : nullptr; } Inventory* Client::getInventory(const InventoryLocation& loc) @@ -1753,11 +1777,17 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) if (b == NULL) return; + if (!m_mesh_update_manager) + return; + m_mesh_update_manager->updateBlock(&m_env.getMap(), p, ack_to_server, urgent); } void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) { + if (!m_mesh_update_manager) + return; + m_mesh_update_manager->updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, true); } @@ -1772,7 +1802,10 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur v3s16 blockpos = getNodeBlockPos(nodepos); v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE; - m_mesh_update_manager->updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, false); + + if (m_mesh_update_manager) + m_mesh_update_manager->updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, false); + // Leading edge if (nodepos.X == blockpos_relative.X) addUpdateMeshTask(blockpos + v3s16(-1, 0, 0), false, urgent); @@ -1784,6 +1817,9 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur void Client::updateCameraOffset(v3s16 camera_offset) { + if (!m_mesh_update_manager) + return; + m_mesh_update_manager->m_camera_offset = camera_offset; } @@ -1820,26 +1856,26 @@ struct TextureUpdateArgs { void Client::showUpdateProgressTexture(void* args, u32 progress, u32 max_progress) { - TextureUpdateArgs* targs = (TextureUpdateArgs*) args; - u16 cur_percent = std::ceil(progress / max_progress * 100.f); - - // update the loading menu -- if necessary - bool do_draw = false; - u64 time_ms = targs->last_time_ms; - if (cur_percent != targs->last_percent) { - targs->last_percent = cur_percent; - time_ms = porting::getTimeMs(); - // only draw when the user will notice something: - do_draw = (time_ms - targs->last_time_ms > 100); - } + TextureUpdateArgs* targs = (TextureUpdateArgs*) args; + u16 cur_percent = std::ceil(progress / max_progress * 100.f); - if (do_draw) { - targs->last_time_ms = time_ms; - std::wostringstream strm; - strm << targs->text_base << L" " << targs->last_percent << L"%..."; - m_rendering_engine->draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0, - 72 + (u16) ((18. / 100.) * (double) targs->last_percent)); - } + // update the loading menu -- if necessary + bool do_draw = false; + u64 time_ms = targs->last_time_ms; + if (cur_percent != targs->last_percent) { + targs->last_percent = cur_percent; + time_ms = porting::getTimeMs(); + // only draw when the user will notice something: + do_draw = (time_ms - targs->last_time_ms > 100); + } + + if (do_draw && m_rendering_engine) { + targs->last_time_ms = time_ms; + std::wostringstream strm; + strm << targs->text_base << L" " << targs->last_percent << L"%..."; + m_rendering_engine->draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0, + 72 + (u16) ((18. / 100.) * (double) targs->last_percent)); + } } void Client::afterContentReceived() @@ -1849,6 +1885,17 @@ void Client::afterContentReceived() assert(m_nodedef_received); // pre-condition assert(mediaReceived()); // pre-condition + if (!m_rendering_engine) { + m_state = LC_Ready; + sendReady(); + + if (m_mods_loaded) + m_script->on_client_ready(m_env.getLocalPlayer()); + + infostream << "Client::afterContentReceived() done" << std::endl; + return; + } + // Clear cached pre-scaled 2D GUI images, as this cache // might have images with the same name but different // content from previous sessions. @@ -1892,6 +1939,7 @@ void Client::afterContentReceived() // Start mesh update thread after setting up content definitions infostream<<"- Starting mesh update thread"<start(); + m_mesh_buffer_handler->start(); m_state = LC_Ready; sendReady(); diff --git a/src/client/client.h b/src/client/client.h index a7179fa2b4339..ae7fc4187fa62 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientdynamicinfo.h" #include #include "util/numeric.h" +#include "mesh_buffer_handler.h" #define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f @@ -68,6 +69,8 @@ class ParticleManager; class Camera; struct PlayerControl; class NetworkPacket; +struct FpsControl; + namespace con { class Connection; } @@ -123,6 +126,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef Client( const char *playername, const std::string &password, + const std::string ai_class, MapDrawControl &control, IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, @@ -376,6 +380,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef return getProtoVersion() != 0; // (set in TOCLIENT_HELLO) } + FpsControl& getFpsControl(); + Minimap* getMinimap() { return m_minimap; } void setCamera(Camera* camera) { m_camera = camera; } @@ -459,9 +465,13 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef bool inhibit_inventory_revert = false; - // - // For clientmap updateCache - std::vector mapBlockMesh_changed; + struct SendGotBlocksData { + v3s16 pos; + u16 modified_version; + }; + +public: + std::unique_ptr m_mesh_buffer_handler; private: void loadMods(); @@ -485,7 +495,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef void sendInit(const std::string &playerName); void startAuth(AuthMechanism chosen_auth_mechanism); void sendDeletedBlocks(std::vector &blocks); - void sendGotBlocks(const std::vector &blocks); + void sendGotBlocks(const std::vector &blocks); void sendRemovedSounds(const std::vector &soundList); bool canSendChatMessage() const; @@ -506,6 +516,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef std::unique_ptr m_mesh_update_manager; + ClientEnvironment m_env; std::unique_ptr m_particle_manager; std::unique_ptr m_con; @@ -625,4 +636,6 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef // The number of blocks the client will combine for mesh generation. MeshGrid m_mesh_grid; + + FpsControl fps_control; }; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 7e1676ffe3ad6..efabcd3c144a4 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -43,10 +43,13 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientEnvironment */ -ClientEnvironment::ClientEnvironment(ClientMap *map, +ClientEnvironment::ClientEnvironment( + Map *map, + bool p_mapTypeClientMap, ITextureSource *texturesource, Client *client): Environment(client), m_map(map), + mapTypeClientMap(p_mapTypeClientMap), m_texturesource(texturesource), m_client(client) { @@ -71,9 +74,13 @@ Map & ClientEnvironment::getMap() return *m_map; } +bool ClientEnvironment::isClientMap() { + return mapTypeClientMap; +} + ClientMap & ClientEnvironment::getClientMap() { - return *m_map; + return *(ClientMap*)m_map; } void ClientEnvironment::setLocalPlayer(LocalPlayer *player) @@ -133,6 +140,9 @@ void ClientEnvironment::step(float dtime) Stuff that has a maximum time increment */ + if (lplayer->getCAO()) + lplayer->stepAI(dtime); + u32 steps = std::ceil(dtime / dtime_max_increment); f32 dtime_part = dtime / steps; for (; steps > 0; --steps) { @@ -319,10 +329,13 @@ u16 ClientEnvironment::addActiveObject(std::unique_ptr objec if (!m_ao_manager.registerObject(std::move(object))) return 0; - obj->addToScene(m_texturesource, m_client->getSceneManager()); + if (m_client->getSceneManager()) { + obj->addToScene(m_texturesource, m_client->getSceneManager()); + + // Update lighting immediately + obj->updateLight(getDayNightRatio()); + } - // Update lighting immediately - obj->updateLight(getDayNightRatio()); return obj->getId(); } diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index 407473be75244..630a6dc6f5452 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -62,10 +62,11 @@ typedef std::unordered_map ClientActiveObjectMap; class ClientEnvironment : public Environment { public: - ClientEnvironment(ClientMap *map, ITextureSource *texturesource, Client *client); + ClientEnvironment(Map *map, bool mapTypeClientMap, ITextureSource *texturesource, Client *client); ~ClientEnvironment(); Map & getMap(); + bool isClientMap(); ClientMap & getClientMap(); Client *getGameDef() { return m_client; } @@ -147,7 +148,8 @@ class ClientEnvironment : public Environment u64 getFrameTimeDelta() const { return m_frame_dtime; } private: - ClientMap *m_map; + Map *m_map; + bool mapTypeClientMap = false; LocalPlayer *m_local_player = nullptr; ITextureSource *m_texturesource; Client *m_client; diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 9953811126132..ff478b7247781 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -97,6 +97,10 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) init_args(start_data, cmd_args); + bool norender = cmd_args.exists("norender"); + if (norender) + return runNoRender(start_data, cmd_args); + #if USE_SOUND if (g_settings->getBool("enable_sound")) g_sound_manager_singleton = createSoundManagerSingleton(); @@ -277,9 +281,107 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) return retval; } +bool ClientLauncher::runNoRender(GameStartData& start_data, const Settings& cmd_args) { + + // Create game callback for menus + g_gamecallback = new MainGameCallback(); + + input = new RandomInputHandler(); + + ChatBackend chat_backend; + + // If an error occurs, this is set to something by menu(). + // It is then displayed before the menu shows on the next call to menu() + std::string error_message; + bool reconnect_requested = false; + + bool first_loop = true; + + /* + Menu-game loop + */ + bool retval = true; + bool* kill = porting::signal_handler_killstatus(); + + while (!*kill && + !g_gamecallback->shutdown_requested) { + + try { // This is used for catching disconnects + + bool should_run_game = launch_game(error_message, reconnect_requested, + start_data, cmd_args); + + // Reset the reconnect_requested flag + reconnect_requested = false; + + // If skip_main_menu, we only want to startup once + if (skip_main_menu && !first_loop) + break; + first_loop = false; + + if (!should_run_game) { + if (skip_main_menu) + break; + continue; + } + + // Break out of menu-game loop to shut down cleanly + if (*kill) + break; + + the_game( + kill, + input, + nullptr, + start_data, + error_message, + chat_backend, + &reconnect_requested + ); + } //try + catch (con::PeerNotFoundException& e) { + error_message = gettext("Connection error (timed out?)"); + errorstream << error_message << std::endl; + } + catch (ShaderException& e) { + error_message = e.what(); + errorstream << error_message << std::endl; + } + +#ifdef NDEBUG + catch (std::exception& e) { + error_message = "Some exception: "; + error_message.append(debug_describe_exc(e)); + errorstream << error_message << std::endl; + } +#endif + + /* Save the settings when leaving the game. + * This makes sure that setting changes made in-game are persisted even + * in case of a later unclean exit from the mainmenu. + * This is especially useful on Android because closing the app from the + * "Recents screen" results in an unclean exit. + * Caveat: This means that the settings are saved twice when exiting Minetest. + */ + if (!g_settings_path.empty()) + g_settings->updateConfigFile(g_settings_path.c_str()); + + // If no main menu, show error and exit + if (skip_main_menu) { + if (!error_message.empty()) + retval = false; + break; + } + } // Menu-game loop + + return retval; +} + void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_args) { skip_main_menu = cmd_args.getFlag("go"); + if (cmd_args.exists("norender")) + skip_main_menu = true; start_data.address = g_settings->get("address"); if (cmd_args.exists("address")) { @@ -461,7 +563,7 @@ bool ClientLauncher::launch_game(std::string &error_message, start_data.address.empty() && !start_data.name.empty(); } - if (!m_rendering_engine->run()) + if (m_rendering_engine && !m_rendering_engine->run()) return false; if (!start_data.isSinglePlayer() && start_data.name.empty()) { diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index dc0794c88fabf..b9b9e59ba4298 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -35,6 +35,8 @@ class ClientLauncher bool run(GameStartData &start_data, const Settings &cmd_args); private: + bool runNoRender(GameStartData& start_data, const Settings& cmd_args); + void init_args(GameStartData &start_data, const Settings &cmd_args); bool init_engine(); void init_input(); diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index a46bc4c18c5d2..c29ef9f21038f 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -1,4 +1,4 @@ -/* + /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "camera.h" // CameraModes #include "util/basic_macros.h" #include "client/renderingengine.h" +#include "COpenGLDriver.h" #include @@ -285,28 +286,12 @@ void ClientMap::updateDrawList() } m_keeplist.clear(); - cache_keep_blocks.clear(); - const v3s16 cam_pos_nodes = floatToInt(m_camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); - for (auto& sector_it : m_sectors) { - const MapSector* sector = sector_it.second; - v2s16 sp = sector->getPos(); - - if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || - sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) - continue; - - for (const auto& entry : sector->getBlocks()) { - MapBlock* block = entry.second.get(); - cache_keep_blocks[block->getPos()] = block; - } - } - // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks frustum culled @@ -363,7 +348,7 @@ void ClientMap::updateDrawList() // Loop through blocks in sector for (const auto& entry : sector->getBlocks()) { MapBlock* block = entry.second.get(); - MapBlockMesh* mesh = block->mesh; + MapBlockMesh* mesh = block->mesh.get(); // Calculate the coordinates for range and frustum culling v3f mesh_sphere_center; @@ -475,7 +460,7 @@ void ClientMap::updateDrawList() if (!block) continue; - MapBlockMesh* mesh = block ? block->mesh : nullptr; + MapBlockMesh* mesh = block ? block->mesh.get() : nullptr; // Calculate the coordinates for range and frustum culling v3f mesh_sphere_center; @@ -696,7 +681,7 @@ void ClientMap::touchMapBlocks() for (const auto& entry : sector->getBlocks()) { MapBlock* block = entry.second.get(); - MapBlockMesh* mesh = block->mesh; + MapBlockMesh* mesh = block->mesh.get(); // Calculate the coordinates for range and frustum culling v3f mesh_sphere_center; @@ -747,8 +732,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if (pass == scene::ESNRP_SOLID) { m_last_drawn_sectors.clear(); - if (doesNeedToUpdateCache()) - updateCacheBuffers(driver); + updateCacheBuffers(driver); } /* @@ -782,12 +766,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) for (auto& i : m_drawlist) { v3s16 block_pos = i.first; MapBlock* block = i.second; - MapBlockMesh* block_mesh = block->mesh; + MapBlockMesh* block_mesh = block->mesh.get(); // If the mesh of the block happened to get deleted, ignore it if (!block_mesh) continue; + auto& transparent_buffers = block_mesh->getTransparentBuffers(); if (transparent_buffers.empty()) continue; @@ -967,119 +952,189 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) g_profiler->avg(prefix + "material swaps [#]", material_swaps); } -bool ClientMap::doesNeedToUpdateCache() { - return !m_client->mapBlockMesh_changed.empty(); -} - void ClientMap::updateCacheBuffers(video::IVideoDriver* driver) { + TextureBufListMaps buffers; + if (!m_client->m_mesh_buffer_handler->getCacheBuffers(buffers)) + return; + + const v3s16 cam_pos_nodes = floatToInt(m_camera_position, BS); + v3s16 p_blocks_min; + v3s16 p_blocks_max; + getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + m_client->m_mesh_buffer_handler->setView(p_blocks_min, p_blocks_max); + auto glDriver = (video::CNullDriver*)driver; + bool canDropTextures = true; + + auto& fps_control = m_client->getFpsControl(); + auto sleep_time_sec = ((double)fps_control.sleep_time) * 1e-6; + + auto now = std::chrono::steady_clock::now(); + auto time_since_last_time = std::chrono::duration(now - last_time_build_buffers).count() - sleep_time_sec; + last_time_build_buffers = now; + + const u64 max_gl_ops = std::max( + (u64)1, + u64((1.0 - std::clamp( + time_since_last_time, + 0.0, + 1.0 + )) * 5000)); + + const u64 gl_ops_points_div = 100; + + u64 gl_ops_processed = 0; + u64 num_load_data_processed = 0; + for (auto it : buffers.loadData) { + if (gl_ops_processed >= max_gl_ops) { + canDropTextures = false; + break; + } - // - // Unload buffers - // - std::vector unload_mapblocks = m_client->mapBlockMesh_changed; - auto cache_keep_blocks_end = cache_keep_blocks.end(); - for (auto it : buffer_data) { - if (cache_keep_blocks.find(it.first->getPos()) != cache_keep_blocks_end) - continue; + auto loadBlockData = it.block_data; + auto& loadDataVec = loadBlockData.data; - unload_mapblocks.push_back(it.first); - } + for (auto loadData : loadDataVec) { + auto texture = loadData.texture; + auto data = buffers.getNoCreate(texture, loadData.layer); + assert(data); - for (auto mapBlockMesh : unload_mapblocks) { - if (buffer_data.find(mapBlockMesh) == buffer_data.end()) - continue; + auto cache = cache_buffers.get(texture, loadData.layer); - auto& bufferList = buffer_data[mapBlockMesh]; - for (auto& meshBufferData : bufferList) { - //std::cout << "Unload:" << meshBufferData.texture->getName().getPath().c_str() << std::endl; + auto buffer = cache->buffer; + if (!buffer->getHWBuffer()) { + buffer->vertexCount = data->vertexCount; + buffer->indexCount = data->indexCount; + buffer->drawPrimitiveCount = 0; + buffer->Material = data->material; + glDriver->getBufferLink(buffer); + } - auto cache = cache_buffers.getNoCreate(meshBufferData.texture, meshBufferData.layer); - if (!cache) { + auto HWBuffer = glDriver->getBufferLink(buffer); + if (!HWBuffer) continue; + + // + // Grow memory + // + size_t setVertexCount = data->vertexCount; + if (buffer->vertexCount != setVertexCount) { + // + // Resize gpu memory + glDriver->resizeVertexHardwareBufferSubData( + HWBuffer, + setVertexCount, + buffer->vertexCount); + + buffer->vertexCount = setVertexCount; + + gl_ops_processed += (setVertexCount / gl_ops_points_div) * 2; } - auto HWBuffer = glDriver->getBufferLink(cache->buffer); - if (HWBuffer) { - if (meshBufferData.indexMemory.is_valid()) { - auto indexCount = meshBufferData.indexMemory.size() / sizeof(u32); - assert(empty_data.size() >= indexCount); - - glDriver->subUpdateIndexHardwareBuffer( - HWBuffer, - (c8*)empty_data.pointer(), - indexCount, - meshBufferData.indexMemory.chunkStart / sizeof(u32)); - } + size_t setIndexCount = data->indexCount; + if (buffer->indexCount != setIndexCount) { + // + // Resize gpu memory + glDriver->resizeIndexHardwareBufferSubData( + HWBuffer, + setIndexCount, + buffer->indexCount); - /*if (meshBufferData.vertexMemory.is_valid()) { - auto vertexCount = meshBufferData.vertexMemory.size() / sizeof(video::S3DVertex); - assert(empty_data.size() >= vertexCount); + buffer->indexCount = setIndexCount; - glDriver->subUpdateVertexHardwareBuffer( - HWBuffer, - (c8*)empty_data.pointer(), - vertexCount, - meshBufferData.vertexMemory.chunkStart / sizeof(video::S3DVertex)); - }*/ + gl_ops_processed += (setIndexCount / gl_ops_points_div) * 2; } - if (meshBufferData.vertexMemory.is_valid()) - cache->vertex_memory.free(meshBufferData.vertexMemory); - if (meshBufferData.indexMemory.is_valid()) - cache->index_memory.free(meshBufferData.indexMemory); - } + // + // Vertices + // + OpenGLSubData* vertexSubData = loadData.glVertexSubData; + OpenGLSubData* indexSubData = loadData.glIndexSubData; - buffer_data.erase(mapBlockMesh); - } + if (vertexSubData) { + auto subData = vertexSubData; - // - // Block meshes has not been built yet. - // - std::unordered_map> build_blocks; - std::set keepTextures; + // + // Move vertex & index memory to GPU + // + video::S3DVertex* vertices; + if (subData->isEmpty()) { + vertices = (video::S3DVertex*)empty_data.pointer(); + assert(subData->size <= empty_data.size()); + } + else + vertices = (video::S3DVertex*)subData->data.pointer(); - for (auto block : m_client->mapBlockMesh_changed) { - cache_keep_blocks[block->getPos()] = block; + glDriver->subUpdateVertexHardwareBuffer( + HWBuffer, + (c8*)vertices, + subData->size / sizeof(video::S3DVertex), + subData->offset / sizeof(video::S3DVertex)); - auto block_mesh = block->mesh; - if (!block_mesh) - continue; + gl_ops_processed += subData->size / gl_ops_points_div; - //auto& transparentBuffers = block_mesh->getMapTransparentBuffers(); + delete subData; + } - for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { - scene::IMesh* mesh = block_mesh->getMesh(layer); - assert(mesh); + if (indexSubData) { + auto subData = indexSubData; - u32 c = mesh->getMeshBufferCount(); - for (u32 i = 0; i < c; i++) { - if (!block_mesh->canMeshBufferBeCached(layer, i)) { - continue; + // + // Move vertex & index memory to GPU + // + u32* indices; + if (subData->isEmpty()) { + indices = empty_data.pointer(); + assert(subData->size <= empty_data.size()); } + else + indices = (u32*)subData->data.pointer(); - scene::IMeshBuffer* buf = mesh->getMeshBuffer(i); + glDriver->subUpdateIndexHardwareBuffer( + HWBuffer, + (c8*)indices, + subData->size / sizeof(u32), + subData->offset / sizeof(u32)); - /*if (transparentBuffers.find(buf) != transparentBuffers.end()) - continue;*/ + gl_ops_processed += subData->size / gl_ops_points_div; - video::IMaterialRenderer* rnd = - driver->getMaterialRenderer(buf->getMaterial().MaterialType); - bool transparent = (rnd && rnd->isTransparent()); - if (transparent) - continue; + buffer->drawPrimitiveCount = loadData.drawPrimitiveCount; + + delete subData; + } + } + + num_load_data_processed++; + } - auto sMeshBufferData = SMeshBufferData(); - sMeshBufferData.meshBuffer = buf; - sMeshBufferData.texture = block_mesh->getBufferMainTexture(layer, i); - sMeshBufferData.layer = layer; - build_blocks[block].push_back(sMeshBufferData); + m_client->m_mesh_buffer_handler->eraseLoadOrder(num_load_data_processed); + + if (canDropTextures) { + // + // Drop textures + struct Drop { + u8 layer; + video::ITexture* texture; + }; + std::vector drop_textures; + for (u8 layer = 0; layer < MAX_TILE_LAYERS; layer++) { + auto& cache_maps = cache_buffers.maps[layer]; + auto& maps = buffers.maps[layer]; + auto maps_end = maps.end(); - keepTextures.insert(sMeshBufferData.texture); + for (auto& it : cache_maps) { + auto texture = it.first; + if (maps.find(texture) != maps_end) + continue; + drop_textures.push_back({ layer, texture }); } } + + for (auto d : drop_textures) + cache_buffers.drop(d.texture, d.layer); } - m_client->mapBlockMesh_changed.clear(); + + buffers.clear(); // // Go through render list and group same materials together @@ -1109,10 +1164,10 @@ void ClientMap::updateCacheBuffers(video::IVideoDriver* driver) { render_uncached[0].clear(); render_uncached[1].clear(); - for (auto& it : cache_keep_blocks) { + for (auto& it : m_drawlist) { v3s16 block_pos = it.first; MapBlock* block = it.second; - MapBlockMesh* block_mesh = block->mesh; + MapBlockMesh* block_mesh = block->mesh.get(); // If the mesh of the block happened to get deleted, ignore it if (!block_mesh) @@ -1140,9 +1195,6 @@ void ClientMap::updateCacheBuffers(video::IVideoDriver* driver) { scene::IMeshBuffer* buf = mesh->getMeshBuffer(i); - video::ITexture* texture = block_mesh->getBufferMainTexture(layer, i); - keepTextures.insert(texture); - /*if (!skip_animate) continue;*/ @@ -1156,6 +1208,7 @@ void ClientMap::updateCacheBuffers(video::IVideoDriver* driver) { } if (block_mesh->isMeshBufferAnimated(layer, i)) { + video::ITexture* texture = block_mesh->getBufferMainTexture(layer, i); if (animate_blocks.find(texture) == animate_blocks.end()) { animate_blocks[texture] = { block_mesh, buf, i }; } @@ -1191,166 +1244,6 @@ void ClientMap::updateCacheBuffers(video::IVideoDriver* driver) { } } - // - // Start building dynamic meshes - // - - for (auto& it : build_blocks) { - auto& sMeshBuffers = buffer_data[it.first]; - if (!sMeshBuffers.empty()) - continue; - - auto& loadBuffers = it.second; - - for (auto& sMeshBufferData : loadBuffers) { - // - // Allocate memory spots for buffers - // - - auto meshBuffer = sMeshBufferData.meshBuffer; - auto vertexCount = meshBuffer->getVertexCount(); - auto indexCount = meshBuffer->getIndexCount(); - - if (indexCount == 0 || vertexCount == 0) { - errorstream << "Block [" << analyze_block(it.first) - << "] contains an empty meshbuf" << std::endl; - continue; - } - - video::ITexture* texture = sMeshBufferData.texture; - auto data = cache_buffers.get(texture, sMeshBufferData.layer); - auto buffer = data->buffer; - if (!buffer->getHWBuffer()) { - buffer->vertexCount = 100'000; - buffer->indexCount = 20'000; - - data->vertex_memory.setSize(buffer->vertexCount * sizeof(video::S3DVertex)); - data->index_memory.setSize(buffer->indexCount * sizeof(u32)); - - buffer->drawPrimitiveCount = 0; - buffer->Material = meshBuffer->getMaterial(); - glDriver->getBufferLink(buffer); - } - - auto HWBuffer = glDriver->getBufferLink(buffer); - if (!HWBuffer) { - continue; - } - - sMeshBufferData.vertexMemory = data->vertex_memory.allocate(vertexCount * sizeof(video::S3DVertex)); - sMeshBufferData.indexMemory = data->index_memory.allocate(indexCount * sizeof(u32)); - - if (!sMeshBufferData.vertexMemory.is_valid()) { - // - // Grow Vertex memory - // - - core::array grow_vertices; - grow_vertices.set_used(data->vertex_memory.used_mem / sizeof(video::S3DVertex) + 50'000); - - // - // Resize gpu memory - glDriver->resizeVertexHardwareBufferSubData( - HWBuffer, - grow_vertices.size()); - - buffer->vertexCount = grow_vertices.size(); - data->vertex_memory.setSize(buffer->vertexCount * sizeof(video::S3DVertex)); - - // - // Try to get offset again - if (!sMeshBufferData.vertexMemory.is_valid()) - sMeshBufferData.vertexMemory = data->vertex_memory.allocate(vertexCount * sizeof(video::S3DVertex)); - - // - // Move vertex & index memory to GPU - // - if (!sMeshBufferData.vertexMemory.is_valid()) - continue; - } - if (!sMeshBufferData.indexMemory.is_valid()) { - // - // Grow Index memory - // - core::array grow_indices; - grow_indices.set_used(data->index_memory.used_mem / sizeof(u32) + 80'000); - - // - // Resize gpu memory - glDriver->resizeIndexHardwareBufferSubData( - HWBuffer, - grow_indices.size()); - - buffer->indexCount = grow_indices.size(); - data->index_memory.setSize(buffer->indexCount * sizeof(u32)); - - // - // Try to get offset again - if (!sMeshBufferData.indexMemory.is_valid()) - sMeshBufferData.indexMemory = data->index_memory.allocate(indexCount * sizeof(u32)); - - // - // Move vertex & index memory to GPU - // - if (!sMeshBufferData.indexMemory.is_valid()) - continue; - } - - // - // Move vertex & index memory to GPU - // - auto vertices = (video::S3DVertex*)meshBuffer->getVertices(); - auto indices = meshBuffer->getIndices(); - - core::array indicesWithOffset; - indicesWithOffset.set_used(indexCount); - u32 indexValueOffset = sMeshBufferData.vertexMemory.chunkStart / sizeof(video::S3DVertex); - u32* indices_ptr = indicesWithOffset.pointer(); - for (u32 i = 0; i < indexCount; ++i) { - *indices_ptr = indices[i] + indexValueOffset; - indices_ptr++; - } - - glDriver->subUpdateVertexHardwareBuffer( - HWBuffer, - (c8*)vertices, - vertexCount, - sMeshBufferData.vertexMemory.chunkStart / sizeof(video::S3DVertex)); - glDriver->subUpdateIndexHardwareBuffer( - HWBuffer, - (c8*)indicesWithOffset.pointer(), - indexCount, - sMeshBufferData.indexMemory.chunkStart / sizeof(u32)); - - buffer->drawPrimitiveCount = (data->index_memory.used_mem / sizeof(u32)) / 3; - - sMeshBuffers.push_back(sMeshBufferData); - } - } - - // - // Check if should drop a texture - struct Drop { - u8 layer; - video::ITexture* texture; - }; - std::vector dropTextures; - for (u8 layer = 0; layer < MAX_TILE_LAYERS; layer++) { - auto& map = cache_buffers.maps[layer]; - for (auto& list : map) { - auto cache = list.second; - auto texture = list.first; - if (cache && keepTextures.find(texture) != keepTextures.end()) - continue; - - dropTextures.push_back({ layer, texture }); - } - } - - for (auto& it : dropTextures) { - cache_buffers.drop(it.texture, it.layer); - } - g_profiler->avg("updateCacheBuffers(): animated meshes [#]", mesh_animate_count); } @@ -1746,7 +1639,7 @@ void ClientMap::updateDrawListShadow(v3f shadow_light_pos, v3f shadow_light_dir, */ for (const auto& entry : sector->getBlocks()) { MapBlock* block = entry.second.get(); - MapBlockMesh* mesh = block->mesh; + MapBlockMesh* mesh = block->mesh.get(); if (!mesh) { // Ignore if mesh doesn't exist continue; diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 4d44ba2d63061..9639e5df5e7c7 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -38,6 +38,8 @@ struct MapDrawControl bool allow_noclip = false; // show a wire frame for debugging bool show_wireframe = false; + + bool norender = false; }; namespace irr @@ -97,21 +99,7 @@ namespace irr } namespace { - struct SMeshBufferData { - MemoryManager::MemoryInfo vertexMemory; - MemoryManager::MemoryInfo indexMemory; - - scene::IMeshBuffer* meshBuffer; - video::ITexture* texture = nullptr; - u8 layer = 0; - }; - - struct MeshBlockBuffer { - MapBlockMesh* mapBlockMesh = nullptr; - scene::IMeshBuffer* meshBuffer = nullptr; - }; - - struct TextureBufListMaps + struct TextureBufferMaps { struct TextureHash { @@ -125,15 +113,9 @@ namespace { struct Data { - scene::SMeshBuffer32* buffer; + scene::SMeshBuffer32* buffer = nullptr; - MemoryManager vertex_memory; - MemoryManager index_memory; - - Data() : - buffer(nullptr), - vertex_memory(sizeof(video::S3DVertex)), - index_memory(sizeof(u32)*3) { + Data() : buffer(nullptr) { } }; @@ -145,7 +127,7 @@ namespace { std::array maps; - ~TextureBufListMaps() { + ~TextureBufferMaps() { clear(); } @@ -306,7 +288,6 @@ class ClientMap : public Map, public scene::ISceneNode void onSettingChanged(const std::string &name); - bool doesNeedToUpdateCache(); void updateCacheBuffers(video::IVideoDriver* driver); protected: @@ -388,11 +369,9 @@ class ClientMap : public Map, public scene::ISceneNode bool m_loops_occlusion_culler; bool m_enable_raytraced_culling; - TextureBufListMaps cache_buffers; + TextureBufferMaps cache_buffers; core::array empty_data; - - std::unordered_map cache_keep_blocks; - std::unordered_map> buffer_data; std::unordered_set render_uncached[2]; int last_frameno = -1; + std::chrono::steady_clock::time_point last_time_build_buffers = std::chrono::steady_clock::now(); }; diff --git a/src/client/clientmap_norender.cpp b/src/client/clientmap_norender.cpp new file mode 100644 index 0000000000000..2aec9eca150c0 --- /dev/null +++ b/src/client/clientmap_norender.cpp @@ -0,0 +1,240 @@ +/* +Minetest +Copyright (C) 2010-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 "clientmap_norender.h" +#include "client.h" +#include +#include "mapsector.h" +#include "mapblock.h" +#include "nodedef.h" +#include "profiler.h" +#include "settings.h" +#include "util/basic_macros.h" + +#include + +static void on_settings_changed(const std::string& name, void* data) +{ + static_cast(data)->onSettingChanged(name); +} +// ClientMapNoRender + +ClientMapNoRender::ClientMapNoRender( + Client* client, + MapDrawControl& control, + s32 id +) : + Map(client), + m_client(client), + m_control(control) +{ + /* TODO: Add a callback function so these can be updated when a setting + * changes. At this point in time it doesn't matter (e.g. /set + * is documented to change server settings only) + * + * TODO: Local caching of settings is not optimal and should at some stage + * be updated to use a global settings object for getting thse values + * (as opposed to the this local caching). This can be addressed in + * a later release. + */ + g_settings->registerChangedCallback("occlusion_culler", on_settings_changed, this); + g_settings->registerChangedCallback("enable_raytraced_culling", on_settings_changed, this); +} + +void ClientMapNoRender::onSettingChanged(const std::string& name) +{ +} + +ClientMapNoRender::~ClientMapNoRender() +{ + g_settings->deregisterChangedCallback("occlusion_culler", on_settings_changed, this); + g_settings->deregisterChangedCallback("enable_raytraced_culling", on_settings_changed, this); +} + +MapSector* ClientMapNoRender::emergeSector(v2s16 p2d) +{ + // Check that it doesn't exist already + MapSector* sector = getSectorNoGenerate(p2d); + + // Create it if it does not exist yet + if (!sector) { + sector = new MapSector(this, p2d, m_gamedef); + m_sectors[p2d] = sector; + } + + return sector; +} + +void ClientMapNoRender::getBlocksInViewRange(v3s16 cam_pos_nodes, + v3s16* p_blocks_min, v3s16* p_blocks_max, float range) +{ + if (range <= 0.0f) + range = m_control.wanted_range; + + v3s16 box_nodes_d = range * v3s16(1, 1, 1); + // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d' + // can exceed the range of v3s16 when a large view range is used near the + // world edges. + v3s32 p_nodes_min( + cam_pos_nodes.X - box_nodes_d.X, + cam_pos_nodes.Y - box_nodes_d.Y, + cam_pos_nodes.Z - box_nodes_d.Z); + v3s32 p_nodes_max( + cam_pos_nodes.X + box_nodes_d.X, + cam_pos_nodes.Y + box_nodes_d.Y, + cam_pos_nodes.Z + box_nodes_d.Z); + // Take a fair amount as we will be dropping more out later + // Umm... these additions are a bit strange but they are needed. + *p_blocks_min = v3s16( + p_nodes_min.X / MAP_BLOCKSIZE - 3, + p_nodes_min.Y / MAP_BLOCKSIZE - 3, + p_nodes_min.Z / MAP_BLOCKSIZE - 3); + *p_blocks_max = v3s16( + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); +} + +class MapBlockFlags +{ +public: + static constexpr u16 CHUNK_EDGE = 8; + static constexpr u16 CHUNK_MASK = CHUNK_EDGE - 1; + static constexpr std::size_t CHUNK_VOLUME = CHUNK_EDGE * CHUNK_EDGE * CHUNK_EDGE; // volume of a chunk + + MapBlockFlags(v3s16 min_pos, v3s16 max_pos) + : min_pos(min_pos), volume((max_pos - min_pos) / CHUNK_EDGE + 1) + { + chunks.resize(volume.X * volume.Y * volume.Z); + } + + class Chunk + { + public: + inline u8& getBits(v3s16 pos) + { + std::size_t address = getAddress(pos); + return bits[address]; + } + + private: + inline std::size_t getAddress(v3s16 pos) { + std::size_t address = (pos.X & CHUNK_MASK) + (pos.Y & CHUNK_MASK) * CHUNK_EDGE + (pos.Z & CHUNK_MASK) * (CHUNK_EDGE * CHUNK_EDGE); + return address; + } + + std::array bits; + }; + + Chunk& getChunk(v3s16 pos) + { + v3s16 delta = (pos - min_pos) / CHUNK_EDGE; + std::size_t address = delta.X + delta.Y * volume.X + delta.Z * volume.X * volume.Y; + Chunk* chunk = chunks[address].get(); + if (!chunk) { + chunk = new Chunk(); + chunks[address].reset(chunk); + } + return *chunk; + } +private: + std::vector> chunks; + v3s16 min_pos; + v3s16 volume; +}; + +void ClientMapNoRender::touchMapBlocks() +{ + if (m_control.range_all) + return; + + ScopeProfiler sp(g_profiler, "CM::touchMapBlocks()", SPT_AVG); + + v3s16 cam_pos_nodes = floatToInt(m_camera_position, BS); + + v3s16 p_blocks_min; + v3s16 p_blocks_max; + getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + + // Number of blocks currently loaded by the client + u32 blocks_loaded = 0; + // Number of blocks with mesh in rendering range + u32 blocks_in_range_with_mesh = 0; + + for (const auto& sector_it : m_sectors) { + const MapSector* sector = sector_it.second; + v2s16 sp = sector->getPos(); + + blocks_loaded += sector->size(); + if (!m_control.range_all) { + if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || + sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) + continue; + } + + /* + Loop through blocks in sector + */ + + for (const auto& entry : sector->getBlocks()) { + MapBlock* block = entry.second.get(); + MapBlockMesh* mesh = block->mesh.get(); + + // Calculate the coordinates for range and frustum culling + v3f mesh_sphere_center; + f32 mesh_sphere_radius; + + v3s16 block_pos_nodes = block->getPosRelative(); + + if (mesh) { + mesh_sphere_center = intToFloat(block_pos_nodes, BS) + + mesh->getBoundingSphereCenter(); + mesh_sphere_radius = mesh->getBoundingRadius(); + } + else { + mesh_sphere_center = intToFloat(block_pos_nodes, BS) + + v3f((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS); + mesh_sphere_radius = 0.0f; + } + + // First, perform a simple distance check. + if (!m_control.range_all && + mesh_sphere_center.getDistanceFrom(m_camera_position) > + m_control.wanted_range * BS + mesh_sphere_radius) + continue; // Out of range, skip. + + // Keep the block alive as long as it is in range. + block->resetUsageTimer(); + blocks_in_range_with_mesh++; + } + } + + g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh); + g_profiler->avg("MapBlocks loaded [#]", blocks_loaded); +} + +void ClientMapNoRender::PrintInfo(std::ostream& out) +{ + out << "ClientMapNoRender: "; +} + +void ClientMapNoRender::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks) +{ + g_profiler->avg("CM::reportMetrics loaded blocks [#]", all_blocks); +} diff --git a/src/client/clientmap_norender.h b/src/client/clientmap_norender.h new file mode 100644 index 0000000000000..6c2d7ae091037 --- /dev/null +++ b/src/client/clientmap_norender.h @@ -0,0 +1,63 @@ +#pragma once + +#include "map.h" +#include "clientmap.h" +#include "tile.h" +#include +#include + +class Client; + +/* + ClientMapNoRender +*/ + +class ClientMapNoRender : public Map +{ +public: + ClientMapNoRender( + Client* client, + MapDrawControl& control, + s32 id + ); + + virtual ~ClientMapNoRender(); + + bool maySaveBlocks() override + { + return false; + } + + /* + Forcefully get a sector from somewhere + */ + MapSector* emergeSector(v2s16 p) override; + + // @brief Calculate statistics about the map and keep the blocks alive + void touchMapBlocks(); + + // For debug printing + void PrintInfo(std::ostream& out) override; + + const MapDrawControl& getControl() const { return m_control; } + f32 getWantedRange() const { return m_control.wanted_range; } + f32 getCameraFov() const { return m_camera_fov; } + + void onSettingChanged(const std::string& name); + + void getBlocksInViewRange(v3s16 cam_pos_nodes, + v3s16* p_blocks_min, v3s16* p_blocks_max, float range = -1.0f); + +protected: + void reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks) override; + +private: + Client* m_client; + + MapDrawControl& m_control; + + v3f m_camera_position = v3f(0, 0, 0); + v3f m_camera_direction = v3f(0, 0, 1); + f32 m_camera_fov = M_PI; + v3s16 m_camera_offset; +}; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 9fb110fdc269b..51a0eb01b6bf8 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1339,6 +1339,9 @@ void GenericCAO::updateTexturePos() // Do not pass by reference, see header. void GenericCAO::updateTextures(std::string mod) { + if (!m_env->isClientMap()) + return; + ITextureSource *tsrc = m_client->tsrc(); bool use_trilinear_filter = g_settings->getBool("trilinear_filter"); @@ -1946,7 +1949,8 @@ void GenericCAO::processMessage(const std::string &data) ClientSimpleObject *simple = createSmokePuff( m_smgr, m_env, m_position, v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); - m_env->addSimpleObject(simple); + if (simple) + m_env->addSimpleObject(simple); } else if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) { m_reset_textures_timer = 0.05; if(damage >= 2) diff --git a/src/client/content_cso.cpp b/src/client/content_cso.cpp index d6c3fd3ef75a5..f9689b74c8dff 100644 --- a/src/client/content_cso.cpp +++ b/src/client/content_cso.cpp @@ -74,6 +74,9 @@ class SmokePuffCSO: public ClientSimpleObject ClientSimpleObject* createSmokePuff(scene::ISceneManager *smgr, ClientEnvironment *env, v3f pos, v2f size) { + if (!smgr) + return nullptr; + return new SmokePuffCSO(smgr, env, pos, size); } diff --git a/src/client/game.cpp b/src/client/game.cpp index 9bd8c6dff3f85..924ff3bfbc95a 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -765,6 +765,7 @@ class Game { void checkZoomEnabled(); void updateCameraDirection(CameraOrientation *cam, float dtime); + void updateCameraDirectionNoRender(CameraOrientation *cam, float dtime); void updateCameraOrientation(CameraOrientation *cam, float dtime); void updatePlayerControl(const CameraOrientation &cam); void updatePauseState(); @@ -977,8 +978,7 @@ class Game { }; Game::Game() : - m_chat_log_buf(g_logger), - m_game_ui(new GameUI()) + m_chat_log_buf(g_logger) { g_settings->registerChangedCallback("doubletap_jump", &settingChangedCallback, this); @@ -1028,6 +1028,9 @@ Game::Game() : Game::~Game() { + if (driver) + driver->removeAllHardwareBuffers(); + delete client; delete soundmaker; sound_manager.reset(); @@ -1097,7 +1100,7 @@ bool Game::startup(bool *kill, // "cache" m_rendering_engine = rendering_engine; - device = m_rendering_engine->get_raw_device(); + device = m_rendering_engine ? m_rendering_engine->get_raw_device() : nullptr; this->kill = kill; this->error_message = &error_message; reconnect_requested = reconnect; @@ -1107,19 +1110,23 @@ bool Game::startup(bool *kill, input->keycache.populate(); - driver = device->getVideoDriver(); - smgr = m_rendering_engine->get_scene_manager(); + if (m_rendering_engine) { + m_game_ui = std::unique_ptr(new GameUI()); + m_game_ui->initFlags(); + } - driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map")); + driver = device ? device->getVideoDriver() : nullptr; + smgr = m_rendering_engine ? m_rendering_engine->get_scene_manager() : nullptr; - smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true); + if (driver) + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map")); + if (smgr) + smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true); // Reinit runData runData = GameRunData(); runData.time_from_last_punch = 10.0; - m_game_ui->initFlags(); - m_first_loop_after_window_activation = true; m_touch_use_crosshair = g_settings->getBool("touch_use_crosshair"); @@ -1134,7 +1141,8 @@ bool Game::startup(bool *kill, if (!createClient(start_data)) return false; - m_rendering_engine->initialize(client, hud); + if (m_rendering_engine) + m_rendering_engine->initialize(client, hud); return true; } @@ -1146,7 +1154,7 @@ void Game::run() RunStats stats = {}; CameraOrientation cam_view_target = {}; CameraOrientation cam_view = {}; - FpsControl draw_times; + FpsControl& draw_times = client->getFpsControl(); f32 dtime; // in seconds /* Clear the profiler */ @@ -1166,7 +1174,7 @@ void Game::run() ); const bool initial_window_maximized = g_settings->getBool("window_maximized"); - while (m_rendering_engine->run() + while ((!m_rendering_engine || m_rendering_engine->run()) && !(*kill || g_gamecallback->shutdown_requested || (server && server->isShutdownRequested()))) { @@ -1175,16 +1183,18 @@ void Game::run() // + Sleep time until the wanted FPS are reached draw_times.limit(device, &dtime, g_menumgr.pausesGame()); - const auto current_dynamic_info = ClientDynamicInfo::getCurrent(); - if (!current_dynamic_info.equal(client_display_info)) { - client_display_info = current_dynamic_info; - dynamic_info_send_timer = 0.2f; - } + if (m_rendering_engine) { + const auto current_dynamic_info = ClientDynamicInfo::getCurrent(); + if (!current_dynamic_info.equal(client_display_info)) { + client_display_info = current_dynamic_info; + dynamic_info_send_timer = 0.2f; + } - if (dynamic_info_send_timer > 0.0f) { - dynamic_info_send_timer -= dtime; - if (dynamic_info_send_timer <= 0.0f) { - client->sendUpdateClientInfo(current_dynamic_info); + if (dynamic_info_send_timer > 0.0f) { + dynamic_info_send_timer -= dtime; + if (dynamic_info_send_timer <= 0.0f) { + client->sendUpdateClientInfo(current_dynamic_info); + } } } @@ -1200,12 +1210,17 @@ void Game::run() processQueues(); - m_game_ui->clearInfoText(); + if (m_game_ui) + m_game_ui->clearInfoText(); updateProfilers(stats, draw_times, dtime); processUserInput(dtime); // Update camera before player movement to avoid camera lag of one frame - updateCameraDirection(&cam_view_target, dtime); + if (m_rendering_engine) + updateCameraDirection(&cam_view_target, dtime); + else + updateCameraDirectionNoRender(&cam_view_target, dtime); + cam_view.camera_yaw += (cam_view_target.camera_yaw - cam_view.camera_yaw) * m_cache_cam_smoothing; cam_view.camera_pitch += (cam_view_target.camera_pitch - @@ -1222,27 +1237,32 @@ void Game::run() updateDebugState(); updateCamera(dtime); updateSound(dtime); - processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud); + processPlayerInteraction(dtime, m_game_ui ? m_game_ui->m_flags.show_hud: false); updateFrame(&graph, &stats, dtime, cam_view); updateProfilerGraphs(&graph); - if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) { - showPauseMenu(); + if (m_rendering_engine) { + if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) { + showPauseMenu(); + } } } - RenderingEngine::autosaveScreensizeAndCo(initial_screen_size, initial_window_maximized); + if (m_rendering_engine) + RenderingEngine::autosaveScreensizeAndCo(initial_screen_size, initial_window_maximized); } void Game::shutdown() { - auto formspec = m_game_ui->getFormspecGUI(); - if (formspec) - formspec->quitMenu(); + if (m_game_ui) { + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec) + formspec->quitMenu(); - // Clear text when exiting. - m_game_ui->clearText(); + // Clear text when exiting. + m_game_ui->clearText(); + } if (g_touchscreengui) g_touchscreengui->hide(); @@ -1264,7 +1284,8 @@ void Game::shutdown() g_menumgr.deletingMenu(g_menumgr.m_stack.front()); } - m_game_ui->deleteFormspec(); + if (m_game_ui) + m_game_ui->deleteFormspec(); chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); @@ -1420,6 +1441,11 @@ bool Game::createClient(const GameStartData &start_data) if (!draw_control) return false; + if (!m_rendering_engine) { + draw_control->norender = true; + draw_control->wanted_range = 120.f; + } + bool could_connect, connect_aborted; if (!connectToServer(start_data, &could_connect, &connect_aborted)) return false; @@ -1442,7 +1468,12 @@ bool Game::createClient(const GameStartData &start_data) return false; } - auto *scsf = new GameGlobalShaderConstantSetterFactory(client); + if (!m_rendering_engine) { + client->afterContentReceived(); + return true; + } + + auto* scsf = new GameGlobalShaderConstantSetterFactory(client); shader_src->addShaderConstantSetterFactory(scsf); shader_src->addShaderConstantSetterFactory( @@ -1484,7 +1515,7 @@ bool Game::createClient(const GameStartData &start_data) crack_animation_length = 5; } - if (!initGui()) + if (m_game_ui && !initGui()) return false; /* Set window caption @@ -1593,10 +1624,13 @@ bool Game::connectToServer(const GameStartData &start_data, << "\" isIPv6=" << connect_address.isIPv6() << std::endl; } + try { - client = new Client(start_data.name.c_str(), + client = new Client( + start_data.name.c_str(), start_data.password, + start_data.playerai, *draw_control, texture_src, shader_src, itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr, m_rendering_engine, m_game_ui.get(), @@ -1618,16 +1652,18 @@ bool Game::connectToServer(const GameStartData &start_data, simple_singleplayer_mode || local_server_mode); try { + FpsControl fps_control; + input->clear(); - FpsControl fps_control; f32 dtime; f32 wait_time = 0; // in seconds bool did_fallback = false; fps_control.reset(); - while (m_rendering_engine->run()) { + //while (m_rendering_engine->run()) { + while (m_rendering_engine ? m_rendering_engine->run() : !(*kill)) { fps_control.limit(device, &dtime); @@ -1692,7 +1728,7 @@ bool Game::getServerContent(bool *aborted) fps_control.reset(); - while (m_rendering_engine->run()) { + while (m_rendering_engine ? m_rendering_engine->run() : !(*kill)) { fps_control.limit(device, &dtime); @@ -1726,12 +1762,19 @@ bool Game::getServerContent(bool *aborted) if (!client->itemdefReceived()) { progress = 25; - m_rendering_engine->draw_load_screen(wstrgettext("Item definitions..."), + if (m_rendering_engine) + m_rendering_engine->draw_load_screen(wstrgettext("Item definitions..."), guienv, texture_src, dtime, progress); + else + infostream << "Item definitions..." << progress << std::endl; + } else if (!client->nodedefReceived()) { progress = 30; - m_rendering_engine->draw_load_screen(wstrgettext("Node definitions..."), - guienv, texture_src, dtime, progress); + if (m_rendering_engine) + m_rendering_engine->draw_load_screen(wstrgettext("Node definitions..."), + guienv, texture_src, dtime, progress); + else + infostream << "Node definitions..." << progress << std::endl; } else { std::ostringstream message; std::fixed(message); @@ -1756,8 +1799,11 @@ bool Game::getServerContent(bool *aborted) } progress = 30 + client->mediaReceiveProgress() * 35 + 0.5; - m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv, - texture_src, dtime, progress); + if (m_rendering_engine) + m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv, + texture_src, dtime, progress); + else + infostream << message.str() << progress << std::endl; } } @@ -1850,13 +1896,17 @@ void Game::updateDebugState() bool has_debug = client->checkPrivilege("debug"); bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG); - if (m_game_ui->m_flags.show_basic_debug) { - if (!has_basic_debug) - m_game_ui->m_flags.show_basic_debug = false; - } else if (m_game_ui->m_flags.show_minimal_debug) { - if (has_basic_debug) - m_game_ui->m_flags.show_basic_debug = true; + if (m_game_ui) { + if (m_game_ui->m_flags.show_basic_debug) { + if (!has_basic_debug) + m_game_ui->m_flags.show_basic_debug = false; + } + else if (m_game_ui->m_flags.show_minimal_debug) { + if (has_basic_debug) + m_game_ui->m_flags.show_basic_debug = true; + } } + if (!has_basic_debug) hud->disableBlockBounds(); if (!has_debug) @@ -1884,7 +1934,8 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, g_profiler->print(infostream); } - m_game_ui->updateProfiler(); + if (m_game_ui) + m_game_ui->updateProfiler(); g_profiler->clear(); } @@ -1953,32 +2004,37 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times, void Game::processUserInput(f32 dtime) { - // Reset input if window not active or some menu is active - if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) { - if (m_game_focused) { - m_game_focused = false; - infostream << "Game lost focus" << std::endl; - input->releaseAllKeys(); - } else { - input->clear(); - } + if (!m_rendering_engine) { + m_game_focused = true; + } + else { + // Reset input if window not active or some menu is active + if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) { + if (m_game_focused) { + m_game_focused = false; + infostream << "Game lost focus" << std::endl; + input->releaseAllKeys(); + } else { + input->clear(); + } - if (g_touchscreengui) - g_touchscreengui->hide(); + if (g_touchscreengui) + g_touchscreengui->hide(); - } else { - if (g_touchscreengui) { - /* on touchscreengui step may generate own input events which ain't - * what we want in case we just did clear them */ - g_touchscreengui->show(); - g_touchscreengui->step(dtime); - } + } else { + if (g_touchscreengui) { + /* on touchscreengui step may generate own input events which ain't + * what we want in case we just did clear them */ + g_touchscreengui->show(); + g_touchscreengui->step(dtime); + } - m_game_focused = true; - } + m_game_focused = true; + } - if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) { - gui_chat_console->closeConsoleAtOnce(); + if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) { + gui_chat_console->closeConsoleAtOnce(); + } } // Input handler step() (used by the random input generator) @@ -2554,6 +2610,18 @@ void Game::checkZoomEnabled() m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); } +void Game::updateCameraDirectionNoRender(CameraOrientation* cam, float dtime) { + LocalPlayer* player = client->getEnv().getLocalPlayer(); + v3f speed = player->getSpeed(); + + if (speed.getLength() > 0.1) { + v3f dir = speed; + dir.normalize(); + + cam->camera_yaw = std::atan2(dir.Y, dir.X); + } +} + void Game::updateCameraDirection(CameraOrientation *cam, float dtime) { auto *cur_control = device->getCursorControl(); @@ -2868,6 +2936,9 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event, CameraOrientation *cam) { + if (!client->getParticleManager()) + return; + LocalPlayer *player = client->getEnv().getLocalPlayer(); client->getParticleManager()->handleParticleEvent(event, client, player); } @@ -3127,15 +3198,23 @@ void Game::updateChat(f32 dtime) auto &buf = chat_backend->getRecentBuffer(); if (buf.getLinesModified()) { buf.resetLinesModified(); - m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount()); + + if (m_game_ui) + m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount()); + else + infostream << chat_backend->getRecentChat().c_str(); } // Make sure that the size is still correct - m_game_ui->updateChatSize(); + if (m_game_ui) + m_game_ui->updateChatSize(); } void Game::updateCamera(f32 dtime) { + if (!camera) + return; + LocalPlayer *player = client->getEnv().getLocalPlayer(); /* @@ -3206,16 +3285,20 @@ void Game::updateSound(f32 dtime) { // Update sound listener LocalPlayer *player = client->getEnv().getLocalPlayer(); + v3f position = camera ? camera->getCameraNode()->getPosition() : player->getPosition(); + v3f direction = camera ? camera->getDirection() : player->getDirection(); + v3f up = camera ? camera->getCameraNode()->getUpVector() : v3f(0, 1, 0); + ClientActiveObject *parent = player->getParent(); - v3s16 camera_offset = camera->getOffset(); + v3s16 camera_offset = camera ? camera->getOffset() : v3s16(); sound_manager->updateListener( - (1.0f/BS) * camera->getCameraNode()->getPosition() + (1.0f/BS) * position + intToFloat(camera_offset, 1.0f), (1.0f/BS) * (parent ? parent->getVelocity() : player->getSpeed()), - camera->getDirection(), - camera->getCameraNode()->getUpVector()); + direction, + up); - sound_volume_control(sound_manager.get(), device->isWindowActive()); + sound_volume_control(sound_manager.get(), device ? device->isWindowActive() : true); // Tell the sound maker whether to make footstep sounds soundmaker->makes_footstep_sound = player->makes_footstep_sound; @@ -3224,7 +3307,7 @@ void Game::updateSound(f32 dtime) if (player->makes_footstep_sound) soundmaker->step(dtime); - ClientMap &map = client->getEnv().getClientMap(); + Map &map = client->getEnv().getMap(); MapNode n = map.getNode(player->getFootstepNodePos()); soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; } @@ -3232,6 +3315,9 @@ void Game::updateSound(f32 dtime) void Game::processPlayerInteraction(f32 dtime, bool show_hud) { + if (!camera) + return; + LocalPlayer *player = client->getEnv().getLocalPlayer(); const v3f camera_direction = camera->getDirection(); @@ -3981,10 +4067,10 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, Fog range */ - if (sky->getFogDistance() >= 0) { + if (sky && sky->getFogDistance() >= 0) { draw_control->wanted_range = MYMIN(draw_control->wanted_range, sky->getFogDistance()); } - if (draw_control->range_all && sky->getFogDistance() < 0) { + if (draw_control->range_all && sky && sky->getFogDistance() < 0) { runData.fog_range = FOG_RANGE_ALL; } else { runData.fog_range = draw_control->wanted_range * BS; @@ -3995,8 +4081,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, */ u32 daynight_ratio = client->getEnv().getDayNightRatio(); float time_brightness = decode_light_f((float)daynight_ratio / 1000.0); - float direct_brightness; - bool sunlight_seen; + float direct_brightness = 1.0; + bool sunlight_seen = true; // When in noclip mode force same sky brightness as above ground so you // can see properly @@ -4004,12 +4090,12 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, client->checkPrivilege("fly")) { direct_brightness = time_brightness; sunlight_seen = true; - } else { + } else if (client->getEnv().isClientMap()) { float old_brightness = sky->getBrightness(); direct_brightness = client->getEnv().getClientMap() - .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), - daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen) - / 255.0; + .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), + daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen) + / 255.0; } float time_of_day_smooth = runData.time_of_day_smooth; @@ -4032,9 +4118,10 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.time_of_day_smooth = time_of_day_smooth; - sky->update(time_of_day_smooth, time_brightness, direct_brightness, - sunlight_seen, camera->getCameraMode(), player->getYaw(), - player->getPitch()); + if (sky) + sky->update(time_of_day_smooth, time_brightness, direct_brightness, + sunlight_seen, camera->getCameraMode(), player->getYaw(), + player->getPitch()); /* Update clouds @@ -4045,7 +4132,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Update particles */ - client->getParticleManager()->step(dtime); + if (client->getParticleManager()) + client->getParticleManager()->step(dtime); /* Damage camera tilt @@ -4082,7 +4170,9 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, // Update wielded tool ItemStack selected_item, hand_item; ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); - camera->wield(tool_item); + + if (camera) + camera->wield(tool_item); } /* @@ -4094,61 +4184,65 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, float update_draw_list_delta = 0.2f; - v3f camera_direction = camera->getDirection(); + if (client->getEnv().isClientMap()) { + v3f camera_direction = camera->getDirection(); - // call only one of updateDrawList, touchMapBlocks, or updateShadow per frame - // (the else-ifs below are intentional) - if (runData.update_draw_list_timer >= update_draw_list_delta + // call only one of updateDrawList, touchMapBlocks, or updateShadow per frame + // (the else-ifs below are intentional) + if (runData.update_draw_list_timer >= update_draw_list_delta || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 || m_camera_offset_changed || client->getEnv().getClientMap().needsUpdateDrawList()) { - runData.update_draw_list_timer = 0; - client->getEnv().getClientMap().updateDrawList(); - runData.update_draw_list_last_cam_dir = camera_direction; - client->getEnv().getClientMap().updateCacheBuffers(driver); - } else if (runData.touch_blocks_timer > update_draw_list_delta) { - client->getEnv().getClientMap().touchMapBlocks(); - runData.touch_blocks_timer = 0; - client->getEnv().getClientMap().updateCacheBuffers(driver); - } else if (RenderingEngine::get_shadow_renderer()) { - updateShadows(); + runData.update_draw_list_timer = 0; + client->getEnv().getClientMap().updateDrawList(); + runData.update_draw_list_last_cam_dir = camera_direction; + } + else if (runData.touch_blocks_timer > update_draw_list_delta) { + client->getEnv().getClientMap().touchMapBlocks(); + runData.touch_blocks_timer = 0; + } + else if (RenderingEngine::get_shadow_renderer()) { + updateShadows(); + } } - m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); + if (m_game_ui) { + m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); - /* - make sure menu is on top - 1. Delete formspec menu reference if menu was removed - 2. Else, make sure formspec menu is on top - */ - auto formspec = m_game_ui->getFormspecGUI(); - do { // breakable. only runs for one iteration - if (!formspec) - break; - - if (formspec->getReferenceCount() == 1) { - // See GUIFormSpecMenu::create what refcnt = 1 means - m_game_ui->deleteFormspec(); - break; - } + /* + make sure menu is on top + 1. Delete formspec menu reference if menu was removed + 2. Else, make sure formspec menu is on top + */ + auto formspec = m_game_ui->getFormspecGUI(); + do { // breakable. only runs for one iteration + if (!formspec) + break; - auto &loc = formspec->getFormspecLocation(); - if (loc.type == InventoryLocation::NODEMETA) { - NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p); - if (!meta || meta->getString("formspec").empty()) { - formspec->quitMenu(); + if (formspec->getReferenceCount() == 1) { + // See GUIFormSpecMenu::create what refcnt = 1 means + m_game_ui->deleteFormspec(); break; } - } - if (isMenuActive()) - guiroot->bringToFront(formspec); - } while (false); + auto& loc = formspec->getFormspecLocation(); + if (loc.type == InventoryLocation::NODEMETA) { + NodeMetadata* meta = client->getEnv().getClientMap().getNodeMetadata(loc.p); + if (!meta || meta->getString("formspec").empty()) { + formspec->quitMenu(); + break; + } + } + + if (isMenuActive()) + guiroot->bringToFront(formspec); + } while (false); + } /* ==================== Drawing begins ==================== */ - if (device->isWindowVisible()) + if (device && device->isWindowVisible()) drawScene(graph, stats); /* ==================== End scene ==================== @@ -4308,6 +4402,9 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats) void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_sky) { + if (!m_rendering_engine) + return; + m_rendering_engine->draw_load_screen(wstrgettext(msg), guienv, texture_src, dtime, percent, draw_sky); } @@ -4355,12 +4452,15 @@ void Game::readSettings() void Game::showDeathFormspec() { + if (!m_game_ui) + return; + static std::string formspec_str = std::string("formspec_version[1]") + SIZE_TAG "bgcolor[#320000b4;true]" "label[4.85,1.35;" + gettext("You died") + "]" - "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" + "button_exit[4,3;3,0.5;§;" + gettext("Respawn") + "]" ; /* Create menu */ diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 0577943cb7cd7..941db984b9c8f 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -141,12 +141,16 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ if (m_flags.show_basic_debug) { v3f player_position = player->getPosition(); + v3s16 block_pos = floatToInt(player_position, BS) / MAP_BLOCKSIZE; + std::ostringstream os(std::ios_base::binary); os << std::setprecision(1) << std::fixed << "pos: (" << (player_position.X / BS) << ", " << (player_position.Y / BS) << ", " << (player_position.Z / BS) - << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° " + << ")" + << "| block_pos: (" << block_pos.X << ", " << block_pos.Y << ", " << block_pos.Z-1 << ")" + << "| yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° " << yawToDirectionString(cam.camera_yaw) << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°" << " | seed: " << ((u64)client->getMapSeed()); diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index e606e1edc5d08..53b31235be21e 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "client.h" #include "content_cao.h" +#include "player_ai.h" /* PlayerSettings @@ -88,6 +89,18 @@ LocalPlayer::~LocalPlayer() m_player_settings.deregisterSettingsCallback(); } +void LocalPlayer::setPlayerAI(std::string className) { + if (className.empty()) + return; + + if (className == "playerai") { + setPlayerAI(); + return; + } + + errorstream << "PlayerAI class name \"" << className.c_str() << "\" not found" << std::endl; +} + static aabb3f getNodeBoundingBox(const std::vector &nodeboxes) { if (nodeboxes.empty()) @@ -551,6 +564,12 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d) move(dtime, env, pos_max_d, NULL); } +void LocalPlayer::stepAI(float dtime) { + if (!ai) + return; + ai->step(dtime); +} + void LocalPlayer::applyControl(float dtime, Environment *env) { // Clear stuff diff --git a/src/client/localplayer.h b/src/client/localplayer.h index fbccb159152cd..a6a1c20587ede 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -31,6 +31,7 @@ class GenericCAO; class ClientActiveObject; class ClientEnvironment; class IGameDef; +class PlayerAI; struct collisionMoveResult; enum class LocalPlayerAnimation @@ -142,6 +143,16 @@ class LocalPlayer : public Player void setPitch(f32 pitch) { m_pitch = pitch; } f32 getPitch() const { return m_pitch; } + // + // Not confirmed that it works + v3f getDirection() const { + return v3f( + std::cos(m_yaw * (180.f / M_PI)), + std::sin(m_pitch * (180.f / M_PI)), + std::sin(m_yaw * (180.f / M_PI)) + ).normalize(); + } + inline void setPosition(const v3f &position) { m_position = position; @@ -181,6 +192,21 @@ class LocalPlayer : public Player inline PlayerSettings &getPlayerSettings() { return m_player_settings; } + template void setPlayerAI() { + static_assert(std::is_base_of::value, "T must inherit from PlayerAI"); + + delete ai; + ai = new T(this); + } + + void setPlayerAI(std::string className); + + void stepAI(float dtime); + + inline Client* getClient() { + return m_client; + } + private: void accelerate(const v3f &target_speed, const f32 max_increase_H, const f32 max_increase_V, const bool use_pitch); @@ -234,4 +260,6 @@ class LocalPlayer : public Player PlayerSettings m_player_settings; Lighting m_lighting; + + PlayerAI* ai = nullptr; }; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 65f64e466867b..67edc0678982f 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -631,6 +631,35 @@ void PartialMeshBuffer::afterDraw() const m_vertex_indexes = m_buffer->Indices.steal(); } +void dumpObj(std::string path, std::string name, irr::video::S3DVertex* vertices, size_t numVertices, u16* indices, size_t numIndices) { + std::stringstream ss; + ss << "o " << name.c_str() << std::endl; + + for (size_t i = 0; i < numVertices; i++) { + auto pos = vertices[i].Pos; + ss << "v\t" << std::fixed << std::setprecision(6) << pos.X << "\t" + << std::fixed << std::setprecision(6) << pos.Y << "\t" + << std::fixed << std::setprecision(6) << pos.Z << std::endl; + + auto normal = vertices[i].Normal; + ss << "vn\t" << std::fixed << std::setprecision(6) << normal.X << "\t" + << std::fixed << std::setprecision(6) << normal.Y << "\t" + << std::fixed << std::setprecision(6) << normal.Z << std::endl; + + auto uv = vertices[i].TCoords; + ss << "vt\t" << std::fixed << std::setprecision(6) << uv.X << "\t" + << std::fixed << std::setprecision(6) << uv.Y << std::endl; + } + for (size_t i = 0; i < numIndices; i += 3) { + ss << "f " << indices[i]+1 << " " << indices[i+1] + 1 << " " << indices[i+2] + 1 + << std::endl; + } + + std::ofstream o(path); + o << ss.rdbuf(); + o.close(); +} + /* MapBlockMesh */ @@ -801,18 +830,17 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs t.updateAttributes(); m_transparent_triangles.push_back(t); } - } else { - auto offset = intToFloat(mesh_grid.getMeshPos(data->m_blockpos) * MAP_BLOCKSIZE, BS); + } + else { + auto offset = intToFloat(bp * MAP_BLOCKSIZE, BS); auto size = p.vertices.size(); auto translatedVertices = new irr::video::S3DVertex[size]; + std::memcpy(translatedVertices, p.vertices.data(), size * sizeof(irr::video::S3DVertex)); for (size_t i = 0; i < size; i++) { - auto vertex = p.vertices[i]; - vertex.Pos += offset; - translatedVertices[i] = vertex; + translatedVertices[i].Pos += offset; } - buf->append(translatedVertices, p.vertices.size(), - &p.indices[0], p.indices.size()); + buf->append(translatedVertices, p.vertices.size(), p.indices.data(), p.indices.size()); delete[] translatedVertices; } @@ -861,7 +889,7 @@ bool MapBlockMesh::isMeshBufferAnimated(u32 layer, u32 index) { scene::IMeshBuffer* buf = mesh->getMeshBuffer(i); bool is_animated = false; - for (auto info : m_animation_info) + for (auto& info : m_animation_info) if (info.first.second == i) { // // Has animation @@ -1019,44 +1047,6 @@ bool MapBlockMesh::animate(bool faraway, float time, u32 daynight_ratio, u32 buf return false; } -bool MapBlockMesh::animateTransparent(float time) -{ - if (!m_has_animation) { - m_animation_force_timer = 100000; - return false; - } - - m_animation_force_timer = myrand_range(5, 100); - - // Texture animation - for (auto &it : m_animation_info) { - - const TileLayer &tile = it.second.tile; - // Figure out current frame - int frameno = (int)(time * 1000 / tile.animation_frame_length_ms - + it.second.frame_offset) % tile.animation_frame_count; - // If frame doesn't change, skip - if (frameno == it.second.frame) - break; - - it.second.frame = frameno; - - scene::IMeshBuffer *buf = m_mesh[it.first.first]->getMeshBuffer(it.first.second); - - const FrameSpec &frame = (*tile.frames)[frameno]; - buf->getMaterial().setTexture(0, frame.texture); - if (m_enable_shaders) { - if (frame.normal_texture) - buf->getMaterial().setTexture(1, frame.normal_texture); - buf->getMaterial().setTexture(2, frame.flags_texture); - } - - break; - } - - return true; -} - void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) { // nothing to do if the entire block is opaque diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 588af8ec5e066..a8f89d32bd881 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -200,7 +200,6 @@ class MapBlockMesh // Returns true if anything has been changed. bool animate(bool faraway, float time, u32 daynight_ratio, u32 bufferIndex); bool animateCracks(int crack); - bool animateTransparent(float time); scene::IMesh *getMesh() { @@ -263,7 +262,7 @@ class MapBlockMesh video::ITexture* getBufferMainTexture(u32 layer, u32 bufferIndex); private: - scene::IMesh *m_mesh[MAX_TILE_LAYERS]; + scene::IMesh* m_mesh[MAX_TILE_LAYERS] = { nullptr, nullptr }; std::vector m_minimap_mapblocks; ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; diff --git a/src/client/memoryManager.cpp b/src/client/memoryManager.cpp index b131bae516cc2..81da36a03fd32 100644 --- a/src/client/memoryManager.cpp +++ b/src/client/memoryManager.cpp @@ -34,6 +34,8 @@ MemoryManager::MemoryInfo MemoryManager::allocate(u32 chunkSize) { newInfo.chunkEnd = newInfo.chunkStart + chunkSize; memory.insert(it, newInfo); + assert(newInfo.chunkStart % align_size == 0); + if (used_mem < newInfo.chunkEnd) used_mem = newInfo.chunkEnd; @@ -49,9 +51,14 @@ void MemoryManager::free(const MemoryManager::MemoryInfo& info) { if (it->id != info.id) continue; + if (it+1 == memory.end() && memory.size() >= 2) { + used_mem = (it-1)->chunkEnd; + } + memory.erase(it); + return; } assert(true); // "Memory not found" -} \ No newline at end of file +} diff --git a/src/client/memoryManager.h b/src/client/memoryManager.h index 8fbb20f74bb34..b3afef23bfc57 100644 --- a/src/client/memoryManager.h +++ b/src/client/memoryManager.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -10,6 +12,7 @@ class MemoryManager { u32 chunkStart = 0; u32 chunkEnd = 0; + // In bytes inline u32 size() { return chunkEnd - chunkStart; } diff --git a/src/client/mesh_buffer_handler.cpp b/src/client/mesh_buffer_handler.cpp new file mode 100644 index 0000000000000..a1cc1f0ea5457 --- /dev/null +++ b/src/client/mesh_buffer_handler.cpp @@ -0,0 +1,673 @@ +#include "mesh_buffer_handler.h" +#include "IMaterialRenderer.h" + +//void OpenGLSubDataArray::optimize(v3s16 view_center) { +// if (data.size() <= 1) +// return; +// +// // +// // Sort these so openGL data will load by blocks +// for (int i = 0; i < data.size() - 1; i++) { +// OpenGLSubData* a = data[i]; +// OpenGLSubData* b = data[i + 1]; +// +// if (a->offset + a->size >= b->offset && a->offset <= b->offset + b->size) +// continue; +// +// const s16 distA = (a->block_pos - view_center).getLengthSQ(); +// const s16 distB = (b->block_pos - view_center).getLengthSQ(); +// +// if (distA <= distB) +// continue; +// +// data[i] = b; +// data[i + 1] = a; +// i -= 2; +// } +// +// std::vector toDelete; +// for (std::vector::reverse_iterator it = data.rbegin(); it != data.rend(); it++) { +// for (std::vector::reverse_iterator cmpIt = std::next(it); cmpIt != data.rend(); cmpIt++) { +// if ((*cmpIt)->offset != (*it)->offset) +// continue; +// if ((*cmpIt)->size != (*it)->size) +// continue; +// +// toDelete.push_back(*cmpIt); +// } +// } +// +// for (auto* elem : toDelete) { +// auto it = std::find(data.begin(), data.end(), elem); +// if (it == data.end()) +// continue; +// +// delete* it; +// data.erase(it); +// } +// +// // Define the structure for groups. +// //struct Group { +// // u32 begin = 0; +// // u32 end = 0; +// // irr::core::array data; +// //}; +// +// //std::vector groups; +// +// //// Initial grouping based on offsets and sizes. +// //for (const auto d : data) { +// // groups.push_back({ d->offset, d->offset + d->size, irr::core::array() }); +// //} +// +// //// Sort groups based on 'begin' to simplify merging process. +// //std::sort(groups.begin(), groups.end(), [](const Group& a, const Group& b) { +// // return a.begin < b.begin; +// //}); +// +// //// Attempt to collapse overlapping or contiguous groups. +// //bool mergedOnce = false; +// //bool merged; +// //do { +// // merged = false; +// // for (size_t i = 0; i < groups.size(); i++) { +// // for (size_t j = i + 1; j < groups.size(); j++) { +// // auto& a = groups[i]; +// // auto& b = groups[j]; +// +// // // Check if 'a' and 'b' overlap or are contiguous. +// // if (a.end >= b.begin) { +// // // Merge 'b' into 'a' and erase 'b'. +// // a.end = std::max(a.end, b.end); +// // groups.erase(groups.begin() + j); +// // merged = true; +// // mergedOnce = true; +// // break; // Start over since we modified the vector. +// // } +// // } +// // if (merged) { +// // break; // Restart merging process if any merge occurred. +// // } +// // } +// //} while (merged); +// +// //// nothing is going to change +// //if (!mergedOnce) +// // return; +// +// //// Allocate memory for the groups based on their new sizes. +// //for (auto& group : groups) { +// // group.data.set_used(group.end - group.begin); // Initially empty, fill in next step. +// //} +// +// //// Copy data into the newly allocated groups. +// //int dataInserted = 0; +// //for (auto d : data) { +// // if (d->isEmpty()) { +// // dataInserted++; +// // continue; +// // } +// +// // for (auto& group : groups) { +// // if (d->offset >= group.begin && d->offset + d->size <= group.end) { +// // // Calculate the correct offset within the group. +// // auto dest = group.data.pointer() + (d->offset - group.begin); +// // std::copy(d->data.pointer(), d->data.pointer() + d->size, dest); +// // dataInserted++; +// // break; // Data can only belong to one group, so stop once found. +// // } +// // } +// //} +// //assert(dataInserted == data.size()); +// +// //// Prepare the new data vector with the grouped data. +// //std::vector newData; +// //for (auto& group : groups) { +// // auto d = new OpenGLSubData(); +// // d->data = std::move(group.data); +// // d->size = group.end - group.begin; +// // d->offset = group.begin; +// // newData.push_back(d); +// //} +// +// //// Cleanup +// //for (auto d : data) +// // delete d; +// +// //// Replace the old data with the new, optimized data. +// //data = std::move(newData); +//} + +MeshBufferWorkerThread::MeshBufferWorkerThread(MeshUpdateManager* meshUpdateManager, video::CNullDriver* driver) : Thread("mesh buffer worker thread") { + this->driver = driver; + this->meshUpdateManager = meshUpdateManager; +} + +MeshBufferWorkerThread::~MeshBufferWorkerThread() { + cache_buffers.clear(); +} + +void MeshBufferWorkerThread::setView(v3s16 min, v3s16 max) { + std::unique_lock lock(m_mutex_view_range, std::try_to_lock); + if (!lock.owns_lock()) + return; + + view_center = (max + min) / 2; + view_min = min; + view_max = max; +} + +bool MeshBufferWorkerThread::getCacheBuffers(TextureBufListMaps& maps) { + std::unique_lock lock(m_mutex_cache_buffers, std::try_to_lock); + if (!lock.owns_lock()) + return false; + + cache_buffers.grabEachTexture(); + maps = cache_buffers; + return true; +} + +void MeshBufferWorkerThread::removeBlocks(v3s16* positions, size_t num) { + MutexAutoLock lock(m_mutex_remove_load_blocks); + remove_load_mapblocks.insert(remove_load_mapblocks.end(), positions, positions + num); +} + +std::vector MeshBufferWorkerThread::getMeshUpdateResults() { + MutexAutoLock lock(m_mutex_mesh_update_results); + auto vector = std::move(mesh_update_results); + mesh_update_results = std::vector(); + return vector; +} + +bool MeshBufferWorkerThread::unload_block(v3s16 pos, TextureBufListMaps::LoadBlockData& loadBlockData) { + if (buffer_data.find(pos) == buffer_data.end()) + return false; + + auto& bufferList = buffer_data[pos]; + for (auto& meshBufferData : bufferList) { + auto data = cache_buffers.getNoCreate(meshBufferData.texture, meshBufferData.layer); + if (!data) { + continue; + } + + if (meshBufferData.indexMemory.is_valid()) { + OpenGLSubData* indexData = new OpenGLSubData(); + indexData->size = meshBufferData.indexMemory.size(); + indexData->offset = meshBufferData.indexMemory.chunkStart; + + data->index_memory.free(meshBufferData.indexMemory); + + TextureBufListMaps::LoadData loadData; + loadData.layer = meshBufferData.layer; + loadData.texture = meshBufferData.texture; + loadData.glIndexSubData = indexData; + loadData.drawPrimitiveCount = (data->index_memory.used_mem / sizeof(u32)) / 3; + + loadBlockData.data.push_back(loadData); + } + + if (meshBufferData.vertexMemory.is_valid()) + data->vertex_memory.free(meshBufferData.vertexMemory); + } + + buffer_data.erase(pos); + return true; +} + +void MeshBufferWorkerThread::doUpdate() { + + static const size_t max_load_data_queue = 70; + + // + // Don't stack on a huge queue. + // + size_t num_load_data_queue = 0; + { + MutexAutoLock lock(m_mutex_cache_buffers); + num_load_data_queue = cache_buffers.loadData.size(); + if (num_load_data_queue >= max_load_data_queue) + return; + } + + // + // Gather results + // + std::vector results; + std::unordered_set unload_mapblocks; + std::unordered_set mapblocks_needs_to_reload; + MeshUpdateResult r; + meshUpdateManager->undelayAll(); + while (meshUpdateManager->getNextResult(r)) { + auto& p = r.p; + + results.push_back(r); + mesh_update_result_queue.push_back(r); + } + + { + MutexAutoLock lock(m_mutex_mesh_update_results); + mesh_update_results.insert(mesh_update_results.end(), results.begin(), results.end()); + } + + // + // sort and cleanup mesh update result queue + // + for (int i = mesh_update_result_queue.size()-1; i > 0; i--) { + auto r = mesh_update_result_queue[i]; + for (int j = i - 1; j >= 0; j--) { + auto& r2 = mesh_update_result_queue[j]; + if (r.p != r2.p) + continue; + + mesh_update_result_queue.erase(mesh_update_result_queue.begin() + j); + i--; + } + } + + std::unordered_set test; + for (auto it : mesh_update_result_queue) { + assert(test.find(it.p) == test.end()); + test.insert(it.p); + } + + // + // Sort by urgent + for (int i = 0; i < (int)mesh_update_result_queue.size() - 1; i++) { + auto r = mesh_update_result_queue[i]; + auto r2 = mesh_update_result_queue[i+1]; + + if (!r2.urgent) + continue; + if (r.urgent) + continue; + + mesh_update_result_queue[i] = r2; + mesh_update_result_queue[i+1] = r; + + i -= 2; + if (i < 0) + i = 0; + } + + static const v3s16 view_padding(0, 0, 0); + v3s16 cache_view_min; + v3s16 cache_view_max; + v3s16 cache_view_center; + { + MutexAutoLock lock(m_mutex_view_range); + cache_view_min = view_min; + cache_view_max = view_max; + cache_view_center = view_center; + } + + cache_view_min -= view_padding; + cache_view_max += view_padding; + + // + // Sort by distance + std::vector distances; + distances.reserve(mesh_update_results.size()); + for (auto& r : mesh_update_result_queue) { + distances.push_back((r.p - cache_view_center).getLengthSQ()); + } + + for (int i = 0; i < (int)mesh_update_result_queue.size() - 1; i++) { + auto r = mesh_update_result_queue[i]; + if (r.urgent) + continue; + + auto distR = distances[i]; + auto distR2 = distances[i + 1]; + + if (distR2 >= distR) + continue; + + mesh_update_result_queue[i] = mesh_update_result_queue[i + 1]; + mesh_update_result_queue[i+1] = r; + distances[i] = distR2; + distances[i + 1] = distR; + + i--; + if (i < 0) + i = 0; + } + + while(!mesh_update_result_queue.empty()) { + auto r = mesh_update_result_queue.front(); + mesh_update_result_queue.erase(mesh_update_result_queue.begin()); + + if (r.mesh.get()) { + bool is_empty = true; + for (int l = 0; l < MAX_TILE_LAYERS; l++) + if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) + is_empty = false; + + if (is_empty) { + if (buffer_data.find(r.p) != buffer_data.end()) + unload_mapblocks.insert(r.p); + } + else + mapblocks_needs_to_reload.insert(r.p); + + load_mapblocks[r.p] = r; + + num_load_data_queue++; + } + + if (num_load_data_queue >= max_load_data_queue) + break; + } + + // + // Unload buffers + // + for (auto& it : buffer_data) { + auto& p = it.first; + if (p.X > cache_view_min.X && p.X < cache_view_max.X && + p.Z > cache_view_min.Z && p.Z < cache_view_max.Z) + continue; + + if (buffer_data.find(r.p) == buffer_data.end()) + continue; + + unload_mapblocks.insert(p); + } + + std::vector loadOrderVec; + + for (auto pos : unload_mapblocks) { + TextureBufListMaps::LoadOrder loadOrder; + loadOrder.pos = pos; + + if (!unload_block(pos, loadOrder.block_data)) + continue; + + loadOrderVec.push_back(loadOrder); + } + + // + // Cleanup load_mapblocks if they are too far away + std::vector remove_load_mapblocks; + { + MutexAutoLock lock(m_mutex_remove_load_blocks); + remove_load_mapblocks = this->remove_load_mapblocks; + this->remove_load_mapblocks.clear(); + } + for (auto& p : remove_load_mapblocks) + load_mapblocks.erase(p); + + // + // Block meshes has not been built yet. + // + std::unordered_map> build_blocks; + std::set keepTextures; + + for (auto it : load_mapblocks) { + auto& r = it.second; + + auto block_mesh = r.mesh; + if (!block_mesh) + continue; + + auto& p = r.p; + if (p.X <= cache_view_min.X || p.X >= cache_view_max.X || + p.Z <= cache_view_min.Z || p.Z >= cache_view_max.Z) + continue; + + if (mapblocks_needs_to_reload.find(p) == mapblocks_needs_to_reload.end()) { + if (buffer_data.find(p) != buffer_data.end()) + continue; + } + + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + scene::IMesh* mesh = block_mesh->getMesh(layer); + assert(mesh); + + u32 c = mesh->getMeshBufferCount(); + for (u32 i = 0; i < c; i++) { + if (!block_mesh->canMeshBufferBeCached(layer, i)) { + continue; + } + + scene::IMeshBuffer* buf = mesh->getMeshBuffer(i); + + video::IMaterialRenderer* rnd = driver->getMaterialRenderer(buf->getMaterial().MaterialType); + bool transparent = (rnd && rnd->isTransparent()); + if (transparent) + continue; + + auto sMeshBufferData = SMeshBufferData(); + sMeshBufferData.meshBuffer = buf; + sMeshBufferData.texture = block_mesh->getBufferMainTexture(layer, i); + sMeshBufferData.layer = layer; + sMeshBufferData.pos = r.p; + build_blocks[p].push_back(sMeshBufferData); + } + } + } + + // + // Start building dynamic meshes + // + for (auto& it : build_blocks) { + auto& pos = it.first; + auto& loadBuffers = it.second; + + TextureBufListMaps::LoadOrder* loadOrder = nullptr; + loadOrderVec.push_back(TextureBufListMaps::LoadOrder()); + loadOrder = &loadOrderVec.back(); + loadOrder->pos = pos; + TextureBufListMaps::LoadBlockData& loadBlockData = loadOrder->block_data; + + // + // Unload here + unload_block(pos, loadBlockData); + + // + // Vertices & Indices + // + for (auto& sMeshBufferData : loadBuffers) { + video::ITexture* texture = sMeshBufferData.texture; + keepTextures.insert(texture); + + TextureBufListMaps::Data* data; + { + MutexAutoLock lock(m_mutex_cache_buffers); + data = cache_buffers.get(texture, sMeshBufferData.layer); + } + + if (!data->vertexCount && !data->indexCount) { + data->vertexCount = 100'000; + data->indexCount = 50'000; + + data->vertex_memory.setSize(data->vertexCount * sizeof(video::S3DVertex)); + data->index_memory.setSize(data->indexCount * sizeof(u32)); + + data->material = sMeshBufferData.meshBuffer->getMaterial(); + } + + // + // Allocate memory spots for buffers + // + auto meshBuffer = sMeshBufferData.meshBuffer; + auto vertexCount = meshBuffer->getVertexCount(); + auto indexCount = meshBuffer->getIndexCount(); + + if (indexCount == 0 || vertexCount == 0) { + continue; + } + + sMeshBufferData.vertexMemory = data->vertex_memory.allocate(vertexCount * sizeof(video::S3DVertex)); + sMeshBufferData.indexMemory = data->index_memory.allocate(indexCount * sizeof(u32)); + + if (!sMeshBufferData.vertexMemory.is_valid()) { + // + // Grow Vertex memory + // + size_t grow_to_size = data->vertex_memory.size / sizeof(video::S3DVertex) + 50'000; + for (auto& sMeshBufferData : loadBuffers) + grow_to_size += sMeshBufferData.meshBuffer->getVertexCount(); + + data->vertexCount = grow_to_size; + data->vertex_memory.setSize(data->vertexCount * sizeof(video::S3DVertex)); + + // + // Try to get offset again + if (!sMeshBufferData.vertexMemory.is_valid()) + sMeshBufferData.vertexMemory = data->vertex_memory.allocate(vertexCount * sizeof(video::S3DVertex)); + + if (!sMeshBufferData.vertexMemory.is_valid()) + continue; + } + + if (!sMeshBufferData.indexMemory.is_valid()) { + // + // Grow Index memory + // + size_t grow_to_size = data->index_memory.size / sizeof(u32) + 80'000; + for (auto& sMeshBufferData : loadBuffers) + grow_to_size += sMeshBufferData.meshBuffer->getIndexCount(); + + data->indexCount = grow_to_size; + data->index_memory.setSize(data->indexCount * sizeof(u32)); + + // + // Try to get offset again + if (!sMeshBufferData.indexMemory.is_valid()) + sMeshBufferData.indexMemory = data->index_memory.allocate(indexCount * sizeof(u32)); + + // + // Move vertex & index memory to GPU + // + if (!sMeshBufferData.indexMemory.is_valid()) + continue; + } + + // + // Move vertex & index memory to GPU + // + + // + // Vertices + auto vertices = (video::S3DVertex*)meshBuffer->getVertices(); + OpenGLSubData* vertexData = new OpenGLSubData(); + vertexData->size = vertexCount * sizeof(video::S3DVertex); + vertexData->data.set_data((u8*)vertices, vertexData->size); + vertexData->offset = sMeshBufferData.vertexMemory.chunkStart; + + auto indices = meshBuffer->getIndices(); + + // + // Indices + OpenGLSubData* indexData = new OpenGLSubData(); + + core::array indicesWithOffset; + indicesWithOffset.set_used(indexCount); + u32 indexValueOffset = sMeshBufferData.vertexMemory.chunkStart / sizeof(video::S3DVertex); + u32* indices_ptr = indicesWithOffset.pointer(); + for (u32 i = 0; i < indexCount; ++i) { + *indices_ptr = indices[i] + indexValueOffset; + indices_ptr++; + } + + indexData->size = indexCount * sizeof(u32); + indexData->data.set_data((u8*)indicesWithOffset.pointer(), indexData->size); + indexData->offset = sMeshBufferData.indexMemory.chunkStart; + + // + // Load Data + TextureBufListMaps::LoadData loadData; + loadData.glIndexSubData = indexData; + loadData.glVertexSubData = vertexData; + loadData.drawPrimitiveCount = (data->index_memory.used_mem / sizeof(u32)) / 3; + loadData.texture = texture; + loadData.layer = sMeshBufferData.layer; + + loadBlockData.data.push_back(loadData); + + buffer_data[sMeshBufferData.pos].push_back(sMeshBufferData); + } + } + + if (!loadOrderVec.empty()) { + MutexAutoLock lock(m_mutex_cache_buffers); + cache_buffers.loadData.insert(cache_buffers.loadData.end(), loadOrderVec.begin(), loadOrderVec.end()); + } + + // + // Keep textures + for (auto it : buffer_data) { + for (auto& buffer : it.second) { + keepTextures.insert(buffer.texture); + } + } + + // + // Check if should drop a texture + struct Drop { + u8 layer; + video::ITexture* texture; + }; + std::vector dropTextures; + for (u8 layer = 0; layer < MAX_TILE_LAYERS; layer++) { + auto& map = cache_buffers.maps[layer]; + for (auto& list : map) { + auto cache = list.second; + auto texture = list.first; + if (cache && keepTextures.find(texture) != keepTextures.end()) + continue; + + dropTextures.push_back({ layer, texture }); + } + } + + { + MutexAutoLock lock(m_mutex_cache_buffers); + for (auto& it : dropTextures) { + cache_buffers.drop(it.texture, it.layer); + } + } +} + + +MeshBufferHandler::MeshBufferHandler(MeshUpdateManager* meshUpdateManager, video::CNullDriver* driver) { + this->meshUpdateManager = meshUpdateManager; + m_worker = std::make_unique(meshUpdateManager, driver); +} + +void MeshBufferHandler::setView(v3s16 min, v3s16 max) { + m_worker->setView(min, max); +} + +std::vector MeshBufferHandler::getMeshUpdateResults() { + return std::move(m_worker->getMeshUpdateResults()); +} + +void MeshBufferHandler::removeBlocks(v3s16* positions, size_t num) { + m_worker->removeBlocks(positions, num); +} + +bool MeshBufferHandler::getCacheBuffers(TextureBufListMaps& maps) { + // This function grabs each texture but will be dropped again after maps get's cleared + return m_worker->getCacheBuffers(maps); +} + +void MeshBufferHandler::start() +{ + m_worker->start(); +} + +void MeshBufferHandler::stop() +{ + m_worker->stop(); +} + +void MeshBufferHandler::wait() +{ + m_worker->wait(); +} + +bool MeshBufferHandler::isRunning() +{ + return meshUpdateManager->isRunning() && m_worker->isRunning(); +} diff --git a/src/client/mesh_buffer_handler.h b/src/client/mesh_buffer_handler.h new file mode 100644 index 0000000000000..86ca3bd5904f1 --- /dev/null +++ b/src/client/mesh_buffer_handler.h @@ -0,0 +1,264 @@ +#pragma once + +#include "threading/mutex_auto_lock.h" +#include "util/thread.h" +#include "mapblock_mesh.h" +#include "mesh_generator_thread.h" +#include "memoryManager.h" +#include +#include "CNullDriver.h" + +struct OpenGLSubData { + irr::core::array data; + u32 size = 0; + u32 offset = 0; + + inline bool isEmpty() const { + return data.empty(); + } +}; + +//struct OpenGLSubDataArray { +// std::vector data; +// +// inline void push_back(OpenGLSubData* d) { +// data.push_back(d); +// } +// +// void optimize(v3s16 view_center); +//}; + +struct TextureBufListMaps +{ + struct TextureHash + { + size_t operator()(const video::ITexture* t) const noexcept + { + // Only hash first texture. Simple and fast. + //return std::hash{}(m.TextureLayers[0].Texture); + return (size_t)t; + } + }; + + struct LoadData { + u8 layer = 0; + video::ITexture* texture = nullptr; + + size_t drawPrimitiveCount = 0; + + OpenGLSubData* glVertexSubData = nullptr; + OpenGLSubData* glIndexSubData = nullptr; + }; + + struct LoadBlockData { + std::vector data; + }; + + struct LoadOrder { + v3s16 pos; + LoadBlockData block_data; + }; + std::vector loadData; + + struct Data + { + size_t vertexCount = 0; + size_t indexCount = 0; + video::SMaterial material; + + MemoryManager vertex_memory; + MemoryManager index_memory; + + Data() : + vertex_memory(sizeof(video::S3DVertex)), + index_memory(sizeof(u32) * 3) {} + }; + + using MaterialBufListMap = std::unordered_map< + video::ITexture*, + std::shared_ptr, + TextureHash>; + + std::array maps; + + ~TextureBufListMaps() { + } + + void clear() + { + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + auto& map = maps[layer]; + + for (auto it : map) { + it.first->drop(); + } + + map.clear(); + } + } + + void grabEachTexture() { + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + auto& map = maps[layer]; + for (auto it : map) + it.first->grab(); + } + } + + Data* getNoCreate(video::ITexture* texture, u8 layer) + { + assert(layer < MAX_TILE_LAYERS); + + // Append to the correct layer + auto& map = maps[layer]; + if (map.find(texture) == map.end()) + return nullptr; + + return map[texture].get(); + } + + Data* get(video::ITexture* texture, u8 layer) + { + assert(layer < MAX_TILE_LAYERS); + + // Append to the correct layer + auto& map = maps[layer]; + + if (map.find(texture) != map.end()) + return map[texture].get(); + + texture->grab(); + + auto* data = new Data(); + map[texture] = std::shared_ptr(data); + return data; + } + + void drop(video::ITexture* texture, u8 layer) + { + assert(layer < MAX_TILE_LAYERS); + + // Append to the correct layer + auto& map = maps[layer]; + if (map.find(texture) == map.end()) + return; + + for (auto& loadOrder : loadData) { + for (int i = 0; i < loadOrder.block_data.data.size(); i++) { + auto& data = loadOrder.block_data.data[i]; + if (data.texture != texture) + continue; + + loadOrder.block_data.data.erase(loadOrder.block_data.data.begin()+i); + i--; + } + } + + texture->drop(); + + map.erase(texture); + } +}; + +struct SMeshBufferData { + MemoryManager::MemoryInfo vertexMemory; + MemoryManager::MemoryInfo indexMemory; + + scene::IMeshBuffer* meshBuffer = nullptr; + video::ITexture* texture = nullptr; + u8 layer = 0; + v3s16 pos; +}; + +class MeshBufferWorkerThread : public Thread +{ +public: + MeshBufferWorkerThread(MeshUpdateManager* meshUpdateManager, video::CNullDriver* driver); + ~MeshBufferWorkerThread(); + void setView(v3s16 min, v3s16 max); + bool getCacheBuffers(TextureBufListMaps &map); + std::vector getMeshUpdateResults(); + void removeBlocks(v3s16* positions, size_t num); + + void eraseLoadOrder(size_t num) { + MutexAutoLock lock(m_mutex_cache_buffers); + cache_buffers.loadData.erase(cache_buffers.loadData.begin(), cache_buffers.loadData.begin() + num); + } + + bool unload_block(v3s16 pos, TextureBufListMaps::LoadBlockData& loadBlockData); + + void stop() + { + meshUpdateManager->stop(); + meshUpdateManager->wait(); + Thread::stop(); + } + + void* run() + { + using namespace std::chrono_literals; + + BEGIN_DEBUG_EXCEPTION_HANDLER + + while (!stopRequested()) { + doUpdate(); + std::this_thread::sleep_for(1ms); + } + + END_DEBUG_EXCEPTION_HANDLER + + return NULL; + } + +private: + void doUpdate(); + +private: + MeshUpdateManager* meshUpdateManager; + video::CNullDriver* driver; + + std::mutex m_mutex_mesh_update_results; + std::vector mesh_update_results; + std::vector mesh_update_result_queue; + + std::mutex m_mutex_cache_buffers; + TextureBufListMaps cache_buffers; + std::unordered_map> buffer_data; + + std::unordered_map load_mapblocks; + + std::mutex m_mutex_view_range; + v3s16 view_center; + v3s16 view_min; + v3s16 view_max; + + std::mutex m_mutex_remove_load_blocks; + std::vector remove_load_mapblocks; +}; + +class MeshBufferHandler { +public: + MeshBufferHandler(MeshUpdateManager* meshUpdateManager, video::CNullDriver *driver); + + void setView(v3s16 min, v3s16 max); + std::vector getMeshUpdateResults(); + void removeBlocks(v3s16* positions, size_t num); + void eraseLoadOrder(size_t num) { + if (!num) + return; + + m_worker->eraseLoadOrder(num); + } + + void start(); + void stop(); + void wait(); + + bool isRunning(); + + bool getCacheBuffers(TextureBufListMaps &map); + +private: + std::unique_ptr m_worker; + MeshUpdateManager* meshUpdateManager; +}; diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 32a01205dacbb..b26b8da64a0fa 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -229,11 +229,9 @@ void MeshUpdateWorkerThread::doUpdate() MapBlockMesh *mesh_new = new MapBlockMesh(m_client, q->data, *m_camera_offset); - - MeshUpdateResult r; r.p = q->p; - r.mesh = mesh_new; + r.mesh = std::shared_ptr(mesh_new); r.solid_sides = get_solid_sides(q->data); r.ack_list = std::move(q->ack_list); r.urgent = q->urgent; @@ -297,6 +295,16 @@ void MeshUpdateManager::putResult(const MeshUpdateResult &result) m_queue_out.push_back(result); } +void MeshUpdateManager::delayResult(const MeshUpdateResult& r) +{ + delay_results.push_back(r); +} + +void MeshUpdateManager::undelayAll() { + for (auto& result : delay_results) + putResult(result); +} + bool MeshUpdateManager::getNextResult(MeshUpdateResult &r) { if (!m_queue_out_urgent.empty()) { diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index 607bc66df2311..6dff81ff35372 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -95,7 +95,7 @@ class MeshUpdateQueue struct MeshUpdateResult { v3s16 p = v3s16(-1338, -1338, -1338); - MapBlockMesh *mesh = nullptr; + std::shared_ptr mesh; u8 solid_sides; std::vector ack_list; bool urgent = false; @@ -134,6 +134,8 @@ class MeshUpdateManager void updateBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent, bool update_neighbors = false); void putResult(const MeshUpdateResult &r); + void delayResult(const MeshUpdateResult &r); + void undelayAll(); bool getNextResult(MeshUpdateResult &r); @@ -153,5 +155,7 @@ class MeshUpdateManager MutexedQueue m_queue_out; MutexedQueue m_queue_out_urgent; + std::vector delay_results; + std::vector> m_workers; }; diff --git a/src/client/player_ai.cpp b/src/client/player_ai.cpp new file mode 100644 index 0000000000000..023bfde21af97 --- /dev/null +++ b/src/client/player_ai.cpp @@ -0,0 +1,14 @@ +#include "player_ai.h" +#include "client.h" + +PlayerAI::PlayerAI(LocalPlayer* local_player) { + this->local_player = local_player; +} + +void PlayerAI::step(float dtime) { + if (!local_player) + return; + + if (local_player->isDead()) + local_player->getClient()->sendRespawn(); +} diff --git a/src/client/player_ai.h b/src/client/player_ai.h new file mode 100644 index 0000000000000..d57c9264408a7 --- /dev/null +++ b/src/client/player_ai.h @@ -0,0 +1,13 @@ +#pragma once + +#include "localplayer.h" + +class PlayerAI { +public: + PlayerAI(LocalPlayer* local_player); + + void step(float dtime); + +private: + LocalPlayer* local_player; +}; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index d013ffab9f653..1b3ea9e4ae92a 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -53,10 +53,9 @@ void FpsControl::reset() void FpsControl::limit(IrrlichtDevice *device, f32 *dtime, bool assume_paused) { - /*const float fps_limit = (device->isWindowFocused() && !assume_paused) + const float fps_limit = (!device || device->isWindowFocused() && !assume_paused) ? g_settings->getFloat("fps_max") - : g_settings->getFloat("fps_max_unfocused");*/ - const float fps_limit = 1000; + : g_settings->getFloat("fps_max_unfocused"); const u64 frametime_min = 1000000.0f / std::max(fps_limit, 1.0f); u64 time = porting::getTimeUs(); diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index ca61d8467763f..062bf86e1c4bd 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -156,9 +156,11 @@ void ShadowRenderer::initialize() size_t ShadowRenderer::addDirectionalLight() { - m_light_list.emplace_back(m_shadow_map_texture_size, + m_light_list.emplace_back( + (u32)m_shadow_map_texture_size, v3f(0.f, 0.f, 0.f), - video::SColor(255, 255, 255, 255), m_shadow_map_max_distance); + video::SColor(255, 255, 255, 255), + m_shadow_map_max_distance); return m_light_list.size() - 1; } diff --git a/src/database/database-mysql.cpp b/src/database/database-mysql.cpp index 11773782ee2c3..2051ce2a18b03 100644 --- a/src/database/database-mysql.cpp +++ b/src/database/database-mysql.cpp @@ -152,11 +152,16 @@ std::map parse_connection_string(const std::string& co void Database_MySQL::connectToDatabase() { + if (initialized()) + return; + m_conn = mysql_init(NULL); + if (!m_conn) { throw std::runtime_error("Failed to initialize MySQL connection"); } + auto params = parse_connection_string(m_connect_string); auto database = params["database_prefix"] + m_type; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a9dd4bd6c34d2..4aa400297f716 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -59,7 +59,8 @@ void set_default_settings() settings->setDefault("screenshot_format", "png"); settings->setDefault("screenshot_quality", "0"); settings->setDefault("client_unload_unused_data_timeout", "600"); - settings->setDefault("client_mapblock_limit", "7500"); + //settings->setDefault("client_mapblock_limit", "7500"); + settings->setDefault("client_mapblock_limit", "4500"); settings->setDefault("enable_build_where_you_stand", "false"); settings->setDefault("curl_timeout", "20000"); settings->setDefault("curl_parallel_limit", "8"); @@ -421,7 +422,7 @@ void set_default_settings() settings->setDefault("chat_message_limit_trigger_kick", "50"); settings->setDefault("sqlite_synchronous", "2"); settings->setDefault("map_compression_level_disk", "-1"); - settings->setDefault("map_compression_level_net", "-1"); + settings->setDefault("map_compression_level_net", "3"); settings->setDefault("full_block_send_enable_min_time_from_building", "2.0"); settings->setDefault("dedicated_server_step", "0.09"); settings->setDefault("active_block_mgmt_interval", "2.0"); diff --git a/src/gameparams.h b/src/gameparams.h index b138f8771c93c..a70f39690992e 100644 --- a/src/gameparams.h +++ b/src/gameparams.h @@ -49,6 +49,7 @@ struct GameStartData : GameParams std::string name; std::string password; std::string address; + std::string playerai; bool local_server; ELoginRegister allow_login_or_register = ELoginRegister::Any; diff --git a/src/main.cpp b/src/main.cpp index e9738b8cc7e81..e8747acb708ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -154,7 +154,7 @@ int main(int argc, char *argv[]) print_help(allowed_options); return cmd_args_ok ? 0 : 1; } - if (cmd_args.getFlag("console")) + if (cmd_args.getFlag("console") || cmd_args.getFlag("norender")) porting::attachOrCreateConsole(); if (cmd_args.getFlag("version")) { @@ -166,6 +166,11 @@ int main(int argc, char *argv[]) // Debug handler BEGIN_DEBUG_EXCEPTION_HANDLER + if (cmd_args.getFlag("norender")) { + cmd_args.set("color", "auto"); + cmd_args.setBool("info", true); + } + if (!setup_log_params(cmd_args)) return 1; @@ -247,10 +252,17 @@ int main(int argc, char *argv[]) porting::attachOrCreateConsole(); game_params.is_dedicated_server = true; #else + const bool noRender = cmd_args.getFlag("norender"); + const bool isServer = cmd_args.getFlag("server"); - if (isServer) + if (isServer || noRender) porting::attachOrCreateConsole(); game_params.is_dedicated_server = isServer; + + if (cmd_args.exists("playerai")) + game_params.playerai = cmd_args.get("playerai"); + if (noRender && game_params.playerai.empty()) + game_params.playerai = "playerai"; #endif if (!game_configure(&game_params, cmd_args)) @@ -394,6 +406,10 @@ static void set_allowed_options(OptionList *allowed_options) _("Disable main menu")))); allowed_options->insert(std::make_pair("console", ValueSpec(VALUETYPE_FLAG, _("Starts with the console (Windows only)")))); + allowed_options->insert(std::make_pair("norender", ValueSpec(VALUETYPE_FLAG, + _("Starts with the console & creates no window for rendering")))); + allowed_options->insert(std::make_pair("playerai", ValueSpec(VALUETYPE_FLAG, + _("ai will control your character")))); #endif } diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 36210cad1421a..a08b48b6ad7b4 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -78,7 +78,7 @@ MapBlock::~MapBlock() { #ifndef SERVER { - delete mesh; + //delete mesh; mesh = nullptr; } #endif @@ -86,6 +86,34 @@ MapBlock::~MapBlock() delete[] data; } +void MapBlock::raiseModified(u32 mod, u32 reason) +{ + if (mod > m_modified) { + m_modified = mod; + m_modified_reason = reason; + if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD) + m_disk_timestamp = m_timestamp; + } + else if (mod == m_modified) { + m_modified_reason |= reason; + } + if (mod == MOD_STATE_WRITE_NEEDED) + contents.clear(); + + static const u32 EDIT_FLAGS = + MOD_REASON_REALLOCATE | + MOD_REASON_STATIC_DATA_ADDED | + MOD_REASON_STATIC_DATA_ADDED | + MOD_REASON_STATIC_DATA_REMOVED | + MOD_REASON_STATIC_DATA_CHANGED | + MOD_REASON_VMANIP | + MOD_REASON_SET_NODE; + + if (reason & EDIT_FLAGS) + m_modified_version++; +} + + bool MapBlock::onObjectsActivation() { // Ignore if no stored objects (to not set changed flag) diff --git a/src/mapblock.h b/src/mapblock.h index a32f9b312f148..8bffa5da7e6ef 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -103,19 +103,7 @@ class MapBlock //// //// Modification tracking methods //// - void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN) - { - if (mod > m_modified) { - m_modified = mod; - m_modified_reason = reason; - if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD) - m_disk_timestamp = m_timestamp; - } else if (mod == m_modified) { - m_modified_reason |= reason; - } - if (mod == MOD_STATE_WRITE_NEEDED) - contents.clear(); - } + void raiseModified(u32 mod, u32 reason = MOD_REASON_UNKNOWN); inline u32 getModified() { @@ -127,6 +115,16 @@ class MapBlock return m_modified_reason; } + inline u16 getModifiedVersion() + { + return m_modified_version; + } + + inline void setModifiedVersion(u16 ver) + { + m_modified_version = ver; + } + std::string getModifiedReasonString(); inline void resetModified() @@ -461,7 +459,7 @@ class MapBlock public: #ifndef SERVER // Only on client - MapBlockMesh *mesh = nullptr; + std::shared_ptr mesh; // marks the sides which are opaque: 00+Z-Z+Y-Y+X-X u8 solid_sides = 0; @@ -526,6 +524,7 @@ class MapBlock */ u16 m_modified = MOD_STATE_CLEAN; u32 m_modified_reason = 0; + u16 m_modified_version = 0; /* When block is removed from active blocks, this is set to gametime. diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 10ecdd34c7ff2..ed0283ae76989 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -302,8 +302,11 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt) v3s16 p; *pkt >> p; + u16 modified_version; + *pkt >> modified_version; - std::string datastring(pkt->getString(6), pkt->getSize() - 6); + const size_t offset = sizeof(v3s16) + sizeof(u16); + std::string datastring(pkt->getString(offset), pkt->getSize() - offset); std::istringstream istr(datastring, std::ios_base::binary); MapSector *sector; @@ -316,6 +319,9 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt) block = sector->getBlockNoCreateNoEx(p.Y); if (block) { + if (block->getModifiedVersion() >= modified_version) + return; + /* Update an existing block */ @@ -331,6 +337,8 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt) block->deSerializeNetworkSpecific(istr); } + block->setModifiedVersion(modified_version); + if (m_localdb) { ServerMap::saveBlock(block, m_localdb); } @@ -685,9 +693,10 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt) return; } - // Mesh update thread must be stopped while - // updating content definitions - sanity_check(!m_mesh_update_manager->isRunning()); + if (m_mesh_update_manager) + // Mesh update thread must be stopped while + // updating content definitions + sanity_check(!m_mesh_update_manager->isRunning()); for (u16 i = 0; i < num_files; i++) { std::string name, sha1_base64; @@ -744,7 +753,7 @@ void Client::handleCommand_Media(NetworkPacket* pkt) bool init_phase = m_media_downloader && m_media_downloader->isStarted(); - if (init_phase) { + if (init_phase && m_mesh_update_manager) { // Mesh update thread must be stopped while // updating content definitions sanity_check(!m_mesh_update_manager->isRunning()); @@ -782,9 +791,10 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt) infostream << "Client: Received node definitions: packet size: " << pkt->getSize() << std::endl; - // Mesh update thread must be stopped while - // updating content definitions - sanity_check(!m_mesh_update_manager->isRunning()); + if (m_mesh_update_manager) + // Mesh update thread must be stopped while + // updating content definitions + sanity_check(!m_mesh_update_manager->isRunning()); // Decompress node definitions std::istringstream tmp_is(pkt->readLongString(), std::ios::binary); @@ -801,9 +811,10 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt) infostream << "Client: Received item definitions: packet size: " << pkt->getSize() << std::endl; - // Mesh update thread must be stopped while - // updating content definitions - sanity_check(!m_mesh_update_manager->isRunning()); + if (m_mesh_update_manager) + // Mesh update thread must be stopped while + // updating content definitions + sanity_check(!m_mesh_update_manager->isRunning()); // Decompress item definitions std::istringstream tmp_is(pkt->readLongString(), std::ios::binary); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index add80b3b23364..1b07c2eba9ed3 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -226,15 +226,15 @@ with this program; if not, write to the Free Software Foundation, Inc., [scheduled bump for 5.9.0] */ -#define LATEST_PROTOCOL_VERSION 44 +#define LATEST_PROTOCOL_VERSION 45 #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range -#define SERVER_PROTOCOL_VERSION_MIN 37 +#define SERVER_PROTOCOL_VERSION_MIN 45 #define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Client's supported network protocol range -#define CLIENT_PROTOCOL_VERSION_MIN 37 +#define CLIENT_PROTOCOL_VERSION_MIN 45 #define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Constant that differentiates the protocol from random data and other protocols diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index f4b5a48593aa3..cad5969ea914b 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -430,37 +430,40 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) void Server::handleCommand_GotBlocks(NetworkPacket* pkt) { - if (pkt->getSize() < 1) - return; - - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - - u8 count; - *pkt >> count; - - if ((s16)pkt->getSize() < 1 + (int)count * 6) { - throw con::InvalidIncomingDataException - ("GOTBLOCKS length is too short"); - } - - ClientInterface::AutoLock lock(m_clients); - RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId()); - - for (u16 i = 0; i < count; i++) { - v3s16 p; - *pkt >> p; - client->GotBlock(p); - } + //if (pkt->getSize() < 1) + // return; + + ///* + // [0] u16 command + // [2] u8 count + // [3] v3s16 pos_0 + // [3+6] v3s16 pos_1 + // ... + //*/ + + //u8 count; + //*pkt >> count; + + //if ((s16)pkt->getSize() < 1 + (int)count * (6 + sizeof(u16))) { + // throw con::InvalidIncomingDataException + // ("GOTBLOCKS length is too short"); + //} + + //ClientInterface::AutoLock lock(m_clients); + //RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId()); + + //Map& map = m_env->getMap(); + + //for (u16 i = 0; i < count; i++) { + // v3s16 p; + // u16 modified_version; + // *pkt >> p; + // *pkt >> modified_version; + // client->GotBlock(p, modified_version); + //} } -void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, - NetworkPacket *pkt) +void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, NetworkPacket *pkt) { if (pkt->getRemainingBytes() < 12 + 12 + 4 + 4 + 4 + 1 + 1) return; @@ -1060,7 +1063,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Re-send block to revert change on client-side RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); - client->SetBlockNotSent(blockpos); + client->ResetNothingToSendTimer(); + //client->SetBlockNotSent(blockpos); } return; } @@ -1217,11 +1221,14 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) v3s16 blockpos = getNodeBlockPos(p_under); RemoteClient *client = getClient(peer_id); // Send unusual result (that is, node not being removed) - if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) - // Re-send block to revert change on client-side - client->SetBlockNotSent(blockpos); - else - client->ResendBlockIfOnWire(blockpos); + //if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) + // // Re-send block to revert change on client-side + // client->ResetNothingToSendTimer(); + // //client->SetBlockNotSent(blockpos); + //else + // client->ResendBlockIfOnWire(blockpos); + + client->SetBlockNotSent(blockpos); return; } // action == INTERACT_DIGGING_COMPLETED @@ -1273,7 +1280,12 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface); v3s16 blockpos2 = getNodeBlockPos(pointed.node_undersurface); - if (had_prediction) { + client->SetBlockNotSent(blockpos); + if (blockpos2 != blockpos) + client->SetBlockNotSent(blockpos2); + + /*if (had_prediction) { + client->ResetNothingToSendTimer(); client->SetBlockNotSent(blockpos); if (blockpos2 != blockpos) client->SetBlockNotSent(blockpos2); @@ -1283,6 +1295,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) client->ResendBlockIfOnWire(blockpos2); } + client->ResetNothingToSendTimer();*/ + return; } // action == INTERACT_PLACE diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 02f69b3e17fb5..a9c039f765a82 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -47,6 +47,16 @@ struct EnumString es_TileAnimationType[] = {0, nullptr}, }; +void luaL_checktype_withname(lua_State* L, int narg, int tag, char* name) { + //luaL_checktype(L, narg, tag); + if (lua_type(L, narg) != tag) { + const char* tname = lua_typename(L, tag); + const char* msg = lua_pushfstring(L, "%s expected, got %s, in %s", + tname, luaL_typename(L, narg), name); + luaL_argerror(L, narg, msg); + } +} + /******************************************************************************/ void read_item_definition(lua_State* L, int index, const ItemDefinition &default_def, ItemDefinition &def) @@ -102,7 +112,7 @@ void read_item_definition(lua_State* L, int index, } else if (lua_isstring(L, -1)) { video::SColor color; read_color(L, -1, &color); - def.wear_bar_params = WearBarParams({{0.0, color}}, + def.wear_bar_params = WearBarParams({{0.0f, color}}, WearBarParams::BLEND_MODE_CONSTANT); } @@ -119,7 +129,8 @@ void read_item_definition(lua_State* L, int index, lua_getfield(L, index, "sounds"); if (!lua_isnil(L, -1)) { - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "sounds"); + //luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "place"); read_simplesoundspec(L, -1, def.sound_place); lua_pop(L, 1); @@ -132,7 +143,8 @@ void read_item_definition(lua_State* L, int index, // No, this is not a mistake. Item sounds are in "sound", node sounds in "sounds". lua_getfield(L, index, "sound"); if (!lua_isnil(L, -1)) { - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "sound"); + //luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "punch_use"); read_simplesoundspec(L, -1, def.sound_use); lua_pop(L, 1); @@ -158,7 +170,8 @@ void read_item_definition(lua_State* L, int index, lua_getfield(L, index, "touch_interaction"); if (!lua_isnil(L, -1)) { - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "touch_interaction"); + //luaL_checktype(L, -1, LUA_TTABLE); TouchInteraction &inter = def.touch_interaction; inter.pointed_nothing = (TouchInteractionMode)getenumfield(L, -1, "pointed_nothing", @@ -296,7 +309,8 @@ void read_object_properties(lua_State *L, int index, if (lua_isnil(L, index)) return; - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "object_properties"); + //luaL_checktype(L, -1, LUA_TTABLE); int hp_max = 0; if (getintfield(L, -1, "hp_max", hp_max)) { @@ -1239,7 +1253,8 @@ NodeBox read_nodebox(lua_State *L, int index) if (lua_isnil(L, -1)) return nodebox; - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "read_nodebox"); + //luaL_checktype(L, -1, LUA_TTABLE); nodebox.type = (NodeBoxType)getenumfield(L, index, "type", ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); @@ -1885,7 +1900,8 @@ void read_groups(lua_State *L, int index, ItemGroupList &result) if (lua_isnil(L, index)) return; - luaL_checktype(L, index, LUA_TTABLE); + luaL_checktype_withname(L, index, LUA_TTABLE, "groups"); + //luaL_checktype(L, index, LUA_TTABLE); result.clear(); lua_pushnil(L); @@ -1930,7 +1946,8 @@ std::vector read_items(lua_State *L, int index, IGameDef *gdef) index = lua_gettop(L) + 1 + index; std::vector items; - luaL_checktype(L, index, LUA_TTABLE); + luaL_checktype_withname(L, index, LUA_TTABLE, "items"); + //luaL_checktype(L, index, LUA_TTABLE); lua_pushnil(L); while (lua_next(L, index)) { s32 key = luaL_checkinteger(L, -2); @@ -1952,7 +1969,8 @@ void luaentity_get(lua_State *L, u16 id) // Get luaentities[i] lua_getglobal(L, "core"); lua_getfield(L, -1, "luaentities"); - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "luaentities"); + //luaL_checktype(L, -1, LUA_TTABLE); lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // Remove luaentities @@ -2229,7 +2247,8 @@ void push_objectRef(lua_State *L, const u16 id) // Get core.object_refs[i] lua_getglobal(L, "core"); lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); + luaL_checktype_withname(L, -1, LUA_TTABLE, "object_refs"); + //luaL_checktype(L, -1, LUA_TTABLE); lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // object_refs diff --git a/src/server.cpp b/src/server.cpp index 0bb3705194e38..bc35f418935be 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -973,7 +973,8 @@ void Server::AsyncRunStep(float dtime, bool initial_step) } case MEET_OTHER: prof.add("MEET_OTHER", 1); - m_clients.markBlocksNotSent(event->modified_blocks); + m_clients.ResetNothingToSendTimer(); + //m_clients.markBlocksNotSent(event->modified_blocks); break; default: prof.add("unknown", 1); @@ -986,8 +987,9 @@ void Server::AsyncRunStep(float dtime, bool initial_step) Set blocks not sent to far players */ for (const u16 far_player : far_players) { - if (RemoteClient *client = getClient(far_player)) - client->SetBlocksNotSent(event->modified_blocks); + if (RemoteClient* client = getClient(far_player)) + client->ResetNothingToSendTimer(); + //client->SetBlocksNotSent(event->modified_blocks); } delete event; @@ -2360,7 +2362,8 @@ void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos, if (far_players) far_players->emplace(client_id); else - client->SetBlockNotSent(block_pos); + client->ResetNothingToSendTimer(); + //client->SetBlockNotSent(block_pos); continue; } @@ -2395,7 +2398,8 @@ void Server::sendMetadataChanged(const std::unordered_set &positions, flo v3s16 block_pos = getNodeBlockPos(pos); if (!client->isBlockSent(block_pos) || player_pos.getDistanceFrom(pos) > far_d_nodes) { - client->SetBlockNotSent(block_pos); + client->ResetNothingToSendTimer(); + //client->SetBlockNotSent(block_pos); continue; } @@ -2423,17 +2427,21 @@ void Server::sendMetadataChanged(const std::unordered_set &positions, flo void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, u16 net_proto_version, SerializedBlockCache *cache) { - thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9); std::string s, *sptr = nullptr; if (cache) { auto it = cache->find({block->getPos(), ver}); - if (it != cache->end()) - sptr = &it->second; + if (it != cache->end()) { + auto& data = it->second; + if (data.last_serialized_version == block->getModifiedVersion()) + sptr = &data.str; + } } // Serialize the block in the right format if (!sptr) { + thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9); + std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false, net_compression_level); block->serializeNetworkSpecific(os); @@ -2441,14 +2449,19 @@ void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, sptr = &s; } - NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id); + NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sizeof(u16) + sptr->size(), peer_id); pkt << block->getPos(); + pkt << block->getModifiedVersion(); pkt.putRawString(*sptr); Send(&pkt); // Store away in cache - if (cache && sptr == &s) - (*cache)[{block->getPos(), ver}] = std::move(s); + if (cache && sptr == &s) { + SerializedBlockCacheData data; + data.str = std::move(s); + data.last_serialized_version = block->getModifiedVersion(); + (*cache)[{block->getPos(), ver}] = std::move(data); + } } void Server::SendBlocks(float dtime) @@ -2458,7 +2471,8 @@ void Server::SendBlocks(float dtime) std::vector queue; - u32 total_sending = 0, unique_clients = 0; + //u32 total_sending = 0, + u32 unique_clients = 0; { ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list"); @@ -2472,7 +2486,7 @@ void Server::SendBlocks(float dtime) if (!client) continue; - total_sending += client->getSendingCount(); + //total_sending += client->getSendingCount(); const auto old_count = queue.size(); client->GetNextBlocks(m_env,m_emerge, dtime, queue); unique_clients += queue.size() > old_count ? 1 : 0; @@ -2494,14 +2508,16 @@ void Server::SendBlocks(float dtime) ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients"); Map &map = m_env->getMap(); - SerializedBlockCache cache, *cache_ptr = nullptr; - if (unique_clients > 1) { - // caching is pointless with a single client - cache_ptr = &cache; - } + //SerializedBlockCache cache, *cache_ptr = nullptr; + //if (unique_clients > 1) { + // // caching is pointless with a single client + // cache_ptr = &cache; + //} + SerializedBlockCache* cache_ptr = &cache; + u32 sentBlocks = 0; for (const PrioritySortedBlockTransfer &block_to_send : queue) { - if (total_sending >= max_blocks_to_send) + if (sentBlocks >= max_blocks_to_send) break; MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos); @@ -2516,8 +2532,8 @@ void Server::SendBlocks(float dtime) SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version, client->net_proto_version, cache_ptr); - client->SentBlock(block_to_send.pos); - total_sending++; + client->SentBlock(block_to_send.pos, block->getModifiedVersion()); + sentBlocks++; } } diff --git a/src/server.h b/src/server.h index 9f9f08ac7dc14..c7485b999fe36 100644 --- a/src/server.h +++ b/src/server.h @@ -464,8 +464,11 @@ class Server : public con::PeerHandler, public MapEventReceiver, return std::hash()(p.first) ^ p.second; } }; - - typedef std::unordered_map, std::string, SBCHash> SerializedBlockCache; + struct SerializedBlockCacheData { + std::string str; + u64 last_serialized_version = 0; + }; + typedef std::unordered_map, SerializedBlockCacheData, SBCHash> SerializedBlockCache; void init(); @@ -753,6 +756,9 @@ class Server : public con::PeerHandler, public MapEventReceiver, MetricCounterPtr m_packet_recv_counter; MetricCounterPtr m_packet_recv_processed_counter; MetricCounterPtr m_map_edit_event_counter; + + // Cache serialized block data. + SerializedBlockCache cache; }; /* diff --git a/src/server/clientiface.cpp b/src/server/clientiface.cpp index 451e744074225..a1e0428606073 100644 --- a/src/server/clientiface.cpp +++ b/src/server/clientiface.cpp @@ -77,13 +77,13 @@ RemoteClient::RemoteClient() : { } -void RemoteClient::ResendBlockIfOnWire(v3s16 p) -{ - // if this block is on wire, mark it for sending again as soon as possible - if (m_blocks_sending.find(p) != m_blocks_sending.end()) { - SetBlockNotSent(p); - } -} +//void RemoteClient::ResendBlockIfOnWire(v3s16 p) +//{ +// // if this block is on wire, mark it for sending again as soon as possible +// /*if (m_blocks_sending.find(p) != m_blocks_sending.end()) { +// SetBlockNotSent(p); +// }*/ +//} LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env) { @@ -114,6 +114,7 @@ void RemoteClient::GetNextBlocks ( m_nothing_to_send_pause_timer -= dtime; m_map_send_completion_timer += dtime; + /* if (m_map_send_completion_timer > g_settings->getFloat("server_unload_unused_data_timeout") * 0.8f) { infostream << "Server: Player " << m_name << ", peer_id=" << peer_id << ": full map send is taking too long (" @@ -122,7 +123,7 @@ void RemoteClient::GetNextBlocks ( << std::endl; m_map_send_completion_timer = 0.0f; m_nearest_unsent_d = 0; - } + }*/ if (m_nothing_to_send_pause_timer >= 0) return; @@ -137,10 +138,10 @@ void RemoteClient::GetNextBlocks ( return; // Won't send anything if already sending - if (m_blocks_sending.size() >= m_max_simul_sends) { - //infostream<<"Not sending any blocks, Queue full."<= m_max_simul_sends) { + // //infostream<<"Not sending any blocks, Queue full."<getBasePosition(); // if the player is attached, get the velocity from the attached object @@ -178,10 +179,8 @@ void RemoteClient::GetNextBlocks ( = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; } - /* - Number of blocks sending + number of blocks selected for sending - */ - u32 num_blocks_selected = m_blocks_sending.size(); + + //u32 num_blocks_selected = m_blocks_sending.size(); /* next time d will be continued from the d from which the nearest @@ -204,27 +203,41 @@ void RemoteClient::GetNextBlocks ( /* Get the starting value of the block finder radius. */ - if (m_last_center != center) { + /*if (m_last_center != center) { m_nearest_unsent_d = 0; m_last_center = center; m_map_send_completion_timer = 0.0f; + }*/ + if (m_last_center != center) { + m_blocks_occ.clear(); + m_last_center = center; + m_map_send_completion_timer = 0.0f; } // reset the unsent distance if the view angle has changed more that 10% of the fov // (this matches isBlockInSight which allows for an extra 10%) if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) { - m_nearest_unsent_d = 0; + m_blocks_occ.clear(); m_last_camera_dir = camera_dir; m_map_send_completion_timer = 0.0f; } - if (m_nearest_unsent_d > 0) { - // make sure any blocks modified since the last time we sent blocks are resent - for (const v3s16 &p : m_blocks_modified) { - m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p)); - } - } - m_blocks_modified.clear(); - s16 d_start = m_nearest_unsent_d; + /*if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) { + m_nearest_unsent_d = 0; + m_last_camera_dir = camera_dir; + m_map_send_completion_timer = 0.0f; + }*/ + //if (m_nearest_unsent_d > 0) { + // // make sure any blocks modified since the last time we sent blocks are resent + // for (const v3s16 &p : m_blocks_modified) { + // m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p)); + // } + //} + //m_blocks_modified.clear(); + + //s16 d_start = m_nearest_unsent_d; + s16 d_start = 0; + const s16 max_d_increment_at_time = 2; + s16 d_max = d_start + max_d_increment_at_time; // Distrust client-sent FOV and get server-set player object property // zoom FOV (degrees) as a check to avoid hacked clients using FOV to load @@ -236,8 +249,8 @@ void RemoteClient::GetNextBlocks ( const s16 full_d_max = std::min(adjustDist(m_max_send_distance, prop_zoom_fov), wanted_range); - const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov), - wanted_range); + /*const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov), + wanted_range);*/ const s16 d_cull_opt = std::min(adjustDist(m_block_cull_optimize_distance, prop_zoom_fov), wanted_range); // f32 to prevent overflow, it is also what isBlockInSight(...) expects @@ -246,12 +259,12 @@ void RemoteClient::GetNextBlocks ( s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov), wanted_range); - s16 d_max = full_d_max; + //s16 d_max = full_d_max; - // Don't loop very much at a time - s16 max_d_increment_at_time = 2; - if (d_max > d_start + max_d_increment_at_time) - d_max = d_start + max_d_increment_at_time; + //// Don't loop very much at a time + //s16 max_d_increment_at_time = 2; + //if (d_max > d_start + max_d_increment_at_time) + // d_max = d_start + max_d_increment_at_time; // cos(angle between velocity and camera) * |velocity| // Limit to 0.0f in case player moves backwards. @@ -268,6 +281,12 @@ void RemoteClient::GetNextBlocks ( const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS); + /* + Number of blocks sending + number of blocks selected for sending + */ + u32 num_blocks_selected = 0; + + //bool didSendAny = false; s16 d; for (d = d_start; d <= d_max; d++) { /* @@ -276,6 +295,8 @@ void RemoteClient::GetNextBlocks ( */ const auto &list = FacePositionCache::getFacePositions(d); + bool hasUnsent = false; + for (auto li = list.begin(); li != list.end(); ++li) { v3s16 p = *li + center; @@ -300,6 +321,31 @@ void RemoteClient::GetNextBlocks ( if (blockpos_over_max_limit(p)) continue; + /* + Check if map has this block + */ + MapBlock* block = env->getMap().getBlockNoCreateNoEx(p); + if (block) { + // First: Reset usage timer, this block will be of use in the future. + block->resetUsageTimer(); + } + + if (block) { + u16 modified_version = block->getModifiedVersion(); + /* + Don't send already sent blocks + */ + auto it_sent = m_blocks_sent.find(p); + if (it_sent != m_blocks_sent.end() && it_sent->second.find(modified_version) != it_sent->second.end()) + continue; + } + + /* + Check occlusion cache first. + */ + if (m_blocks_occ.find(p) != m_blocks_occ.end()) + continue; + // If this is true, inexistent block will be made from scratch bool generate = d <= d_max_gen; @@ -320,14 +366,6 @@ void RemoteClient::GetNextBlocks ( continue; } - /* - Check if map has this block - */ - MapBlock *block = env->getMap().getBlockNoCreateNoEx(p); - if (block) { - // First: Reset usage timer, this block will be of use in the future. - block->resetUsageTimer(); - } // Don't select too many blocks for sending if (num_blocks_selected >= max_simul_dynamic) { @@ -335,17 +373,11 @@ void RemoteClient::GetNextBlocks ( goto queue_full_break; } - // Don't send blocks that are currently being transferred - if (m_blocks_sending.find(p) != m_blocks_sending.end()) - continue; - - /* - Don't send already sent blocks - */ - if (m_blocks_sent.find(p) != m_blocks_sent.end()) - continue; - if (block) { + // Don't send blocks that are currently being transferred + /* auto it_sending = m_blocks_sending.find(p); + if (it_sending != m_blocks_sending.end() && it_sending->second.find(modified_version) != it_sending->second.end()) + continue;*/ /* If block is not generated and generating new ones is not wanted, skip block. @@ -357,24 +389,22 @@ void RemoteClient::GetNextBlocks ( If block is not close, don't send it if it consists of air only. */ - if (d >= d_opt && block->isAir()) - continue; + /*if (d >= d_opt && block->isAir()) + continue;*/ } - /* - Check occlusion cache first. - */ - if (m_blocks_occ.find(p) != m_blocks_occ.end()) - continue; + /* Note that we do this even before the block is loaded as this does not depend on its contents. */ if (m_occ_cull && - env->getMap().isBlockOccluded(p * MAP_BLOCKSIZE, cam_pos_nodes, d >= d_cull_opt)) { + env->getMap().isBlockOccluded(p * MAP_BLOCKSIZE, cam_pos_nodes, false/*d >= d_cull_opt*/)) { m_blocks_occ.insert(p); continue; } + hasUnsent = true; + /* Add inexistent block to emerge queue. */ @@ -403,6 +433,12 @@ void RemoteClient::GetNextBlocks ( dest.push_back(q); num_blocks_selected += 1; + + //didSendAny = true; + } + + if (!hasUnsent && d_max < full_d_max) { + d_max++; } } queue_full_break: @@ -429,55 +465,60 @@ void RemoteClient::GetNextBlocks ( } } - if (new_nearest_unsent_d != -1 && m_nearest_unsent_d != new_nearest_unsent_d) { - m_nearest_unsent_d = new_nearest_unsent_d; - // if the distance has changed, clear the occlusion cache - m_blocks_occ.clear(); - } + //if (new_nearest_unsent_d != -1 && m_nearest_unsent_d != new_nearest_unsent_d) { + // m_nearest_unsent_d = new_nearest_unsent_d; + // // if the distance has changed, clear the occlusion cache + // m_blocks_occ.clear(); + //} + + //if (!didSendAny) { + // //m_nearest_unsent_d = new_nearest_unsent_d; + // // if the distance has changed, clear the occlusion cache + // m_blocks_occ.clear(); + //} } -void RemoteClient::GotBlock(v3s16 p) +void RemoteClient::GotBlock(v3s16 p, u16 modified_version) { - if (m_blocks_sending.find(p) != m_blocks_sending.end()) { - m_blocks_sending.erase(p); - // only add to sent blocks if it actually was sending - // (it might have been modified since) - m_blocks_sent.insert(p); - } else { - m_excess_gotblocks++; - } + /*m_blocks_sending[p].erase(modified_version); + m_blocks_sent[p].insert(modified_version);*/ } -void RemoteClient::SentBlock(v3s16 p) +void RemoteClient::SentBlock(v3s16 p, u16 modified_version) { - if (m_blocks_sending.find(p) == m_blocks_sending.end()) - m_blocks_sending[p] = 0.0f; + //m_blocks_sending[p].insert(modified_version); + m_blocks_sent[p].insert(modified_version); + + /*if (m_blocks_sending.find({ p, modified_version }) == m_blocks_sending.end()) + m_blocks_sending[{ p, modified_version }] = 0.0f; else infostream<<"RemoteClient::SentBlock(): Sent block" - " already in m_blocks_sending"< 0) - m_blocks_modified.insert(p); -} - -void RemoteClient::SetBlocksNotSent(const std::vector &blocks) -{ - m_nothing_to_send_pause_timer = 0; - - for (v3s16 p : blocks) { - // remove the block from sending and sent sets, - // and mark as modified if found - if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0) - m_blocks_modified.insert(p); - } -} + /*if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0) + m_blocks_modified.insert(p);*/ +} + +//void RemoteClient::SetBlocksNotSent(const std::vector &blocks) +//{ +// m_nothing_to_send_pause_timer = 0; +// +// for (v3s16 p : blocks) { +// // remove the block from sending and sent sets, +// // and mark as modified if found +// if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0) +// m_blocks_modified.insert(p); +// } +//} void RemoteClient::notifyEvent(ClientStateEvent event) { @@ -693,15 +734,21 @@ std::vector ClientInterface::getClientIDs(ClientState min_state) return reply; } -void ClientInterface::markBlocksNotSent(const std::vector &positions) -{ - RecursiveMutexAutoLock clientslock(m_clients_mutex); - for (const auto &client : m_clients) { - if (client.second->getState() >= CS_Active) - client.second->SetBlocksNotSent(positions); +void ClientInterface::ResetNothingToSendTimer() { + for (const auto& client : m_clients) { + client.second->ResetNothingToSendTimer(); } } +//void ClientInterface::markBlocksNotSent(const std::vector &positions) +//{ +// RecursiveMutexAutoLock clientslock(m_clients_mutex); +// for (const auto &client : m_clients) { +// if (client.second->getState() >= CS_Active) +// client.second->SetBlocksNotSent(positions); +// } +//} + /** * Verify if user limit was reached. * User limit count all clients from HelloSent state (MT protocol user) to Active state diff --git a/src/server/clientiface.h b/src/server/clientiface.h index ac41b00caed31..fe1c0f21161af 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -262,12 +262,13 @@ class RemoteClient void GetNextBlocks(ServerEnvironment *env, EmergeManager* emerge, float dtime, std::vector &dest); - void GotBlock(v3s16 p); + void GotBlock(v3s16 p, u16 modified_version); - void SentBlock(v3s16 p); + void SentBlock(v3s16 p, u16 modified_version); + inline void ResetNothingToSendTimer() { m_nothing_to_send_pause_timer = 0; } void SetBlockNotSent(v3s16 p); - void SetBlocksNotSent(const std::vector &blocks); + //void SetBlocksNotSent(const std::vector &blocks); /** * tell client about this block being modified right now. @@ -275,13 +276,16 @@ class RemoteClient * while modification is processed by server * @param p position of modified block */ - void ResendBlockIfOnWire(v3s16 p); + //void ResendBlockIfOnWire(v3s16 p); - u32 getSendingCount() const { return m_blocks_sending.size(); } + //u32 getSendingCount() const { return m_blocks_sending.size(); } bool isBlockSent(v3s16 p) const { - return m_blocks_sent.find(p) != m_blocks_sent.end(); + auto it = m_blocks_sent.find(p); + if (it == m_blocks_sent.end()) + return false; + return !it->second.empty(); } bool markMediaSent(const std::string &name) { @@ -293,8 +297,8 @@ class RemoteClient { o<<"RemoteClient "<