diff options
92 files changed, 1390 insertions, 2011 deletions
diff --git a/.gitmodules b/.gitmodules index e167fe6d5..9849b32df 100644 --- a/.gitmodules +++ b/.gitmodules @@ -55,10 +55,6 @@ path = lib/tolua++ url = https://github.com/cuberite/toluapp.git ignore = dirty -[submodule "lib/zlib"] - path = lib/zlib - url = https://github.com/cuberite/zlib.git - ignore = dirty [submodule "lib/fmt"] path = lib/fmt url = https://github.com/fmtlib/fmt.git @@ -66,3 +62,6 @@ [submodule "Tools/BlockTypePaletteGenerator/lib/lunajson"] path = Tools/BlockTypePaletteGenerator/lib/lunajson url = https://github.com/grafi-tt/lunajson.git +[submodule "lib/libdeflate"] + path = lib/libdeflate + url = https://github.com/cuberite/libdeflate diff --git a/CMake/AddDependencies.cmake b/CMake/AddDependencies.cmake index f16e5a327..9f73df3c7 100644 --- a/CMake/AddDependencies.cmake +++ b/CMake/AddDependencies.cmake @@ -28,7 +28,7 @@ function(build_dependencies) # Enumerate all submodule libraries # SQLiteCpp needs to be included before sqlite so the lsqlite target is available: - set(DEPENDENCIES expat fmt jsoncpp libevent lua luaexpat mbedtls SQLiteCpp sqlite tolua++ zlib) + set(DEPENDENCIES expat fmt jsoncpp libdeflate libevent lua luaexpat mbedtls SQLiteCpp sqlite tolua++) foreach(DEPENDENCY ${DEPENDENCIES}) # Check that the libraries are present: if (NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/${DEPENDENCY}/CMakeLists.txt") @@ -62,13 +62,13 @@ function(link_dependencies TARGET) event_extra fmt::fmt jsoncpp_lib + libdeflate lsqlite lualib luaexpat mbedtls SQLiteCpp tolualib - zlib ) # Link process information library: diff --git a/CMake/GroupSources.cmake b/CMake/GroupSources.cmake index a6d2bcc0e..59bd8c5c0 100644 --- a/CMake/GroupSources.cmake +++ b/CMake/GroupSources.cmake @@ -9,6 +9,7 @@ function(group_sources) expat fmt jsoncpp_lib + libdeflate lualib luaexpat mbedcrypto @@ -18,7 +19,6 @@ function(group_sources) sqlite3 SQLiteCpp tolualib - zlib PROPERTIES FOLDER Libraries ) diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 663b2ec68..88e999bd3 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -1765,17 +1765,6 @@ end }, Notes = "Sets the locale that Cuberite keeps on record. Initially the locale is initialized in protocol handshake, this function allows plugins to override the stored value (but only server-side and only until the user disconnects).", }, - SetUsername = - { - Params = - { - { - Name = "Name", - Type = "string", - }, - }, - Notes = "Sets the username", - }, SetViewDistance = { Params = @@ -14459,7 +14448,7 @@ end dtMagma = { Notes = "Damage from contact with a magma block" - }, + }, dtDrown = { Notes = "Damage received by drowning in water / lava" diff --git a/Tools/MCADefrag/CMakeLists.txt b/Tools/MCADefrag/CMakeLists.txt index 6ad894390..86a16b98a 100644 --- a/Tools/MCADefrag/CMakeLists.txt +++ b/Tools/MCADefrag/CMakeLists.txt @@ -17,10 +17,6 @@ function(flatten_files arg1) endfunction() -# Include the libraries: - -add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib) - # Include the shared files: set(SHARED_SRC ../../src/StringCompression.cpp @@ -66,11 +62,9 @@ source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR}) # Include the main source files: set(SOURCES MCADefrag.cpp - Globals.cpp ) set(HEADERS MCADefrag.h - Globals.h ) source_group("" FILES ${SOURCES} ${HEADERS}) @@ -84,7 +78,7 @@ add_executable(MCADefrag ${SHARED_OSS_HDR} ) -target_link_libraries(MCADefrag zlib fmt::fmt Threads::Threads) +target_link_libraries(MCADefrag fmt::fmt libdeflate Threads::Threads) include(../../SetFlags.cmake) set_exe_flags(MCADefrag) diff --git a/Tools/MCADefrag/Globals.cpp b/Tools/MCADefrag/Globals.cpp deleted file mode 100644 index 13c6ae709..000000000 --- a/Tools/MCADefrag/Globals.cpp +++ /dev/null @@ -1,10 +0,0 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - diff --git a/Tools/MCADefrag/Globals.h b/Tools/MCADefrag/Globals.h deleted file mode 100644 index def1fbdb9..000000000 --- a/Tools/MCADefrag/Globals.h +++ /dev/null @@ -1,174 +0,0 @@ - -// Globals.h - -// This file gets included from every module in the project, so that global symbols may be introduced easily -// Also used for precompiled header generation in MSVC environments - - - - - -// Compiler-dependent stuff: -#if defined(_MSC_VER) - // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether - #pragma warning(disable:4481) - - // Disable some warnings that we don't care about: - #pragma warning(disable:4100) - -#elif defined(__GNUC__) - - // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? - #define abstract - -#else - - #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" - -#endif - - - - - -// Integral types with predefined sizes: -typedef long long Int64; -typedef int Int32; -typedef short Int16; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; - -typedef unsigned char Byte; - - - - - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for any class that shouldn't allow copying itself -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &); \ - void operator=(const TypeName &) - -// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc -#define UNUSED(X) (void)(X) - - - - -// OS-dependent stuff: -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include <Windows.h> - #include <winsock2.h> - #include <ws2tcpip.h> - - // Windows SDK defines min and max macros, messing up with our std::min and std::max usage - #undef min - #undef max - - // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant - #ifdef GetFreeSpace - #undef GetFreeSpace - #endif // GetFreeSpace - - #define SocketError WSAGetLastError() -#else - #include <sys/types.h> - #include <sys/stat.h> // for mkdir - #include <sys/time.h> - #include <sys/socket.h> - #include <netinet/in.h> - #include <arpa/inet.h> - #include <netdb.h> - #include <dirent.h> - #include <iostream> - #include <unistd.h> - - #include <cstring> - #include <pthread.h> - #include <semaphore.h> - #include <cerrno> - #include <fcntl.h> - - typedef int SOCKET; - enum - { - INVALID_SOCKET = -1, - }; - #define closesocket close - #define SocketError errno -#endif - - - - - -// CRT stuff: -#include <cassert> -#include <cstdio> -#include <cmath> -#include <cstdarg> -#include <ctime> - - - - - -// STL stuff: -#include <vector> -#include <list> -#include <deque> -#include <string> -#include <map> -#include <algorithm> -#include <memory> -#include <atomic> -#include <mutex> -#include <thread> -#include <condition_variable> - - - - -// Common headers (without macros): -#include "fmt.h" -#include "LoggerSimple.h" -#include "StringUtils.h" -#include "OSSupport/CriticalSection.h" -#include "OSSupport/Event.h" -#include "OSSupport/IsThread.h" -#include "OSSupport/File.h" - - - - - -// Common definitions: - -/** Evaluates to the number of elements in an array (compile-time!) */ -#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) - -/** Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)") */ -#define KiB * 1024 -#define MiB * 1024 * 1024 - -/** Faster than (int)floorf((float)x / (float)div) */ -#define FAST_FLOOR_DIV(x, div) ((x) < 0 ? (((int)x / div) - 1) : ((int)x / div)) - -// Own version of assert() that writes failed assertions to the log for review -#ifdef NDEBUG - #define ASSERT(x) ((void)0) -#else - #define ASSERT assert -#endif - -// Pretty much the same as ASSERT() but stays in Release builds -#define VERIFY(x) (!!(x) || (LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), exit(1), 0)) - - - - - diff --git a/Tools/MCADefrag/MCADefrag.cpp b/Tools/MCADefrag/MCADefrag.cpp index c7886da35..d11505d45 100644 --- a/Tools/MCADefrag/MCADefrag.cpp +++ b/Tools/MCADefrag/MCADefrag.cpp @@ -8,7 +8,6 @@ #include "Logger.h" #include "LoggerSimple.h" #include "LoggerListeners.h" -#include "zlib/zlib.h" @@ -129,7 +128,8 @@ AString cMCADefrag::GetNextFileName(void) cMCADefrag::cThread::cThread(cMCADefrag & a_Parent) : super("MCADefrag thread"), m_Parent(a_Parent), - m_IsChunkUncompressed(false) + m_IsChunkUncompressed(false), + m_Compressor(12) // Set the highest compression factor { } @@ -384,27 +384,33 @@ bool cMCADefrag::cThread::UncompressChunkGzip(void) bool cMCADefrag::cThread::UncompressChunkZlib(void) { - // Uncompress the data: - z_stream strm; - strm.zalloc = nullptr; - strm.zfree = nullptr; - strm.opaque = nullptr; - inflateInit(&strm); - strm.next_out = m_RawChunkData; - strm.avail_out = sizeof(m_RawChunkData); - strm.next_in = m_CompressedChunkData + 1; // The first byte is the compression method, skip it - strm.avail_in = static_cast<uInt>(m_CompressedChunkDataSize); - int res = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - if (res != Z_STREAM_END) + try { - LOGWARNING("Failed to uncompress chunk data: %s", strm.msg); + // Uncompress the data + + const auto ExtractedData = m_Extractor.ExtractZLib( + { + reinterpret_cast<const std::byte *>(m_CompressedChunkData + 1), // The first byte is the compression method, skip it + static_cast<size_t>(m_CompressedChunkDataSize - 1) + }); + const auto Extracted = ExtractedData.GetView(); + + if (Extracted.size() > MAX_RAW_CHUNK_DATA_SIZE) + { + LOGINFO("Too much data for the internal decompression buffer!"); + return false; + } + + std::copy(Extracted.begin(), Extracted.end(), reinterpret_cast<std::byte *>(m_RawChunkData)); + m_RawChunkDataSize = static_cast<int>(Extracted.size()); + + return true; + } + catch (const std::exception & Oops) + { + LOGWARNING("Failed to uncompress chunk data. %s", Oops.what()); return false; } - ASSERT(strm.total_out < static_cast<uLong>(std::numeric_limits<int>::max())); - m_RawChunkDataSize = static_cast<int>(strm.total_out); - - return true; } @@ -413,23 +419,33 @@ bool cMCADefrag::cThread::UncompressChunkZlib(void) bool cMCADefrag::cThread::CompressChunk(void) { - // Check that the compressed data can fit: - uLongf CompressedSize = compressBound(static_cast<uLong>(m_RawChunkDataSize)); - if (CompressedSize > sizeof(m_CompressedChunkData)) + try { - LOGINFO("Too much data for the internal compression buffer!"); - return false; - } + // Compress the data (using the highest compression factor, as set in the constructor) + + const auto CompressedData = m_Compressor.CompressZLib( + { + reinterpret_cast<const std::byte *>(m_RawChunkData), + static_cast<size_t>(m_RawChunkDataSize) + }); + const auto Compressed = CompressedData.GetView(); + + // Check that the compressed data can fit: + if (Compressed.size() > MAX_COMPRESSED_CHUNK_DATA_SIZE) + { + LOGINFO("Too much data for the internal compression buffer!"); + return false; + } + + m_CompressedChunkData[0] = COMPRESSION_ZLIB; + std::copy(Compressed.begin(), Compressed.end(), reinterpret_cast<std::byte *>(m_CompressedChunkData + 1)); + m_CompressedChunkDataSize = static_cast<int>(Compressed.size()) + 1; - // Compress the data using the highest compression factor: - int errorcode = compress2(m_CompressedChunkData + 1, &CompressedSize, m_RawChunkData, static_cast<uLong>(m_RawChunkDataSize), Z_BEST_COMPRESSION); - if (errorcode != Z_OK) + return true; + } + catch (const std::exception & Oops) { - LOGINFO("Recompression failed: %d", errorcode); + LOGWARNING("Recompression failed. %s", Oops.what()); return false; } - m_CompressedChunkData[0] = COMPRESSION_ZLIB; - ASSERT(CompressedSize < static_cast<uLong>(std::numeric_limits<int>::max())); - m_CompressedChunkDataSize = static_cast<int>(CompressedSize + 1); - return true; } diff --git a/Tools/MCADefrag/MCADefrag.h b/Tools/MCADefrag/MCADefrag.h index cddaef625..cc151d032 100644 --- a/Tools/MCADefrag/MCADefrag.h +++ b/Tools/MCADefrag/MCADefrag.h @@ -13,10 +13,18 @@ +#include "OSSupport/IsThread.h" +#include "StringCompression.h" + + + + + class cMCADefrag { public: + enum { MAX_COMPRESSED_CHUNK_DATA_SIZE = (1 MiB), @@ -33,6 +41,7 @@ public: void Run(void); protected: + /** A single thread processing MCA files from the queue */ class cThread : public cIsThread @@ -40,9 +49,11 @@ protected: typedef cIsThread super; public: + cThread(cMCADefrag & a_Parent); protected: + /** The compression methods, as specified by the MCA compression method byte. */ enum { @@ -75,6 +86,12 @@ protected: WriteChunk() tests this flag to decide whether to call Compress(). */ bool m_IsChunkUncompressed; + /** An instance of the compressor. */ + Compression::Compressor m_Compressor; + + /** An instance of the extractor. */ + Compression::Extractor m_Extractor; + /** Processes the specified file. */ void ProcessFile(const AString & a_FileName); diff --git a/Tools/NoiseSpeedTest/CMakeLists.txt b/Tools/NoiseSpeedTest/CMakeLists.txt index 9488fdca0..296828ab6 100644 --- a/Tools/NoiseSpeedTest/CMakeLists.txt +++ b/Tools/NoiseSpeedTest/CMakeLists.txt @@ -36,11 +36,9 @@ source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR}) # Include the main source files: set(SOURCES NoiseSpeedTest.cpp - Globals.cpp ) set(HEADERS NoiseSpeedTest.h - Globals.h SimplexNoise.h ) diff --git a/Tools/NoiseSpeedTest/Globals.cpp b/Tools/NoiseSpeedTest/Globals.cpp deleted file mode 100644 index 13c6ae709..000000000 --- a/Tools/NoiseSpeedTest/Globals.cpp +++ /dev/null @@ -1,10 +0,0 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - diff --git a/Tools/NoiseSpeedTest/Globals.h b/Tools/NoiseSpeedTest/Globals.h deleted file mode 100644 index 375573bb4..000000000 --- a/Tools/NoiseSpeedTest/Globals.h +++ /dev/null @@ -1,196 +0,0 @@ - -// Globals.h - -// This file gets included from every module in the project, so that global symbols may be introduced easily -// Also used for precompiled header generation in MSVC environments - - - - - -// Compiler-dependent stuff: -#if defined(_MSC_VER) - // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether - #pragma warning(disable:4481) - - // Disable some warnings that we don't care about: - #pragma warning(disable:4100) - -#elif defined(__GNUC__) - - // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? - #define abstract - -#else - - #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" - -#endif - - - - - -#ifndef TOLUA_TEMPLATE_BIND - #define TOLUA_TEMPLATE_BIND(x) -#endif - - - - - -// Integral types with predefined sizes: -typedef long long Int64; -typedef int Int32; -typedef short Int16; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; - -typedef unsigned char Byte; - - - - - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for any class that shouldn't allow copying itself -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &); \ - void operator=(const TypeName &) - -// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc -#define UNUSED(X) (void)(X) - - - - -// OS-dependent stuff: -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include <Windows.h> - #include <winsock2.h> - #include <ws2tcpip.h> - - // Windows SDK defines min and max macros, messing up with our std::min and std::max usage - #undef min - #undef max - - // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant - #ifdef GetFreeSpace - #undef GetFreeSpace - #endif // GetFreeSpace - - #define SocketError WSAGetLastError() -#else - #include <sys/types.h> - #include <sys/stat.h> // for mkdir - #include <sys/time.h> - #include <sys/socket.h> - #include <netinet/in.h> - #include <arpa/inet.h> - #include <netdb.h> - #include <dirent.h> - #include <iostream> - #include <unistd.h> - - #include <cstring> - #include <pthread.h> - #include <semaphore.h> - #include <cerrno> - #include <fcntl.h> - - typedef int SOCKET; - enum - { - INVALID_SOCKET = -1, - }; - #define closesocket close - #define SocketError errno -#endif - - - - - -// CRT stuff: -#include <cassert> -#include <cstdio> -#include <cmath> -#include <cstdarg> -#include <ctime> - - - - - -// STL stuff: -#include <vector> -#include <list> -#include <deque> -#include <string> -#include <map> -#include <algorithm> -#include <memory> -#include <atomic> -#include <mutex> -#include <thread> -#include <condition_variable> - - - - - -// Common headers (without macros): -#include "fmt.h" -#include "LoggerSimple.h" -#include "StringUtils.h" -#include "OSSupport/CriticalSection.h" -#include "OSSupport/Event.h" -#include "OSSupport/IsThread.h" -#include "OSSupport/File.h" - - - - - -// Common definitions: - -/** Evaluates to the number of elements in an array (compile-time!) */ -#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) - -/** Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)") */ -#define KiB * 1024 -#define MiB * 1024 * 1024 - -/** Faster than (int)floorf((float)x / (float)div) */ -#define FAST_FLOOR_DIV(x, div) ((x) < 0 ? (((int)x / div) - 1) : ((int)x / div)) - -// Own version of assert() that writes failed assertions to the log for review -#ifdef NDEBUG - #define ASSERT(x) ((void)0) -#else - #define ASSERT assert -#endif - -// Pretty much the same as ASSERT() but stays in Release builds -#define VERIFY(x) (!!(x) || (LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), exit(1), 0)) - - - - - -/** Clamps the value into the specified range. */ -template <typename T> -T Clamp(T a_Value, T a_Min, T a_Max) -{ - return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); -} - -template <typename T> -auto ToUnsigned(T a_Val) -{ - ASSERT(a_Val >= 0); - return static_cast<std::make_unsigned_t<T>>(a_Val); -} diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt index 90258f64f..48a095f19 100644 --- a/Tools/ProtoProxy/CMakeLists.txt +++ b/Tools/ProtoProxy/CMakeLists.txt @@ -15,8 +15,6 @@ function(flatten_files arg1) set(${arg1} "${res}" PARENT_SCOPE) endfunction() -add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib) - # Include the shared files: set(SHARED_SRC ../../src/ByteBuffer.cpp @@ -33,6 +31,7 @@ set(SHARED_SRC ) set(SHARED_HDR ../../src/ByteBuffer.h + ../../src/Globals.h ../../src/StringUtils.h ../../src/UUID.h ../../src/mbedTLS++/AesCfb128Decryptor.h @@ -72,13 +71,11 @@ source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR}) # Include the main source files: set(SOURCES Connection.cpp - Globals.cpp ProtoProxy.cpp Server.cpp ) set(HEADERS Connection.h - Globals.h Server.h ) source_group("" FILES ${SOURCES} ${HEADERS}) @@ -92,7 +89,7 @@ add_executable(ProtoProxy ${SHARED_OSS_HDR} ) -target_link_libraries(ProtoProxy zlib mbedtls fmt::fmt Threads::Threads) +target_link_libraries(ProtoProxy fmt::fmt libdeflate mbedtls Threads::Threads) include(../../SetFlags.cmake) set_exe_flags(ProtoProxy) diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index 4c72c7097..f9b732142 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -12,6 +12,8 @@ #ifdef _WIN32 #include <direct.h> // For _mkdir() +#else + #include <sys/stat.h> // for mkdir #endif @@ -59,25 +61,25 @@ #define COPY_TO_SERVER() \ do { \ - AString ToServer; \ + ContiguousByteBuffer ToServer; \ m_ClientBuffer.ReadAgain(ToServer); \ switch (m_ServerState) \ { \ case csUnencrypted: \ { \ - SERVERSEND(ToServer.data(), ToServer.size()); \ + SERVERSEND(ToServer); \ break; \ } \ case csEncryptedUnderstood: \ case csEncryptedUnknown: \ { \ - SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ + SERVERENCRYPTSEND(ToServer); \ break; \ } \ case csWaitingForEncryption: \ { \ Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \ - m_ServerEncryptionBuffer.append(ToServer.data(), ToServer.size()); \ + m_ServerEncryptionBuffer += ToServer; \ break; \ } \ } \ @@ -86,19 +88,19 @@ #define COPY_TO_CLIENT() \ do { \ - AString ToClient; \ + ContiguousByteBuffer ToClient; \ m_ServerBuffer.ReadAgain(ToClient); \ switch (m_ClientState) \ { \ case csUnencrypted: \ { \ - CLIENTSEND(ToClient.data(), ToClient.size()); \ + CLIENTSEND(ToClient); \ break; \ } \ case csEncryptedUnderstood: \ case csEncryptedUnknown: \ { \ - CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ + CLIENTENCRYPTSEND(ToClient); \ break; \ } \ case csWaitingForEncryption: \ @@ -114,7 +116,7 @@ do { \ if (!Proc) \ { \ - AString Leftover; \ + ContiguousByteBuffer Leftover; \ m_ClientBuffer.ReadAgain(Leftover); \ DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \ m_ClientBuffer.ResetRead(); \ @@ -374,15 +376,15 @@ bool cConnection::RelayFromServer(void) } case csEncryptedUnderstood: { - m_ServerDecryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res)); + m_ServerDecryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), reinterpret_cast<const Byte *>(Buffer), static_cast<size_t>(res)); DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res); return DecodeServersPackets(Buffer, res); } case csEncryptedUnknown: { - m_ServerDecryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res)); + m_ServerDecryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), reinterpret_cast<const Byte *>(Buffer), static_cast<size_t>(res)); DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res); - return CLIENTSEND(Buffer, static_cast<size_t>(res)); + return CLIENTSEND({ reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res) }); } } ASSERT(!"Unhandled server state while relaying from server"); @@ -419,8 +421,8 @@ bool cConnection::RelayFromClient(void) case csEncryptedUnknown: { DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the CLIENT", res); - m_ServerEncryptor.ProcessData(reinterpret_cast<Byte *>(Buffer), reinterpret_cast<Byte *>(Buffer), static_cast<size_t>(res)); - return SERVERSEND(Buffer, static_cast<size_t>(res)); + m_ServerEncryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res)); + return SERVERSEND({ reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res) }); } } ASSERT(!"Unhandled server state while relaying from client"); @@ -441,11 +443,11 @@ double cConnection::GetRelativeTime(void) -bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, const char * a_Peer) +bool cConnection::SendData(SOCKET a_Socket, const ContiguousByteBufferView a_Data, const char * a_Peer) { - DataLog(a_Data, a_Size, "Sending data to %s, %u bytes", a_Peer, static_cast<unsigned>(a_Size)); + DataLog(a_Data.data(), a_Data.size(), "Sending data to %s, %zu bytes", a_Peer, a_Data.size()); - int res = static_cast<int>(send(a_Socket, a_Data, a_Size, 0)); // Windows uses int for a_Size, Linux uses size_t; but Windows doesn't complain. Return type is int on Windows and ssize_t on Linux + int res = static_cast<int>(send(a_Socket, reinterpret_cast<const char *>(a_Data.data()), a_Data.size(), 0)); // Windows uses int for a_Size, Linux uses size_t; but Windows doesn't complain. Return type is int on Windows and ssize_t on Linux if (res <= 0) { Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, SocketError); @@ -460,32 +462,30 @@ bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer) { - AString All; + ContiguousByteBuffer All; a_Data.ReadAll(All); a_Data.CommitRead(); - return SendData(a_Socket, All.data(), All.size(), a_Peer); + return SendData(a_Socket, All, a_Peer); } -bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer) +bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, ContiguousByteBufferView a_Data, const char * a_Peer) { - DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer); - const Byte * Data = reinterpret_cast<const Byte *>(a_Data); - while (a_Size > 0) + DataLog(a_Data.data(), a_Data.size(), "Encrypting %zu bytes to %s", a_Data.size(), a_Peer); + while (a_Data.size() > 0) { - Byte Buffer[64 KiB]; - size_t NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size; - a_Encryptor.ProcessData(Buffer, Data, NumBytes); - bool res = SendData(a_Socket, reinterpret_cast<const char *>(Buffer), NumBytes, a_Peer); + std::byte Buffer[64 KiB]; + size_t NumBytes = (a_Data.size() > sizeof(Buffer)) ? sizeof(Buffer) : a_Data.size(); + a_Encryptor.ProcessData(Buffer, a_Data.data(), NumBytes); + bool res = SendData(a_Socket, { Buffer, NumBytes }, a_Peer); if (!res) { return false; } - Data += NumBytes; - a_Size -= NumBytes; + a_Data = a_Data.substr(NumBytes); } return true; } @@ -496,10 +496,10 @@ bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Enc bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) { - AString All; + ContiguousByteBuffer All; a_Data.ReadAll(All); a_Data.CommitRead(); - return SendEncryptedData(a_Socket, a_Encryptor, All.data(), All.size(), a_Peer); + return SendEncryptedData(a_Socket, a_Encryptor, All, a_Peer); } @@ -647,7 +647,7 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size) if (PacketLen == 0) { m_ServerBuffer.ResetRead(); - AString All; + ContiguousByteBuffer All; m_ServerBuffer.ReadAll(All); DataLog(All.data(), All.size(), "====== Received a bad packet length? Inspect the contents below ======"); m_ServerBuffer.CommitRead(); // Try to recover by marking everything as read @@ -798,10 +798,11 @@ bool cConnection::HandleClientHandshake(void) Packet.WriteVarUTF8String(ServerHost); Packet.WriteBEUInt16(m_Server.GetConnectPort()); Packet.WriteVarInt32(NextState); - AString Pkt; + ContiguousByteBuffer Pkt; Packet.ReadAll(Pkt); cByteBuffer ToServer(512); - ToServer.WriteVarUTF8String(Pkt); + ToServer.WriteVarInt32(static_cast<UInt32>(Pkt.size())); + ToServer.Write(Pkt.data(), Pkt.size()); SERVERSEND(ToServer); m_ClientProtocolState = static_cast<int>(NextState); @@ -1111,8 +1112,8 @@ bool cConnection::HandleClientPluginMessage(void) { HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ChannelName); HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, Length); - AString Data; - if (!m_ClientBuffer.ReadString(Data, Length)) + ContiguousByteBuffer Data; + if (!m_ClientBuffer.ReadSome(Data, Length)) { return false; } @@ -1253,8 +1254,8 @@ bool cConnection::HandleClientWindowClose(void) bool cConnection::HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar) { - AString Data; - if (!m_ClientBuffer.ReadString(Data, a_PacketLen - a_PacketReadSoFar)) + ContiguousByteBuffer Data; + if (!m_ClientBuffer.ReadSome(Data, a_PacketLen - a_PacketReadSoFar)) { return false; } @@ -1288,14 +1289,14 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void) // Read the packet from the server: HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PublicKeyLength); - AString PublicKey; - if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength)) + ContiguousByteBuffer PublicKey; + if (!m_ServerBuffer.ReadSome(PublicKey, PublicKeyLength)) { return false; } HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NonceLength); - AString Nonce; - if (!m_ServerBuffer.ReadString(Nonce, NonceLength)) + ContiguousByteBuffer Nonce; + if (!m_ServerBuffer.ReadSome(Nonce, NonceLength)) { return false; } @@ -1304,7 +1305,7 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void) DataLog(PublicKey.data(), PublicKey.size(), " Public key (%u bytes)", static_cast<unsigned>(PublicKey.size())); // Reply to the server: - SendEncryptionKeyResponse(PublicKey, Nonce); + SendEncryptionKeyResponse({ reinterpret_cast<const char *>(PublicKey.data()), PublicKey.size() }, { reinterpret_cast<const char *>(Nonce.data()), Nonce.size() }); // Do not send to client - we want the client connection open return true; @@ -1330,7 +1331,7 @@ bool cConnection::HandleServerLoginSuccess(void) Log("Server communication is now encrypted"); m_ServerState = csEncryptedUnderstood; DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size()); - SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size()); + SERVERENCRYPTSEND(m_ServerEncryptionBuffer); m_ServerEncryptionBuffer.clear(); } COPY_TO_CLIENT(); @@ -1827,8 +1828,8 @@ bool cConnection::HandleServerKick(void) AString PacketStart("\xff"); PacketStart.push_back(static_cast<char>(ReasonBE16.size() / 256)); PacketStart.push_back(static_cast<char>(ReasonBE16.size() % 256)); - CLIENTSEND(PacketStart.data(), PacketStart.size()); - CLIENTSEND(reinterpret_cast<const char *>(ReasonBE16.data()), ReasonBE16.size() * sizeof(char16_t)); + CLIENTSEND({ reinterpret_cast<const std::byte *>(PacketStart.data()), PacketStart.size() }); + CLIENTSEND({ reinterpret_cast<const std::byte *>(ReasonBE16.data()), ReasonBE16.size() * sizeof(char16_t) }); return true; } else @@ -1856,8 +1857,8 @@ bool cConnection::HandleServerMapChunk(void) HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PrimaryBitmap); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, AdditionalBitmap); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize); - AString CompressedData; - if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + ContiguousByteBuffer CompressedData; + if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize)) { return false; } @@ -1880,8 +1881,8 @@ bool cConnection::HandleServerMapChunkBulk(void) HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ChunkCount); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize); HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent); - AString CompressedData; - if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + ContiguousByteBuffer CompressedData; + if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize)) { return false; } @@ -1930,8 +1931,8 @@ bool cConnection::HandleServerMultiBlockChange(void) HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumBlocks); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataSize); - AString BlockChangeData; - if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize)) + ContiguousByteBuffer BlockChangeData; + if (!m_ServerBuffer.ReadSome(BlockChangeData, DataSize)) { return false; } @@ -2039,8 +2040,8 @@ bool cConnection::HandleServerPluginMessage(void) { HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Length); - AString Data; - if (!m_ServerBuffer.ReadString(Data, Length)) + ContiguousByteBuffer Data; + if (!m_ServerBuffer.ReadSome(Data, Length)) { return false; } @@ -2278,7 +2279,7 @@ bool cConnection::HandleServerSpawnObjectVehicle(void) #ifdef _DEBUG // DEBUG: // This packet is still troublesome when DataIndicator != 0 - AString Buffer; + ContiguousByteBuffer Buffer; m_ServerBuffer.ResetRead(); m_ServerBuffer.ReadAll(Buffer); m_ServerBuffer.ResetRead(); @@ -2328,7 +2329,7 @@ bool cConnection::HandleServerSpawnObjectVehicle(void) } // TODO: Splash potions } - if ((ExtraLen > 0) && !m_ServerBuffer.ReadString(ExtraData, ExtraLen)) + if ((ExtraLen > 0) && !m_ServerBuffer.ReadSome(ExtraData, ExtraLen)) { return false; } @@ -2449,10 +2450,11 @@ bool cConnection::HandleServerStatusResponse(void) cByteBuffer Packet(Response.size() + 50); Packet.WriteVarInt32(0); // Packet type - status response Packet.WriteVarUTF8String(Response); - AString Pkt; + ContiguousByteBuffer Pkt; Packet.ReadAll(Pkt); cByteBuffer ToClient(Response.size() + 50); - ToClient.WriteVarUTF8String(Pkt); + ToClient.WriteVarInt32(static_cast<UInt32>(Pkt.size())); + ToClient.Write(Pkt.data(), Pkt.size()); CLIENTSEND(ToClient); return true; } @@ -2545,8 +2547,8 @@ bool cConnection::HandleServerUpdateTileEntity(void) HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Action); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, DataLength); - AString Data; - if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength)) + ContiguousByteBuffer Data; + if ((DataLength > 0) && !m_ServerBuffer.ReadSome(Data, DataLength)) { return false; } @@ -2662,9 +2664,9 @@ bool cConnection::HandleServerWindowOpen(void) bool cConnection::HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar) { - AString Data; + ContiguousByteBuffer Data; ASSERT(a_PacketLen >= a_PacketReadSoFar); - if (!m_ServerBuffer.ReadString(Data, a_PacketLen - a_PacketReadSoFar)) + if (!m_ServerBuffer.ReadSome(Data, a_PacketLen - a_PacketReadSoFar)) { return false; } @@ -2764,9 +2766,9 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata) rs = rs - static_cast<int>(a_Buffer.GetReadableSpace()); cByteBuffer LenBuf(8); LenBuf.WriteVarInt32(Len); - AString VarLen; + ContiguousByteBuffer VarLen; LenBuf.ReadAll(VarLen); - a_Metadata.append(VarLen); + a_Metadata += { reinterpret_cast<const char *>(VarLen.data()), VarLen.size() }; Length = Len; break; } @@ -2794,12 +2796,12 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata) } // switch (Type) // Read the data in this item: - AString data; - if (!a_Buffer.ReadString(data, Length)) + ContiguousByteBuffer data; + if (!a_Buffer.ReadSome(data, Length)) { return false; } - a_Metadata.append(data); + a_Metadata += { reinterpret_cast<const char *>(data.data()), data.size() }; if (!a_Buffer.ReadBEUInt8(x)) { return false; diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h index 6c13efba2..b524561a1 100644 --- a/Tools/ProtoProxy/Connection.h +++ b/Tools/ProtoProxy/Connection.h @@ -13,6 +13,10 @@ #include "mbedTLS++/AesCfb128Decryptor.h" #include "mbedTLS++/AesCfb128Encryptor.h" +#ifndef _WIN32 + typedef int SOCKET; +#endif + @@ -85,7 +89,7 @@ protected: cAesCfb128Decryptor m_ServerDecryptor; cAesCfb128Encryptor m_ServerEncryptor; - AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established + ContiguousByteBuffer m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established /** Set to true when PACKET_PING is received from the client; will cause special parsing for server kick */ bool m_HasClientPinged; @@ -119,13 +123,13 @@ protected: double GetRelativeTime(void); /** Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. */ - bool SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, const char * a_Peer); + bool SendData(SOCKET a_Socket, ContiguousByteBufferView a_Data, const char * a_Peer); /** Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. */ bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer); /** Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false */ - bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer); + bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, ContiguousByteBufferView a_Data, const char * a_Peer); /** Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false */ bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); diff --git a/Tools/ProtoProxy/Globals.cpp b/Tools/ProtoProxy/Globals.cpp deleted file mode 100644 index 13c6ae709..000000000 --- a/Tools/ProtoProxy/Globals.cpp +++ /dev/null @@ -1,10 +0,0 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - diff --git a/Tools/ProtoProxy/Globals.h b/Tools/ProtoProxy/Globals.h deleted file mode 100644 index 71173d1db..000000000 --- a/Tools/ProtoProxy/Globals.h +++ /dev/null @@ -1,177 +0,0 @@ - -// Globals.h - -// This file gets included from every module in the project, so that global symbols may be introduced easily -// Also used for precompiled header generation in MSVC environments - - - - - -// Compiler-dependent stuff: -#if defined(_MSC_VER) - // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether - #pragma warning(disable:4481) - - // Disable some warnings that we don't care about: - #pragma warning(disable:4100) - -#elif defined(__GNUC__) - - // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? - #define abstract - -#else - - #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" - -#endif - - - - - -// Integral types with predefined sizes: -typedef signed long long Int64; -typedef signed int Int32; -typedef signed short Int16; -typedef signed char Int8; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; -typedef unsigned char UInt8; - -typedef unsigned char Byte; - - - - - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for any class that shouldn't allow copying itself -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &); \ - void operator=(const TypeName &) - -// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc -#define UNUSED(X) (void)(X) - - - - -// OS-dependent stuff: -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include <Windows.h> - #include <winsock2.h> - #include <ws2tcpip.h> - - // Windows SDK defines min and max macros, messing up with our std::min and std::max usage - #undef min - #undef max - - // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant - #ifdef GetFreeSpace - #undef GetFreeSpace - #endif // GetFreeSpace - - #define SocketError WSAGetLastError() -#else - #include <sys/types.h> - #include <sys/stat.h> // for mkdir - #include <sys/time.h> - #include <sys/socket.h> - #include <netinet/in.h> - #include <arpa/inet.h> - #include <netdb.h> - #include <time.h> - #include <dirent.h> - #include <errno.h> - #include <iostream> - #include <unistd.h> - - #include <cstdio> - #include <cstring> - #include <pthread.h> - #include <semaphore.h> - #include <errno.h> - #include <fcntl.h> - - typedef int SOCKET; - enum - { - INVALID_SOCKET = -1, - }; - #define closesocket close - #define SocketError errno -#endif - - - - - -// CRT stuff: -#include <assert.h> -#include <stdio.h> -#include <math.h> -#include <stdarg.h> -#include <time.h> - - - - - -// STL stuff: -#include <chrono> -#include <vector> -#include <list> -#include <deque> -#include <string> -#include <map> -#include <algorithm> -#include <memory> -#include <atomic> -#include <mutex> -#include <thread> -#include <condition_variable> - - - - - -// Common headers (part 1, without macros): -#include "fmt.h" -#include "StringUtils.h" -#include "OSSupport/CriticalSection.h" - -#include "LoggerSimple.h" - - - -// Common definitions: - -/** Evaluates to the number of elements in an array (compile-time!) */ -#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) - -/* Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)") */ -#define KiB * 1024 - -/* Faster than (int)floorf((float)x / (float)div) */ -#define FAST_FLOOR_DIV(x, div) ((x) < 0 ? (((int)x / div) - 1) : ((int)x / div)) - -// Own version of assert() that writes failed assertions to the log for review -#ifdef NDEBUG - #define ASSERT(x) ((void)0) -#else - #define ASSERT assert -#endif - -// Pretty much the same as ASSERT() but stays in Release builds -#define VERIFY(x) (!!(x) || (LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), exit(1), 0)) - - - - - - diff --git a/Tools/ProtoProxy/Server.h b/Tools/ProtoProxy/Server.h index c09a72b85..e8db7f1c7 100644 --- a/Tools/ProtoProxy/Server.h +++ b/Tools/ProtoProxy/Server.h @@ -9,8 +9,20 @@ #pragma once +#include "Globals.h" #include "mbedTLS++/RsaPrivateKey.h" +#ifdef _WIN32 + #define SocketError WSAGetLastError() +#else + typedef int SOCKET; + enum + { + INVALID_SOCKET = -1, + }; + #define closesocket close + #define SocketError errno +#endif @@ -20,7 +32,7 @@ class cServer { SOCKET m_ListenSocket; cRsaPrivateKey m_PrivateKey; - AString m_PublicKeyDER; + ContiguousByteBuffer m_PublicKeyDER; UInt16 m_ConnectPort; public: @@ -30,7 +42,7 @@ public: void Run(void); cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } - const AString & GetPublicKeyDER (void) { return m_PublicKeyDER; } + ContiguousByteBufferView GetPublicKeyDER (void) { return m_PublicKeyDER; } UInt16 GetConnectPort(void) const { return m_ConnectPort; } } ; diff --git a/lib/libdeflate b/lib/libdeflate new file mode 160000 +Subproject e0f46c3c9fbb4213ac7534b2bfe5cfc78e07c1f diff --git a/lib/zlib b/lib/zlib deleted file mode 160000 -Subproject a9c7b30641ed8b86590141a1b4025263bce4c88 diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index c6042ac62..81dcb0e67 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -863,7 +863,7 @@ void cLuaState::Push(const AStringVector & a_Vector) int index = 1; for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) { - tolua_pushstring(m_LuaState, itr->c_str()); + Push(*itr); lua_rawseti(m_LuaState, newTable, index); } } @@ -916,6 +916,17 @@ void cLuaState::Push(const cLuaState::cRef & a_Ref) +void cLuaState::Push(const ContiguousByteBufferView a_Data) +{ + ASSERT(IsValid()); + + lua_pushlstring(m_LuaState, reinterpret_cast<const char *>(a_Data.data()), a_Data.size()); +} + + + + + void cLuaState::Push(const Vector3d & a_Vector) { ASSERT(IsValid()); @@ -1355,6 +1366,22 @@ bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefSharedPtr & a_Ref) +bool cLuaState::GetStackValue(int a_StackPos, ContiguousByteBuffer & a_Data) +{ + size_t Length = 0; + const char * const Data = lua_tolstring(m_LuaState, a_StackPos, &Length); + if (Data != nullptr) + { + a_Data.assign(reinterpret_cast<const std::byte *>(Data), Length); + return true; + } + return false; +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) { if (lua_isnumber(m_LuaState, a_StackPos)) diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 0bdecdfc7..b3f567ecb 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -619,6 +619,7 @@ public: void Push(const cItem & a_Item); void Push(const cNil & a_Nil); void Push(const cRef & a_Ref); + void Push(ContiguousByteBufferView a_Data); void Push(const Vector3d & a_Vector); void Push(const Vector3i & a_Vector); @@ -658,6 +659,7 @@ public: bool GetStackValue(int a_StackPos, cTrackedRef & a_Ref); bool GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref); bool GetStackValue(int a_StackPos, cTrackedRefSharedPtr & a_Ref); + bool GetStackValue(int a_StackPos, ContiguousByteBuffer & a_Data); bool GetStackValue(int a_StackPos, double & a_Value); bool GetStackValue(int a_StackPos, eBlockFace & a_Value); bool GetStackValue(int a_StackPos, eWeather & a_Value); diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 92f7dd92b..20364100f 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -188,9 +188,7 @@ static int tolua_CompressStringZLIB(lua_State * tolua_S) S.GetStackValues(1, ToCompress, CompressionLevel); // Compress the string: - AString res; - CompressString(ToCompress.data(), ToCompress.size(), res, CompressionLevel); - S.Push(res); + S.Push(Compression::Compressor(CompressionLevel).CompressZLib(ToCompress.data(), ToCompress.size()).GetView()); return 1; } @@ -211,14 +209,21 @@ static int tolua_UncompressStringZLIB(lua_State * tolua_S) } // Get the params: - AString ToUncompress; + ContiguousByteBuffer ToUncompress; size_t UncompressedSize = 0; S.GetStackValues(1, ToUncompress, UncompressedSize); - // Compress the string: - AString res; - UncompressString(ToUncompress.data(), ToUncompress.size(), res, UncompressedSize); - S.Push(res); + try + { + // Decompress the string: + S.Push(Compression::Extractor().ExtractZLib(ToUncompress, UncompressedSize).GetView()); + } + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); + cLuaState::LogStackTrace(tolua_S); + return 0; + } return 1; } @@ -239,13 +244,11 @@ static int tolua_CompressStringGZIP(lua_State * tolua_S) } // Get the params: - AString ToCompress; + ContiguousByteBuffer ToCompress; S.GetStackValues(1, ToCompress); // Compress the string: - AString res; - CompressStringGZIP(ToCompress.data(), ToCompress.size(), res); - S.Push(res); + S.Push(Compression::Compressor().CompressGZip(ToCompress).GetView()); return 1; } @@ -253,7 +256,7 @@ static int tolua_CompressStringGZIP(lua_State * tolua_S) -static int tolua_UncompressStringGZIP(lua_State * tolua_S) +static int tolua_InflateString(lua_State * tolua_S) { cLuaState S(tolua_S); if ( @@ -266,40 +269,20 @@ static int tolua_UncompressStringGZIP(lua_State * tolua_S) } // Get the params: - AString ToUncompress; + ContiguousByteBuffer ToUncompress; S.GetStackValues(1, ToUncompress); - // Compress the string: - AString res; - UncompressStringGZIP(ToUncompress.data(), ToUncompress.size(), res); - S.Push(res); - return 1; -} - - - - - -static int tolua_InflateString(lua_State * tolua_S) -{ - cLuaState S(tolua_S); - if ( - !S.CheckParamString(1) || - !S.CheckParamEnd(2) - ) + try { + // Decompress the string: + S.Push(Compression::Extractor().ExtractZLib(ToUncompress).GetView()); + } + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); cLuaState::LogStackTrace(tolua_S); return 0; } - - // Get the params: - AString ToUncompress; - S.GetStackValues(1, ToUncompress); - - // Compress the string: - AString res; - InflateString(ToUncompress.data(), ToUncompress.size(), res); - S.Push(res); return 1; } @@ -4552,7 +4535,7 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB); tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB); tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP); - tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP); + tolua_function(tolua_S, "UncompressStringGZIP", tolua_InflateString); tolua_function(tolua_S, "InflateString", tolua_InflateString); tolua_endmodule(tolua_S); diff --git a/src/Bindings/ManualBindings_BlockArea.cpp b/src/Bindings/ManualBindings_BlockArea.cpp index 5af150599..5f281fadc 100644 --- a/src/Bindings/ManualBindings_BlockArea.cpp +++ b/src/Bindings/ManualBindings_BlockArea.cpp @@ -435,7 +435,17 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * a_LuaState) return L.ApiParamError("Invalid 'self', must not be nil"); } - L.Push(cSchematicFileSerializer::LoadFromSchematicFile(*self, fileName)); + try + { + cSchematicFileSerializer::LoadFromSchematicFile(*self, fileName); + L.Push(true); + } + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); + L.LogStackTrace(); + L.Push(false); + } return 1; } @@ -457,7 +467,7 @@ static int tolua_cBlockArea_LoadFromSchematicString(lua_State * a_LuaState) return 0; } cBlockArea * self; - AString data; + ContiguousByteBuffer data; if (!L.GetStackValues(1, self, data)) { return L.ApiParamError("Cannot read the parameters"); @@ -467,7 +477,17 @@ static int tolua_cBlockArea_LoadFromSchematicString(lua_State * a_LuaState) return L.ApiParamError("Invalid 'self', must not be nil"); } - L.Push(cSchematicFileSerializer::LoadFromSchematicString(*self, data)); + try + { + cSchematicFileSerializer::LoadFromSchematicString(*self, data); + L.Push(true); + } + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); + L.LogStackTrace(); + L.Push(false); + } return 1; } @@ -625,7 +645,17 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * a_LuaState) return L.ApiParamError("Invalid 'self', must not be nil"); } - L.Push(cSchematicFileSerializer::SaveToSchematicFile(*self, fileName)); + try + { + cSchematicFileSerializer::SaveToSchematicFile(*self, fileName); + L.Push(true); + } + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); + L.LogStackTrace(); + L.Push(false); + } return 1; } @@ -655,13 +685,17 @@ static int tolua_cBlockArea_SaveToSchematicString(lua_State * a_LuaState) return L.ApiParamError("Invalid 'self', must not be nil"); } - AString data; - if (cSchematicFileSerializer::SaveToSchematicString(*self, data)) + try { - L.Push(data); + L.Push(cSchematicFileSerializer::SaveToSchematicString(*self).GetView()); return 1; } - return 0; + catch (const std::exception & Oops) + { + LOGWARNING(Oops.what()); + L.LogStackTrace(); + return 0; + } } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index e051ff61c..47f8820e8 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -95,7 +95,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; - virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) = 0; + virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, ContiguousByteBufferView a_Message) = 0; virtual bool OnPluginsLoaded (void) = 0; virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index f4cfd4a86..9b6bccf7c 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -818,7 +818,7 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block -bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) +bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const ContiguousByteBufferView a_Message) { return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message); } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 2a48adb1e..c3064450f 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -117,7 +117,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) override; + virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, ContiguousByteBufferView a_Message) override; virtual bool OnPluginsLoaded (void) override; virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 310a3968b..59761ead9 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1009,7 +1009,7 @@ bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, i -bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) +bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const ContiguousByteBufferView a_Message) { return GenericCallHook(HOOK_PLUGIN_MESSAGE, [&](cPlugin * a_Plugin) { diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 8d75509a1..c1f798291 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -288,7 +288,7 @@ public: bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message); + bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, ContiguousByteBufferView a_Message); bool CallHookPluginsLoaded (void); bool CallHookPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index f3a6e3a7d..2de6aec60 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -83,7 +83,7 @@ Unfortunately it is very slow, so it is disabled even for regular DEBUG builds. // cByteBuffer: cByteBuffer::cByteBuffer(size_t a_BufferSize) : - m_Buffer(new char[a_BufferSize + 1]), + m_Buffer(new std::byte[a_BufferSize + 1]), m_BufferSize(a_BufferSize + 1), m_DataStart(0), m_WritePos(0), @@ -446,7 +446,15 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value) { LOGWARNING("%s: String too large: %u (%u KiB)", __FUNCTION__, Size, Size / 1024); } - return ReadString(a_Value, static_cast<size_t>(Size)); + ContiguousByteBuffer Buffer; + if (!ReadSome(Buffer, static_cast<size_t>(Size))) + { + return false; + } + // "Convert" a UTF-8 encoded string into system-native char. + // This isn't great, better would be to use codecvt: + a_Value = { reinterpret_cast<const char *>(Buffer.data()), Buffer.size() }; + return true; } @@ -552,6 +560,18 @@ bool cByteBuffer::WriteBEInt8(Int8 a_Value) +bool cByteBuffer::WriteBEInt8(const std::byte a_Value) +{ + CHECK_THREAD + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + bool cByteBuffer::WriteBEUInt8(UInt8 a_Value) { CHECK_THREAD @@ -836,7 +856,7 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count) -bool cByteBuffer::ReadString(AString & a_String, size_t a_Count) +bool cByteBuffer::ReadSome(ContiguousByteBuffer & a_String, size_t a_Count) { CHECK_THREAD CheckValid(); @@ -886,11 +906,11 @@ bool cByteBuffer::SkipRead(size_t a_Count) -void cByteBuffer::ReadAll(AString & a_Data) +void cByteBuffer::ReadAll(ContiguousByteBuffer & a_Data) { CHECK_THREAD CheckValid(); - ReadString(a_Data, GetReadableSpace()); + ReadSome(a_Data, GetReadableSpace()); } @@ -944,7 +964,7 @@ void cByteBuffer::ResetRead(void) -void cByteBuffer::ReadAgain(AString & a_Out) +void cByteBuffer::ReadAgain(ContiguousByteBuffer & a_Out) { // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party @@ -1004,8 +1024,3 @@ size_t cByteBuffer::GetVarIntSize(UInt32 a_Value) return Count; } - - - - - diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 1116de08c..cbf215f38 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -11,6 +11,8 @@ + + // fwd: class cUUID; @@ -87,6 +89,7 @@ public: // Write the specified datatype; return true if successfully written bool WriteBEInt8 (Int8 a_Value); + bool WriteBEInt8 (std::byte a_Value); bool WriteBEInt16 (Int16 a_Value); bool WriteBEInt32 (Int32 a_Value); bool WriteBEInt64 (Int64 a_Value); @@ -111,13 +114,13 @@ public: bool WriteBuf(const void * a_Buffer, size_t a_Count); /** Reads a_Count bytes into a_String; returns true if successful */ - bool ReadString(AString & a_String, size_t a_Count); + bool ReadSome(ContiguousByteBuffer & a_String, size_t a_Count); /** Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer */ bool SkipRead(size_t a_Count); /** Reads all available data into a_Data */ - void ReadAll(AString & a_Data); + void ReadAll(ContiguousByteBuffer & a_Data); /** Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success. */ bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes); @@ -129,7 +132,7 @@ public: void ResetRead(void); /** Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication */ - void ReadAgain(AString & a_Out); + void ReadAgain(ContiguousByteBuffer & a_Out); /** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */ void CheckValid(void) const; @@ -138,7 +141,8 @@ public: static size_t GetVarIntSize(UInt32 a_Value); protected: - char * m_Buffer; + + std::byte * m_Buffer; size_t m_BufferSize; // Total size of the ringbuffer size_t m_DataStart; // Where the data starts in the ringbuffer @@ -154,7 +158,3 @@ protected: /** Advances the m_ReadPos by a_Count bytes */ void AdvanceReadPos(size_t a_Count); } ; - - - - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3844c6c10..02c372a1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources( ChunkMap.cpp ChunkSender.cpp ChunkStay.cpp + CircularBufferCompressor.cpp ClientHandle.cpp Color.cpp CommandOutput.cpp @@ -88,6 +89,7 @@ target_sources( ChunkMap.h ChunkSender.h ChunkStay.h + CircularBufferCompressor.h ClientHandle.h Color.h CommandOutput.h diff --git a/src/Chunk.cpp b/src/Chunk.cpp index ff5757703..5a6cca3eb 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -11,7 +11,6 @@ #include "World.h" #include "ClientHandle.h" #include "Server.h" -#include "zlib/zlib.h" #include "Defines.h" #include "BlockEntities/BeaconEntity.h" #include "BlockEntities/BedEntity.h" diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index a2cd621d7..f06dd057f 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -22,9 +22,6 @@ #include "DeadlockDetect.h" #include "BlockEntities/BlockEntity.h" -#include "zlib/zlib.h" -#include "json/json.h" - diff --git a/src/CircularBufferCompressor.cpp b/src/CircularBufferCompressor.cpp new file mode 100644 index 000000000..823dd8862 --- /dev/null +++ b/src/CircularBufferCompressor.cpp @@ -0,0 +1,67 @@ + +#include "Globals.h" +#include "CircularBufferCompressor.h" +#include "ByteBuffer.h" + + + + + +ContiguousByteBufferView CircularBufferCompressor::GetView() const +{ + return m_ContiguousIntermediate; +} + + + + + +Compression::Result CircularBufferCompressor::Compress() +{ + return m_Compressor.CompressZLib(m_ContiguousIntermediate); +} + + + + + +void CircularBufferCompressor::ReadFrom(cByteBuffer & Buffer) +{ + Buffer.ReadAll(m_ContiguousIntermediate); +} + + + + + +void CircularBufferCompressor::ReadFrom(cByteBuffer & Buffer, size_t Size) +{ + Buffer.ReadSome(m_ContiguousIntermediate, Size); +} + + + + + +ContiguousByteBufferView CircularBufferExtractor::GetView() const +{ + return m_ContiguousIntermediate; +} + + + + + +Compression::Result CircularBufferExtractor::Extract(size_t UncompressedSize) +{ + return m_Extractor.ExtractZLib(m_ContiguousIntermediate, UncompressedSize); +} + + + + + +void CircularBufferExtractor::ReadFrom(cByteBuffer & Buffer, size_t Size) +{ + Buffer.ReadSome(m_ContiguousIntermediate, Size); +} diff --git a/src/CircularBufferCompressor.h b/src/CircularBufferCompressor.h new file mode 100644 index 000000000..12c708baf --- /dev/null +++ b/src/CircularBufferCompressor.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "StringCompression.h" + + + + + +class CircularBufferCompressor +{ +public: + + ContiguousByteBufferView GetView() const; + + Compression::Result Compress(); + void ReadFrom(cByteBuffer & Buffer); + void ReadFrom(cByteBuffer & Buffer, size_t Size); + +private: + + Compression::Compressor m_Compressor; + std::basic_string<std::byte> m_ContiguousIntermediate; +}; + + + + + +class CircularBufferExtractor +{ +public: + + ContiguousByteBufferView GetView() const; + + Compression::Result Extract(size_t UncompressedSize); + void ReadFrom(cByteBuffer & Buffer, size_t Size); + +private: + + Compression::Extractor m_Extractor; + std::basic_string<std::byte> m_ContiguousIntermediate; +}; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 89fe3d3b9..4ec01744b 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -678,7 +678,7 @@ void cClientHandle::HandlePing(void) -bool cClientHandle::HandleLogin(const AString & a_Username) +bool cClientHandle::HandleLogin() { { cCSLock lock(m_CSState); @@ -694,10 +694,8 @@ bool cClientHandle::HandleLogin(const AString & a_Username) // LOGD("Handling login for client %s @ %s (%p), state = %d", a_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load()); - m_Username = a_Username; - // Let the plugins know about this event, they may refuse the player: - if (cRoot::Get()->GetPluginManager()->CallHookLogin(*this, m_ProtocolVersion, a_Username)) + if (cRoot::Get()->GetPluginManager()->CallHookLogin(*this, m_ProtocolVersion, GetUsername())) { SendDisconnect("Login Rejected!"); return false; @@ -885,7 +883,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, -void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message) +void cClientHandle::HandlePluginMessage(const AString & a_Channel, const ContiguousByteBufferView a_Message) { if (a_Channel == "REGISTER") { @@ -903,7 +901,7 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString } else if (a_Channel == "FML|HS") { - m_ForgeHandshake.DataReceived(this, a_Message.c_str(), a_Message.size()); + m_ForgeHandshake.DataReceived(this, a_Message); } else if (!HasPluginChannel(a_Channel)) { @@ -920,7 +918,7 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString -AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginChannels) +AStringVector cClientHandle::BreakApartPluginChannels(const ContiguousByteBufferView a_PluginChannels) { // Break the string on each NUL character. // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator @@ -929,19 +927,21 @@ AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginCh AStringVector res; for (size_t i = 0; i < len; i++) { - if (a_PluginChannels[i] != 0) + if (a_PluginChannels[i] != std::byte(0)) { continue; } if (i > first) { - res.push_back(a_PluginChannels.substr(first, i - first)); + const auto Part = a_PluginChannels.substr(first, i - first); + res.emplace_back(reinterpret_cast<const char *>(Part.data()), Part.size()); } first = i + 1; } // for i - a_PluginChannels[] if (first < len) { - res.push_back(a_PluginChannels.substr(first, len - first)); + const auto Part = a_PluginChannels.substr(first, len - first); + res.emplace_back(reinterpret_cast<const char *>(Part.data()), Part.size()); } return res; } @@ -2001,7 +2001,7 @@ void cClientHandle::HandleTabCompletion(const AString & a_Text) -void cClientHandle::SendData(const char * a_Data, size_t a_Size) +void cClientHandle::SendData(const ContiguousByteBufferView a_Data) { if (m_HasSentDC) { @@ -2010,7 +2010,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size) } cCSLock Lock(m_CSOutgoingData); - m_OutgoingData.append(a_Data, a_Size); + m_OutgoingData += a_Data; } @@ -2085,7 +2085,15 @@ void cClientHandle::Tick(float a_Dt) m_BreakProgress += m_Player->GetMiningProgressPerTick(Block); } - ProcessProtocolInOut(); + try + { + ProcessProtocolInOut(); + } + catch (const std::exception & Oops) + { + Kick(Oops.what()); + return; // Return early to give a chance to send the kick packet before link shutdown + } // If player has been kicked, terminate the connection: if (m_State == csKicked) @@ -2451,7 +2459,7 @@ void cClientHandle::SendChatSystem(const cCompositeChat & a_Message) -void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, const std::string_view a_ChunkData) +void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, const ContiguousByteBufferView a_ChunkData) { ASSERT(m_Player != nullptr); @@ -2486,7 +2494,7 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, const std::string_ // Add the chunk to the list of chunks sent to the player: { cCSLock Lock(m_CSChunkLists); - m_SentChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_SentChunks.emplace_back(a_ChunkX, a_ChunkZ); } } @@ -2860,7 +2868,16 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) -void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +void cClientHandle::SendPluginMessage(const AString & a_Channel, const std::string_view a_Message) +{ + m_Protocol->SendPluginMessage(a_Channel, { reinterpret_cast<const std::byte *>(a_Message.data()), a_Message.size() }); +} + + + + + +void cClientHandle::SendPluginMessage(const AString & a_Channel, const ContiguousByteBufferView a_Message) { m_Protocol->SendPluginMessage(a_Channel, a_Message); } @@ -3231,9 +3248,9 @@ const AString & cClientHandle::GetUsername(void) const -void cClientHandle::SetUsername(const AString & a_Username) +void cClientHandle::SetUsername(AString && a_Username) { - m_Username = a_Username; + m_Username = std::move(a_Username); } @@ -3398,7 +3415,7 @@ void cClientHandle::ProcessProtocolInOut(void) } // Send any queued outgoing data: - AString OutgoingData; + ContiguousByteBuffer OutgoingData; { cCSLock Lock(m_CSOutgoingData); std::swap(OutgoingData, m_OutgoingData); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index e52236411..555c4396a 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -153,7 +153,7 @@ public: // tolua_export void SendChatAboveActionBar (const cCompositeChat & a_Message); void SendChatSystem (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChatSystem (const cCompositeChat & a_Message); - void SendChunkData (int a_ChunkX, int a_ChunkZ, const std::string_view a_ChunkData); + void SendChunkData (int a_ChunkX, int a_ChunkZ, ContiguousByteBufferView a_ChunkData); void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count); void SendDestroyEntity (const cEntity & a_Entity); void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle); @@ -192,7 +192,8 @@ public: // tolua_export void SendPlayerMoveLook (void); void SendPlayerPosition (void); void SendPlayerSpawn (const cPlayer & a_Player); - void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp + void SendPluginMessage (const AString & a_Channel, std::string_view a_Message); // Exported in ManualBindings.cpp + void SendPluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message); void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID); void SendResourcePack (const AString & a_ResourcePackUrl); void SendResetTitle (void); // tolua_export @@ -231,9 +232,10 @@ public: // tolua_export void SendWindowOpen (const cWindow & a_Window); void SendWindowProperty (const cWindow & a_Window, size_t a_Property, short a_Value); + const AString & GetUsername(void) const; // tolua_export + void SetUsername(AString && a_Username); + // tolua_begin - const AString & GetUsername(void) const; - void SetUsername( const AString & a_Username); inline short GetPing(void) const { return static_cast<short>(std::chrono::duration_cast<std::chrono::milliseconds>(m_Ping).count()); } @@ -357,7 +359,7 @@ public: // tolua_export void HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); - void HandlePluginMessage (const AString & a_Channel, const AString & a_Message); + void HandlePluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message); void HandleRespawn (void); void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, eHand a_Hand); void HandleSlotSelected (Int16 a_SlotNum); @@ -379,11 +381,10 @@ public: // tolua_export void HandleCraftRecipe (UInt32 a_RecipeId); /** Called when the protocol has finished logging the user in. - Return true to allow the user in; false to kick them. - */ - bool HandleLogin(const AString & a_Username); + Return true to allow the user in; false to kick them. */ + bool HandleLogin(); - void SendData(const char * a_Data, size_t a_Size); + void SendData(ContiguousByteBufferView a_Data); /** Called when the player moves into a different world. Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */ @@ -448,7 +449,7 @@ private: /** Buffer for storing outgoing data from any thread; will get sent in Tick() (to prevent deadlocks). Protected by m_CSOutgoingData. */ - AString m_OutgoingData; + ContiguousByteBuffer m_OutgoingData; Vector3d m_ConfirmPosition; @@ -580,7 +581,7 @@ private: void FinishDigAnimation(); /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ - AStringVector BreakApartPluginChannels(const AString & a_PluginChannels); + AStringVector BreakApartPluginChannels(ContiguousByteBufferView a_PluginChannels); /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */ void RegisterPluginChannels(const AStringVector & a_ChannelList); diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 8b7d998d0..5b75402b8 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -430,10 +430,8 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) else if (NoCaseCompare(finisher, "DirtPockets") == 0) { auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 1, cFinishGenOrePockets::DefaultNaturalPatches()); - if (gen->Initialize(a_IniFile, "DirtPockets")) - { - m_FinishGens.push_back(gen); - } + gen->Initialize(a_IniFile, "DirtPockets"); + m_FinishGens.push_back(gen); } else if (NoCaseCompare(finisher, "DistortedMembraneOverhangs") == 0) { @@ -508,15 +506,6 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) { m_FinishGens.push_back(cFinishGenPtr(new cFinishGenNetherClumpFoliage(m_Seed))); } - else if (NoCaseCompare(*itr, "NetherForts") == 0) - { - LOGINFO("The NetherForts finisher is obsolete, you should use \"PieceStructures: NetherFort\" instead."); - auto gen = std::make_shared<cPieceStructuresGen>(m_Seed); - if (gen->Initialize("NetherFort", seaLevel, m_BiomeGen, m_CompositedHeightCache)) - { - m_FinishGens.push_back(gen); - } - } else if (NoCaseCompare(finisher, "NetherOreNests") == 0) { m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(m_Seed + 2, cFinishGenOreNests::DefaultNetherOres())); @@ -528,10 +517,8 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) else if (NoCaseCompare(finisher, "OrePockets") == 0) { auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 2, cFinishGenOrePockets::DefaultOverworldOres()); - if (gen->Initialize(a_IniFile, "OrePockets")) - { - m_FinishGens.push_back(gen); - } + gen->Initialize(a_IniFile, "OrePockets"); + m_FinishGens.push_back(gen); } else if (NoCaseCompare(finisher, "OverworldClumpFlowers") == 0) { @@ -561,15 +548,6 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPreSimulator(PreSimulateFallingBlocks, PreSimulateWater, PreSimulateLava))); } - else if (NoCaseCompare(finisher, "RainbowRoads") == 0) - { - LOGINFO("The RainbowRoads finisher is obsolete, you should use \"PieceStructures: RainbowRoads\" instead."); - auto gen = std::make_shared<cPieceStructuresGen>(m_Seed); - if (gen->Initialize("RainbowRoads", seaLevel, m_BiomeGen, m_CompositedHeightCache)) - { - m_FinishGens.push_back(gen); - } - } else if (NoCaseCompare(finisher, "Ravines") == 0) { m_FinishGens.push_back(cFinishGenPtr(new cStructGenRavines(m_Seed, 128))); @@ -645,15 +623,6 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) { m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(m_Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen))); } - else if (NoCaseCompare(finisher, "UnderwaterBases") == 0) - { - LOGINFO("The UnderwaterBases finisher is obsolete, you should use \"PieceStructures: UnderwaterBases\" instead."); - auto gen = std::make_shared<cPieceStructuresGen>(m_Seed); - if (gen->Initialize("UnderwaterBases", seaLevel, m_BiomeGen, m_CompositedHeightCache)) - { - m_FinishGens.push_back(gen); - } - } else if (NoCaseCompare(finisher, "Villages") == 0) { int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384); diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 10bd4eac9..6e3f8b446 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -1985,7 +1985,7 @@ void cFinishGenOreNests::GenerateOre( //////////////////////////////////////////////////////////////////////////////// // cFinishGenOrePockets: -bool cFinishGenOrePockets::Initialize(cIniFile & a_IniFile, const AString & a_GenName) +void cFinishGenOrePockets::Initialize(cIniFile & a_IniFile, const AString & a_GenName) { // Read the OreInfos configuration: auto valueName = a_GenName + "Blocks"; @@ -2003,8 +2003,6 @@ bool cFinishGenOrePockets::Initialize(cIniFile & a_IniFile, const AString & a_Ge // Read the optional seed configuration (but do not store the default): valueName = a_GenName + "Seed"; SetSeed(a_IniFile.GetValueI("Generator", valueName, m_Noise.GetSeed())); - - return true; } diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index d6458e4fc..76b58e60d 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -589,9 +589,8 @@ public: {} /** Reads the configuration from the specified INI file. - a_GenName is the name of the generator (this class may be used for OrePockets and DirtPockets, each has a different default). - Returns true on success, false and logs errors to console on failure. */ - bool Initialize(cIniFile & a_IniFile, const AString & a_GenName); + a_GenName is the name of the generator (this class may be used for OrePockets and DirtPockets, each has a different default). */ + void Initialize(cIniFile & a_IniFile, const AString & a_GenName); protected: diff --git a/src/Generating/PrefabPiecePool.cpp b/src/Generating/PrefabPiecePool.cpp index e52bb1621..28355660c 100644 --- a/src/Generating/PrefabPiecePool.cpp +++ b/src/Generating/PrefabPiecePool.cpp @@ -78,15 +78,6 @@ cPrefabPiecePool::cPrefabPiecePool( -cPrefabPiecePool::cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings) -{ - LoadFromFile(a_FileName, a_LogWarnings); -} - - - - - cPrefabPiecePool::~cPrefabPiecePool() { Clear(); @@ -174,21 +165,28 @@ bool cPrefabPiecePool::LoadFromString(const AString & a_Contents, const AString // If the contents start with GZip signature, ungzip and retry: if (a_Contents.substr(0, 3) == "\x1f\x8b\x08") { - AString Uncompressed; - auto res = UncompressStringGZIP(a_Contents.data(), a_Contents.size(), Uncompressed); - if (res == Z_OK) + try { - return LoadFromString(Uncompressed, a_FileName, a_LogWarnings); + const auto Extracted = Compression::Extractor().ExtractGZip( + { + reinterpret_cast<const std::byte *>(a_Contents.data()), a_Contents.size() + }); + + // Here we do an extra std::string conversion, hardly efficient, but... + // Better would be refactor into LoadFromByteView for the GZip decompression path, and getting cFile to support std::byte. + // ...so it'll do for now. + + return LoadFromString(std::string(Extracted.GetStringView()), a_FileName, a_LogWarnings); } - else + catch (const std::exception & Oops) { - CONDWARNING(a_LogWarnings, "Failed to decompress Gzip data in file %s: %d", a_FileName.c_str(), res); + CONDWARNING(a_LogWarnings, "Failed to decompress Gzip data in file %s. %s", a_FileName.c_str(), Oops.what()); return false; } } // Search the first 8 KiB of the file for the format auto-detection string: - auto Header = a_Contents.substr(0, 8192); + const auto Header = a_Contents.substr(0, 8 KiB); if (Header.find("CubesetFormatVersion =") != AString::npos) { return LoadFromCubeset(a_Contents, a_FileName, a_LogWarnings); @@ -391,10 +389,14 @@ std::unique_ptr<cPrefab> cPrefabPiecePool::LoadPrefabFromCubesetVer1( SchematicFileName = a_FileName.substr(0, PathEnd) + SchematicFileName; } cBlockArea area; - if (!cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName)) + try + { + cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName); + } + catch (const std::exception & Oops) { - CONDWARNING(a_LogWarnings, "Cannot load schematic file \"%s\" for piece %s in cubeset %s.", - SchematicFileName.c_str(), a_PieceName.c_str(), a_FileName.c_str() + CONDWARNING(a_LogWarnings, "Cannot load schematic file \"%s\" for piece %s in cubeset %s. %s", + SchematicFileName.c_str(), a_PieceName.c_str(), a_FileName.c_str(), Oops.what() ); return nullptr; } diff --git a/src/Generating/PrefabPiecePool.h b/src/Generating/PrefabPiecePool.h index 84aba92d1..6bbb7ac13 100644 --- a/src/Generating/PrefabPiecePool.h +++ b/src/Generating/PrefabPiecePool.h @@ -44,10 +44,6 @@ public: int a_DefaultStartingPieceHeight = -1 ); - /** Creates a pool and loads the contents of the specified file into it. - If a_LogWarnings is true, logs a warning to console when loading fails. */ - cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings); - /** Destroys the pool, freeing all pieces. */ virtual ~cPrefabPiecePool() override; diff --git a/src/Globals.h b/src/Globals.h index 192969fa5..cdfea9e5a 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -75,39 +75,8 @@ #endif -#include <stddef.h> -// Integral types with predefined sizes: -typedef signed long long Int64; -typedef signed int Int32; -typedef signed short Int16; -typedef signed char Int8; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; -typedef unsigned char UInt8; - -typedef unsigned char Byte; -typedef Byte ColourID; - - -template <typename T, size_t Size> -class SizeChecker -{ - static_assert(sizeof(T) == Size, "Check the size of integral types"); -}; - -template class SizeChecker<Int64, 8>; -template class SizeChecker<Int32, 4>; -template class SizeChecker<Int16, 2>; -template class SizeChecker<Int8, 1>; - -template class SizeChecker<UInt64, 8>; -template class SizeChecker<UInt32, 4>; -template class SizeChecker<UInt16, 2>; -template class SizeChecker<UInt8, 1>; // A macro to disallow the copy constructor and operator = functions // This should be used in the declarations for any class that shouldn't allow copying itself @@ -130,6 +99,7 @@ template class SizeChecker<UInt8, 1>; + // OS-dependent stuff: #ifdef _WIN32 @@ -161,6 +131,7 @@ template class SizeChecker<UInt8, 1>; #include <cstdio> #include <cmath> #include <cstdarg> +#include <cstddef> @@ -188,10 +159,43 @@ template class SizeChecker<UInt8, 1>; #include <unordered_map> #include <unordered_set> #include <vector> +#include <variant> + +// Integral types with predefined sizes: +typedef signed long long Int64; +typedef signed int Int32; +typedef signed short Int16; +typedef signed char Int8; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; +typedef unsigned char UInt8; + +typedef unsigned char Byte; +typedef Byte ColourID; + + +template <typename T, size_t Size> +class SizeChecker +{ + static_assert(sizeof(T) == Size, "Check the size of integral types"); +}; + +template class SizeChecker<Int64, 8>; +template class SizeChecker<Int32, 4>; +template class SizeChecker<Int16, 2>; +template class SizeChecker<Int8, 1>; + +template class SizeChecker<UInt64, 8>; +template class SizeChecker<UInt32, 4>; +template class SizeChecker<UInt16, 2>; +template class SizeChecker<UInt8, 1>; + // Common headers (part 1, without macros): #include "fmt.h" #include "StringUtils.h" @@ -301,6 +305,20 @@ template class SizeChecker<UInt8, 1>; + +namespace cpp20 +{ + template <class T> + std::enable_if_t<std::is_array_v<T> && (std::extent_v<T> == 0), std::unique_ptr<T>> make_unique_for_overwrite(std::size_t a_Size) + { + return std::unique_ptr<T>(new std::remove_extent_t<T>[a_Size]); + } +} + + + + + /** Clamp X to the specified range. */ template <typename T> T Clamp(T a_Value, T a_Min, T a_Max) @@ -334,6 +352,9 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value) using cTickTime = std::chrono::duration<int, std::ratio_multiply<std::chrono::milliseconds::period, std::ratio<50>>>; using cTickTimeLong = std::chrono::duration<Int64, cTickTime::period>; +using ContiguousByteBuffer = std::basic_string<std::byte>; +using ContiguousByteBufferView = std::basic_string_view<std::byte>; + #ifndef TOLUA_TEMPLATE_BIND #define TOLUA_TEMPLATE_BIND(x) #endif @@ -355,4 +376,3 @@ auto ToUnsigned(T a_Val) // Common headers (part 2, with macros): #include "Vector3.h" - diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index 8c5eb92a4..618463bd6 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -157,19 +157,18 @@ int cFile::Read (void * a_Buffer, size_t a_NumBytes) -AString cFile::Read(size_t a_NumBytes) +ContiguousByteBuffer cFile::Read(size_t a_NumBytes) { ASSERT(IsOpen()); if (!IsOpen()) { - return AString(); + return {}; } - // HACK: This depends on the knowledge that AString::data() returns the internal buffer, rather than a copy of it. - AString res; - res.resize(a_NumBytes); - auto newSize = fread(const_cast<char *>(res.data()), 1, a_NumBytes, m_File); + ContiguousByteBuffer res; + res.resize(a_NumBytes); // TODO: investigate if worth hacking around std::string internals to avoid initialisation + auto newSize = fread(res.data(), sizeof(std::byte), a_NumBytes, m_File); res.resize(newSize); return res; } @@ -284,9 +283,8 @@ int cFile::ReadRestOfFile(AString & a_Contents) auto DataSize = static_cast<size_t>(TotalSize - Position); - // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly - a_Contents.assign(DataSize, '\0'); - return Read(static_cast<void *>(const_cast<char *>(a_Contents.data())), DataSize); + a_Contents.resize(DataSize); // TODO: investigate if worth hacking around std::string internals to avoid initialisation + return Read(a_Contents.data(), DataSize); } @@ -709,3 +707,54 @@ void cFile::Flush(void) { fflush(m_File); } + + + + + +template <class StreamType> +FileStream<StreamType>::FileStream(const std::string & Path) +{ + // Except on failbit, which is what open sets on failure: + FileStream::exceptions(FileStream::failbit | FileStream::badbit); + + // Open the file: + FileStream::open(Path); + + // Only subsequently except on serious errors, and not on conditions like EOF or malformed input: + FileStream::exceptions(FileStream::badbit); +} + + + + + +template <class StreamType> +FileStream<StreamType>::FileStream(const std::string & Path, const typename FileStream::openmode Mode) +{ + // Except on failbit, which is what open sets on failure: + FileStream::exceptions(FileStream::failbit | FileStream::badbit); + + // Open the file: + FileStream::open(Path, Mode); + + // Only subsequently except on serious errors, and not on conditions like EOF or malformed input: + FileStream::exceptions(FileStream::badbit); +} + + + + + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-template-vtables" // http://bugs.llvm.org/show_bug.cgi?id=18733 +#endif + +// Instantiate the templated wrapper for input and output: +template class FileStream<std::ifstream>; +template class FileStream<std::ofstream>; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h index 59f3a5558..7a3333483 100644 --- a/src/OSSupport/File.h +++ b/src/OSSupport/File.h @@ -75,7 +75,7 @@ public: int Read(void * a_Buffer, size_t a_NumBytes); /** Reads up to a_NumBytes bytes, returns the bytes actually read, or empty string on failure; asserts if not open */ - AString Read(size_t a_NumBytes); + std::basic_string<std::byte> Read(size_t a_NumBytes); /** Writes up to a_NumBytes bytes from a_Buffer, returns the number of bytes actually written, or -1 on failure; asserts if not open */ int Write(const void * a_Buffer, size_t a_NumBytes); @@ -192,17 +192,8 @@ class FileStream final : public StreamType { public: - FileStream(const std::string & Path) - { - // Except on failbit, which is what open sets on failure: - FileStream::exceptions(FileStream::failbit | FileStream::badbit); - - // Open the file: - FileStream::open(Path); - - // Only subsequently except on serious errors, and not on conditions like EOF or malformed input: - FileStream::exceptions(FileStream::badbit); - } + FileStream(const std::string & Path); + FileStream(const std::string & Path, const typename FileStream::openmode Mode); }; @@ -211,3 +202,15 @@ public: using InputFileStream = FileStream<std::ifstream>; using OutputFileStream = FileStream<std::ofstream>; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-template-vtables" // http://bugs.llvm.org/show_bug.cgi?id=18733 +#endif + +extern template class FileStream<std::ifstream>; +extern template class FileStream<std::ofstream>; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp index e83d42583..0bf26dfed 100644 --- a/src/OSSupport/GZipFile.cpp +++ b/src/OSSupport/GZipFile.cpp @@ -4,107 +4,27 @@ // Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines #include "Globals.h" +#include "File.h" #include "GZipFile.h" -cGZipFile::cGZipFile(void) : - m_File(nullptr), m_Mode(fmRead) +Compression::Result GZipFile::ReadRestOfFile(const std::string & a_FileName) { -} - - - - - -cGZipFile::~cGZipFile() -{ - Close(); -} - + InputFileStream File(a_FileName, InputFileStream::binary); + const std::string Input{ std::istreambuf_iterator<char>(File), std::istreambuf_iterator<char>() }; + const ContiguousByteBufferView Data{ reinterpret_cast<const std::byte *>(Input.data()), Input.size() }; - - - -bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) -{ - if (m_File != nullptr) - { - ASSERT(!"A file is already open in this object"); - return false; - } - m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); - m_Mode = a_Mode; - return (m_File != nullptr); + return Compression::Extractor().ExtractGZip(Data); } -void cGZipFile::Close(void) +void GZipFile::Write(const std::string & a_FileName, ContiguousByteBufferView a_Contents) { - if (m_File != nullptr) - { - gzclose(m_File); - m_File = nullptr; - } + OutputFileStream(a_FileName, OutputFileStream::binary) << Compression::Compressor().CompressGZip(a_Contents).GetStringView(); } - - - - - -int cGZipFile::ReadRestOfFile(AString & a_Contents) -{ - if (m_File == nullptr) - { - ASSERT(!"No file has been opened"); - return -1; - } - - if (m_Mode != fmRead) - { - ASSERT(!"Bad file mode, cannot read"); - return -1; - } - - // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! - int NumBytesRead = 0; - int TotalBytes = 0; - char Buffer[64 KiB]; - while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) - { - TotalBytes += NumBytesRead; - a_Contents.append(Buffer, static_cast<size_t>(NumBytesRead)); - } - // NumBytesRead is < 0 on error - return (NumBytesRead >= 0) ? TotalBytes : NumBytesRead; -} - - - - - -bool cGZipFile::Write(const char * a_Contents, int a_Size) -{ - if (m_File == nullptr) - { - ASSERT(!"No file has been opened"); - return false; - } - - if (m_Mode != fmWrite) - { - ASSERT(!"Bad file mode, cannot write"); - return false; - } - - return (gzwrite(m_File, a_Contents, static_cast<unsigned int>(a_Size)) != 0); -} - - - - diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h index 00a2fd717..dd4999339 100644 --- a/src/OSSupport/GZipFile.h +++ b/src/OSSupport/GZipFile.h @@ -1,7 +1,7 @@ // GZipFile.h -// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines +// Declares the GZipFile namespace representing a wrapper over a file stream that can read and write to GZip'd files @@ -9,44 +9,17 @@ #pragma once -#include "zlib/zlib.h" +#include "StringCompression.h" -class cGZipFile +namespace GZipFile { -public: - enum eMode - { - fmRead, // Read-only. If the file doesn't exist, object will not be valid - fmWrite, // Write-only. If the file already exists, it will be overwritten - } ; + /** Reads the rest of the file and returns the decompressed contents. */ + Compression::Result ReadRestOfFile(const std::string & a_FileName); - cGZipFile(void); - ~cGZipFile(); - - /** Opens the file. Returns true if successful. Fails if a file has already been opened through this object. */ - bool Open(const AString & a_FileName, eMode a_Mode); - - /** Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode */ - void Close(void); - - /** Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error */ - int ReadRestOfFile(AString & a_Contents); - - /** Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. */ - bool Write(const AString & a_Contents) { return Write(a_Contents.data(), static_cast<int>(a_Contents.size())); } - - bool Write(const char * a_Data, int a_Size); - -protected: - gzFile m_File; - eMode m_Mode; + /** Writes a_Contents into file, compressing it along the way. */ + void Write(const std::string & a_FileName, ContiguousByteBufferView a_Contents); } ; - - - - - diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index ac740a145..a16cce02b 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -1,6 +1,5 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "zlib/zlib.h" #include "Protocol_1_8.h" #include "Protocol_1_9.h" #include "../ClientHandle.h" @@ -543,14 +542,10 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh inline void cChunkDataSerializer::CompressPacketInto(ChunkDataCache & a_Cache) { - m_Packet.ReadAll(a_Cache.PacketData); + m_Compressor.ReadFrom(m_Packet); m_Packet.CommitRead(); - if (!cProtocol_1_8_0::CompressPacket(a_Cache.PacketData, a_Cache.ToSend)) - { - ASSERT(!"Packet compression failed."); - return; - } + cProtocol_1_8_0::CompressPacket(m_Compressor, a_Cache.ToSend); a_Cache.Engaged = true; } diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index aeff7c356..ea0b0be11 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -3,6 +3,8 @@ #include "../ByteBuffer.h" #include "../ChunkData.h" #include "../Defines.h" +#include "CircularBufferCompressor.h" +#include "StringCompression.h" @@ -37,8 +39,7 @@ class cChunkDataSerializer /** A single cache entry containing the raw data, compressed data, and a validity flag. */ struct ChunkDataCache { - std::string PacketData; - std::string ToSend; + ContiguousByteBuffer ToSend; bool Engaged = false; }; @@ -50,7 +51,7 @@ public: Parameters are the coordinates of the chunk to serialise, and the data and biome data read from the chunk. */ void SendToClients(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, const ClientHandles & a_SendTo); -protected: +private: /** Serialises the given chunk, storing the result into the given cache entry, and sends the data. If the cache entry is already present, simply re-uses it. */ @@ -74,6 +75,9 @@ protected: /** A staging area used to construct the chunk packet, persistent to avoid reallocating. */ cByteBuffer m_Packet; + /** A compressor used to compress the chunk data. */ + CircularBufferCompressor m_Compressor; + /** The dimension for the World this Serializer is tied to. */ const eDimension m_Dimension; @@ -81,7 +85,3 @@ protected: It is used during a single invocation of SendToClients with more than one client. */ std::array<ChunkDataCache, static_cast<size_t>(CacheVersion::Last) + 1> m_Cache; } ; - - - - diff --git a/src/Protocol/ForgeHandshake.cpp b/src/Protocol/ForgeHandshake.cpp index 8c16d4f0c..99b585894 100644 --- a/src/Protocol/ForgeHandshake.cpp +++ b/src/Protocol/ForgeHandshake.cpp @@ -36,8 +36,8 @@ namespace ClientPhase /** Server handshake state phases. */ namespace ServerPhase { - static const Int8 WAITINGCACK = 2; - static const Int8 COMPLETE = 3; + static const auto WAITINGCACK = std::byte(2); + static const auto COMPLETE = std::byte(3); } @@ -105,12 +105,12 @@ void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID & m_UUID = a_UUID; m_Properties = a_Properties; - static const std::array<AString, 5> Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; - AString ChannelsString; + static const std::array<std::string_view, 5> Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; + ContiguousByteBuffer ChannelsString; for (auto & Channel: Channels) { - ChannelsString.append(Channel); - ChannelsString.push_back('\0'); + ChannelsString.append({ reinterpret_cast<const std::byte *>(Channel.data()), Channel.size() }); + ChannelsString.push_back(std::byte(0)); } m_Client->SendPluginMessage("REGISTER", ChannelsString); @@ -123,7 +123,6 @@ void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID & void cForgeHandshake::SendServerHello() { - AString Message; cByteBuffer Buf(6); // Discriminator | Byte | Always 0 for ServerHello Buf.WriteBEInt8(Discriminator::ServerHello); @@ -131,6 +130,8 @@ void cForgeHandshake::SendServerHello() Buf.WriteBEInt8(2); // Dimension TODO Buf.WriteBEInt32(0); + + ContiguousByteBuffer Message; Buf.ReadAll(Message); m_Client->SendPluginMessage("FML|HS", Message); @@ -140,18 +141,18 @@ void cForgeHandshake::SendServerHello() -AStringMap cForgeHandshake::ParseModList(const char * a_Data, size_t a_Size) +AStringMap cForgeHandshake::ParseModList(const ContiguousByteBufferView a_Data) { AStringMap Mods; - if (a_Size < 4) + if (a_Data.size() < 4) { - SetError(Printf("ParseModList invalid packet, missing length (size = %zu)", a_Size)); + SetError(Printf("ParseModList invalid packet, missing length (size = %zu)", a_Data.size())); return Mods; } - cByteBuffer Buf(a_Size); - Buf.Write(a_Data, a_Size); + cByteBuffer Buf(a_Data.size()); + Buf.Write(a_Data.data(), a_Data.size()); UInt32 NumMods; if (!Buf.ReadVarInt32(NumMods)) { @@ -182,11 +183,11 @@ AStringMap cForgeHandshake::ParseModList(const char * a_Data, size_t a_Size) -void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { - if (a_Size == 2) + if (a_Data.size() == 2) { - int FmlProtocolVersion = a_Data[1]; + const auto FmlProtocolVersion = static_cast<Int8>(a_Data[1]); LOGD("Received ClientHello with FML protocol version %d", FmlProtocolVersion); if (FmlProtocolVersion != 2) { @@ -195,7 +196,7 @@ void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a } else { - SetError(Printf("Received unexpected length of ClientHello: %zu", a_Size)); + SetError(Printf("Received unexpected length of ClientHello: %zu", a_Data.size())); } } @@ -203,11 +204,11 @@ void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a -void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleModList(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { LOGD("Received ModList"); - auto ClientMods = ParseModList(a_Data + 1, a_Size - 1); + auto ClientMods = ParseModList(a_Data.substr(1)); AString ClientModsString; for (auto & item: ClientMods) { @@ -241,7 +242,8 @@ void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Dat Buf.WriteVarUTF8String(item.first); // name Buf.WriteVarUTF8String(item.second); // version } - AString ServerModList; + + ContiguousByteBuffer ServerModList; Buf.ReadAll(ServerModList); m_Client->SendPluginMessage("FML|HS", ServerModList); @@ -251,15 +253,15 @@ void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Dat -void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { - if (a_Size != 2) + if (a_Data.size() != 2) { - SetError(Printf("Unexpected HandshakeAck packet length: %zu", a_Size)); + SetError(Printf("Unexpected HandshakeAck packet length: %zu", a_Data.size())); return; } - auto Phase = a_Data[1]; + const auto Phase = static_cast<Int8>(a_Data[1]); LOGD("Received client HandshakeAck with phase = %d", Phase); switch (Phase) @@ -282,7 +284,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * Buf.WriteVarInt32(NumSubstitutions); Buf.WriteVarInt32(NumDummies); - AString RegistryData; + ContiguousByteBuffer RegistryData; Buf.ReadAll(RegistryData); m_Client->SendPluginMessage("FML|HS", RegistryData); break; @@ -292,8 +294,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * { LOGD("Client finished receiving registry data; acknowledging"); - AString Ack; - Ack.push_back(Discriminator::HandshakeAck); + ContiguousByteBuffer Ack; + Ack.push_back(std::byte(Discriminator::HandshakeAck)); Ack.push_back(ServerPhase::WAITINGCACK); m_Client->SendPluginMessage("FML|HS", Ack); break; @@ -303,8 +305,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * { LOGD("Client is pending completion; sending complete ack"); - AString Ack; - Ack.push_back(Discriminator::HandshakeAck); + ContiguousByteBuffer Ack; + Ack.push_back(std::byte(Discriminator::HandshakeAck)); Ack.push_back(ServerPhase::COMPLETE); m_Client->SendPluginMessage("FML|HS", Ack); @@ -320,7 +322,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * default: { - SetError(Printf("Received unknown phase in Forge handshake acknowledgement: %d", Phase)); + SetError(fmt::format("Received unknown phase in Forge handshake acknowledgement: {}", Phase)); break; } } @@ -330,11 +332,11 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * -void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::DataReceived(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { if (!m_IsForgeClient) { - SetError(Printf("Received unexpected Forge data from non-Forge client (%zu bytes)", a_Size)); + SetError(Printf("Received unexpected Forge data from non-Forge client (%zu bytes)", a_Data.size())); return; } if (m_Errored) @@ -343,19 +345,18 @@ void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data return; } - if (a_Size <= 1) + if (a_Data.size() <= 1) { - SetError(Printf("Received unexpectedly short Forge data (%zu bytes)", a_Size)); + SetError(Printf("Received unexpectedly short Forge data (%zu bytes)", a_Data.size())); return; } - auto Discriminator = a_Data[0]; - + const auto Discriminator = static_cast<Int8>(a_Data[0]); switch (Discriminator) { - case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data, a_Size); break; - case Discriminator::ModList: HandleModList(a_Client, a_Data, a_Size); break; - case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data, a_Size); break; + case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data); break; + case Discriminator::ModList: HandleModList(a_Client, a_Data); break; + case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data); break; default: { diff --git a/src/Protocol/ForgeHandshake.h b/src/Protocol/ForgeHandshake.h index 46e0efaa6..061369c15 100644 --- a/src/Protocol/ForgeHandshake.h +++ b/src/Protocol/ForgeHandshake.h @@ -34,7 +34,7 @@ public: void SendServerHello(); /** Process received data from the client advancing the Forge handshake. */ - void DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size); + void DataReceived(cClientHandle * a_Client, ContiguousByteBufferView a_Data); private: /** True if the Forge handshake is in an errored state. */ @@ -48,13 +48,13 @@ private: cUUID m_UUID; Json::Value m_Properties; - void HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size); - void HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size); - void HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size); + void HandleClientHello(cClientHandle * a_Client, ContiguousByteBufferView a_Data); + void HandleModList(cClientHandle * a_Client, ContiguousByteBufferView a_Data); + void HandleHandshakeAck(cClientHandle * a_Client, ContiguousByteBufferView a_Data); /** Set errored state to prevent further handshake message processing. */ void SetError(const AString & message); /** Parse the client ModList packet of installed Forge mods and versions. */ - AStringMap ParseModList(const char * a_Data, size_t a_Size); + AStringMap ParseModList(ContiguousByteBufferView a_Data); }; diff --git a/src/Protocol/Packetizer.h b/src/Protocol/Packetizer.h index 4ece6e4fa..f4d632a27 100644 --- a/src/Protocol/Packetizer.h +++ b/src/Protocol/Packetizer.h @@ -119,9 +119,9 @@ public: } - inline void WriteBuf(const char * a_Data, size_t a_Size) + inline void WriteBuf(const ContiguousByteBufferView a_Data) { - VERIFY(m_Out.Write(a_Data, a_Size)); + VERIFY(m_Out.Write(a_Data.data(), a_Data.size())); } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 5c9d5105f..341e0b0f4 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -365,7 +365,7 @@ public: virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0; virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 0; virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) = 0; - virtual void SendChunkData (const std::string_view a_ChunkData) = 0; + virtual void SendChunkData (ContiguousByteBufferView a_ChunkData) = 0; virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) = 0; @@ -405,7 +405,7 @@ public: virtual void SendPlayerMoveLook (void) = 0; virtual void SendPlayerPosition (void) = 0; virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; - virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; + virtual void SendPluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; virtual void SendResetTitle (void) = 0; virtual void SendResourcePack (const AString & a_ResourcePackUrl) = 0; @@ -468,7 +468,7 @@ protected: virtual Version GetProtocolVersion() = 0; /** A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it. */ - virtual void SendData(const char * a_Data, size_t a_Size) = 0; + virtual void SendData(ContiguousByteBufferView a_Data) = 0; /** Sends a single packet contained within the cPacketizer class. The cPacketizer's destructor calls this to send the contained packet; protocol may transform the data (compression in 1.8 etc). */ diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 5d146c46a..dc6b93b01 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -123,11 +123,6 @@ void cMultiVersionProtocol::HandleIncomingDataInRecognitionStage(cClientHandle & HandleIncomingDataInOldPingResponseStage(a_Clyent, a_In); }; } - catch (const std::exception & Oops) - { - a_Client.Kick(Oops.what()); - return; - } // Explicitly process any remaining data with the new handler: HandleIncomingData(a_Client, a_Data); @@ -341,15 +336,15 @@ void cMultiVersionProtocol::SendPacket(cClientHandle & a_Client, cByteBuffer & a // Compression doesn't apply to this state, send raw data: VERIFY(OutPacketLenBuffer.WriteVarInt32(PacketLen)); - AString LengthData; + ContiguousByteBuffer LengthData; OutPacketLenBuffer.ReadAll(LengthData); - a_Client.SendData(LengthData.data(), LengthData.size()); + a_Client.SendData(LengthData); // Send the packet's payload: - AString PacketData; + ContiguousByteBuffer PacketData; a_OutPacketBuffer.ReadAll(PacketData); a_OutPacketBuffer.CommitRead(); - a_Client.SendData(PacketData.data(), PacketData.size()); + a_Client.SendData(PacketData); } diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index 0efef360c..b3a3205d5 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -679,7 +679,7 @@ void cProtocol_1_10_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 05bf35562..ff0c34223 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -520,7 +520,7 @@ void cProtocol_1_11_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 93a1160ee..30a975d18 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -1045,7 +1045,7 @@ void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) { // TODO not yet used, not sure if it is needed // https://wiki.vg/index.php?title=Protocol&oldid=14204#Crafting_Book_Data - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); } @@ -1054,7 +1054,7 @@ void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) void cProtocol_1_12::HandlePacketAdvancementTab(cByteBuffer & a_ByteBuffer) { - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); m_Client->GetPlayer()->SendMessageInfo("The new advancements are not implemented."); } diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index 251f8cf55..4da065af7 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -298,13 +298,14 @@ void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) m_Client->SetClientBrand(Brand); // Send back our brand, including the length: - SendPluginMessage("minecraft:brand", "\x08""Cuberite"); + m_Client->SendPluginMessage("minecraft:brand", "\x08""Cuberite"); return; } + ContiguousByteBuffer Data; + // Read the plugin message and relay to clienthandle: - AString Data; - VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds + VERIFY(a_ByteBuffer.ReadSome(Data, a_ByteBuffer.GetReadableSpace())); // Always succeeds m_Client->HandlePluginMessage(Channel, Data); } @@ -694,8 +695,8 @@ bool cProtocol_1_13::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; @@ -1423,8 +1424,8 @@ bool cProtocol_1_13_2::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; diff --git a/src/Protocol/Protocol_1_14.cpp b/src/Protocol/Protocol_1_14.cpp index e015c6cd1..bc0e68d94 100644 --- a/src/Protocol/Protocol_1_14.cpp +++ b/src/Protocol/Protocol_1_14.cpp @@ -231,8 +231,7 @@ bool cProtocol_1_14::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketTyp // Game switch (a_PacketType) { - default: AString dum; a_ByteBuffer.ReadAll(dum); a_ByteBuffer.CommitRead(); a_ByteBuffer.Write(" ", 1); - return true; + default: a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); a_ByteBuffer.CommitRead(); return true; } } diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index d9d18b41a..12e2d441c 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -85,7 +85,6 @@ Implements the 1.8 protocol classes: const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... -const uLongf MAX_COMPRESSED_PACKET_LEN = 200 KiB; // Maximum size of compressed packets. static const UInt32 CompressionThreshold = 256; // After how large a packet should we compress it. @@ -178,7 +177,7 @@ void cProtocol_1_8_0::DataReceived(cByteBuffer & a_Buffer, const char * a_Data, { if (m_IsEncrypted) { - Byte Decrypted[512]; + std::byte Decrypted[512]; while (a_Size > 0) { size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; @@ -320,12 +319,12 @@ void cProtocol_1_8_0::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type -void cProtocol_1_8_0::SendChunkData(const std::string_view a_ChunkData) +void cProtocol_1_8_0::SendChunkData(const ContiguousByteBufferView a_ChunkData) { ASSERT(m_State == 3); // In game mode? cCSLock Lock(m_CSPacket); - SendData(a_ChunkData.data(), a_ChunkData.size()); + SendData(a_ChunkData); } @@ -1143,13 +1142,13 @@ void cProtocol_1_8_0::SendPlayerSpawn(const cPlayer & a_Player) -void cProtocol_1_8_0::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +void cProtocol_1_8_0::SendPluginMessage(const AString & a_Channel, const ContiguousByteBufferView a_Message) { ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, pktPluginMessage); Pkt.WriteString(a_Channel); - Pkt.WriteBuf(a_Message.data(), a_Message.size()); + Pkt.WriteBuf(a_Message); } @@ -1717,11 +1716,11 @@ void cProtocol_1_8_0::SendWindowProperty(const cWindow & a_Window, size_t a_Prop -bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_CompressedData) +void cProtocol_1_8_0::CompressPacket(CircularBufferCompressor & a_Packet, ContiguousByteBuffer & a_CompressedData) { - const auto UncompressedSize = a_Packet.size(); + const auto Uncompressed = a_Packet.GetView(); - if (UncompressedSize < CompressionThreshold) + if (Uncompressed.size() < CompressionThreshold) { /* Size doesn't reach threshold, not worth compressing. @@ -1734,7 +1733,7 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr ---------------------------------------------- */ const UInt32 DataSize = 0; - const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + UncompressedSize); + const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + Uncompressed.size()); cByteBuffer LengthHeaderBuffer( cByteBuffer::GetVarIntSize(PacketSize) + @@ -1744,14 +1743,14 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr LengthHeaderBuffer.WriteVarInt32(PacketSize); LengthHeaderBuffer.WriteVarInt32(DataSize); - AString LengthData; + ContiguousByteBuffer LengthData; LengthHeaderBuffer.ReadAll(LengthData); - a_CompressedData.reserve(LengthData.size() + UncompressedSize); - a_CompressedData.assign(LengthData.data(), LengthData.size()); - a_CompressedData.append(a_Packet); + a_CompressedData.reserve(LengthData.size() + Uncompressed.size()); + a_CompressedData = LengthData; + a_CompressedData += Uncompressed; - return true; + return; } /* Definitely worth compressing. @@ -1765,28 +1764,11 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr ---------------------------------------------- */ - // Compress the data: - char CompressedData[MAX_COMPRESSED_PACKET_LEN]; - - uLongf CompressedSize = compressBound(static_cast<uLongf>(a_Packet.size())); - if (CompressedSize >= MAX_COMPRESSED_PACKET_LEN) - { - ASSERT(!"Too high packet size."); - return false; - } - - if ( - compress2( - reinterpret_cast<Bytef *>(CompressedData), &CompressedSize, - reinterpret_cast<const Bytef *>(a_Packet.data()), static_cast<uLongf>(a_Packet.size()), Z_DEFAULT_COMPRESSION - ) != Z_OK - ) - { - return false; - } + const auto CompressedData = a_Packet.Compress(); + const auto Compressed = CompressedData.GetView(); - const UInt32 DataSize = static_cast<UInt32>(UncompressedSize); - const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + CompressedSize); + const UInt32 DataSize = static_cast<UInt32>(Uncompressed.size()); + const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + Compressed.size()); cByteBuffer LengthHeaderBuffer( cByteBuffer::GetVarIntSize(PacketSize) + @@ -1796,14 +1778,12 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr LengthHeaderBuffer.WriteVarInt32(PacketSize); LengthHeaderBuffer.WriteVarInt32(DataSize); - AString LengthData; + ContiguousByteBuffer LengthData; LengthHeaderBuffer.ReadAll(LengthData); - a_CompressedData.reserve(LengthData.size() + CompressedSize); - a_CompressedData.assign(LengthData.data(), LengthData.size()); - a_CompressedData.append(CompressedData, CompressedSize); - - return true; + a_CompressedData.reserve(LengthData.size() + Compressed.size()); + a_CompressedData = LengthData; + a_CompressedData += Compressed; } @@ -1947,7 +1927,7 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat { if (a_Buffer.GetReadableSpace() > 0) { - AString AllData; + ContiguousByteBuffer AllData; size_t OldReadableSpace = a_Buffer.GetReadableSpace(); a_Buffer.ReadAll(AllData); a_Buffer.ResetRead(); @@ -1992,12 +1972,11 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat } // Check packet for compression: - UInt32 UncompressedSize = 0; - AString UncompressedData; if (m_State == 3) { UInt32 NumBytesRead = static_cast<UInt32>(a_Buffer.GetReadableSpace()); + UInt32 UncompressedSize; if (!a_Buffer.ReadVarInt(UncompressedSize)) { m_Client->Kick("Compression packet incomplete"); @@ -2011,113 +1990,35 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat if (UncompressedSize > 0) { // Decompress the data: - AString CompressedData; - VERIFY(a_Buffer.ReadString(CompressedData, PacketLen)); - if (InflateString(CompressedData.data(), PacketLen, UncompressedData) != Z_OK) - { - m_Client->Kick("Compression failure"); - return; - } - PacketLen = static_cast<UInt32>(UncompressedData.size()); - if (PacketLen != UncompressedSize) - { - m_Client->Kick("Wrong uncompressed packet size given"); - return; - } - } - } - - // Move the packet payload to a separate cByteBuffer, bb: - cByteBuffer bb(PacketLen + 1); - if (UncompressedSize == 0) - { - // No compression was used, move directly - VERIFY(a_Buffer.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen))); - } - else - { - // Compression was used, move the uncompressed data: - VERIFY(bb.Write(UncompressedData.data(), UncompressedData.size())); - } - a_Buffer.CommitRead(); + m_Extractor.ReadFrom(a_Buffer, PacketLen); + a_Buffer.CommitRead(); - UInt32 PacketType; - if (!bb.ReadVarInt(PacketType)) - { - // Not enough data - break; - } - - // Write one NUL extra, so that we can detect over-reads - bb.Write("\0", 1); + const auto UncompressedData = m_Extractor.Extract(UncompressedSize); + const auto Uncompressed = UncompressedData.GetView(); + cByteBuffer bb(Uncompressed.size()); - // Log the packet info into the comm log file: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - AString PacketData; - bb.ReadAll(PacketData); - bb.ResetRead(); - bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again - ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read - PacketData.resize(PacketData.size() - 1); - AString PacketDataHex; - CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16); - m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n", - PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str() - ); - } + // Compression was used, move the uncompressed data: + VERIFY(bb.Write(Uncompressed.data(), Uncompressed.size())); - if (!HandlePacket(bb, PacketType)) - { - // Unknown packet, already been reported, but without the length. Log the length here: - LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen); - - #ifdef _DEBUG - // Dump the packet contents into the log: - bb.ResetRead(); - AString Packet; - bb.ReadAll(Packet); - Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection - AString Out; - CreateHexDump(Out, Packet.data(), Packet.size(), 24); - LOGD("Packet contents:\n%s", Out.c_str()); - #endif // _DEBUG - - // Put a message in the comm log: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n"); + HandlePacket(bb); + continue; } - - return; } - // The packet should have 1 byte left in the buffer - the NUL we had added - if (bb.GetReadableSpace() != 1) - { - // Read more or less than packet length, report as error - LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read %zu bytes, packet contained %u bytes", - PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen - ); + // Move the packet payload to a separate cByteBuffer, bb: + cByteBuffer bb(PacketLen); - // Put a message in the comm log: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %zu left) ^^^^^^\n\n\n", - 1, bb.GetReadableSpace() - ); - m_CommLogFile.Flush(); - } + // No compression was used, move directly: + VERIFY(a_Buffer.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen))); + a_Buffer.CommitRead(); - ASSERT(!"Read wrong number of bytes!"); - m_Client->PacketError(PacketType); - } + HandlePacket(bb); } // for (ever) // Log any leftover bytes into the logfile: if (g_ShouldLogCommIn && (a_Buffer.GetReadableSpace() > 0) && m_CommLogFile.IsOpen()) { - AString AllData; + ContiguousByteBuffer AllData; size_t OldReadableSpace = a_Buffer.GetReadableSpace(); a_Buffer.ReadAll(AllData); a_Buffer.ResetRead(); @@ -2367,8 +2268,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu { return; } - AString EncKey; - if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength)) + ContiguousByteBuffer EncKey; + if (!a_ByteBuffer.ReadSome(EncKey, EncKeyLength)) { return; } @@ -2376,8 +2277,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu { return; } - AString EncNonce; - if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) + ContiguousByteBuffer EncNonce; + if (!a_ByteBuffer.ReadSome(EncNonce, EncNonceLength)) { return; } @@ -2391,7 +2292,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu // Decrypt EncNonce using privkey cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); UInt32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; - int res = rsaDecryptor.Decrypt(reinterpret_cast<const Byte *>(EncNonce.data()), EncNonce.size(), reinterpret_cast<Byte *>(DecryptedNonce), sizeof(DecryptedNonce)); + int res = rsaDecryptor.Decrypt(EncNonce, reinterpret_cast<Byte *>(DecryptedNonce), sizeof(DecryptedNonce)); if (res != 4) { LOGD("Bad nonce length: got %d, exp %d", res, 4); @@ -2407,7 +2308,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu // Decrypt the symmetric encryption key using privkey: Byte DecryptedKey[MAX_ENC_LEN]; - res = rsaDecryptor.Decrypt(reinterpret_cast<const Byte *>(EncKey.data()), EncKey.size(), DecryptedKey, sizeof(DecryptedKey)); + res = rsaDecryptor.Decrypt(EncKey, DecryptedKey, sizeof(DecryptedKey)); if (res != 16) { LOGD("Bad key length"); @@ -2416,7 +2317,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu } StartEncryption(DecryptedKey); - m_Client->HandleLogin(m_Client->GetUsername()); + m_Client->HandleLogin(); } @@ -2438,22 +2339,22 @@ void cProtocol_1_8_0::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer) return; } - cServer * Server = cRoot::Get()->GetServer(); + m_Client->SetUsername(std::move(Username)); + // If auth is required, then send the encryption request: - if (Server->ShouldAuthenticate()) + if (const auto Server = cRoot::Get()->GetServer(); Server->ShouldAuthenticate()) { cPacketizer Pkt(*this, pktEncryptionRequest); Pkt.WriteString(Server->GetServerID()); - const AString & PubKeyDer = Server->GetPublicKeyDER(); + const auto PubKeyDer = Server->GetPublicKeyDER(); Pkt.WriteVarInt32(static_cast<UInt32>(PubKeyDer.size())); - Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size()); + Pkt.WriteBuf(PubKeyDer); Pkt.WriteVarInt32(4); Pkt.WriteBEInt32(static_cast<int>(reinterpret_cast<intptr_t>(this))); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) - m_Client->SetUsername(Username); return; } - m_Client->HandleLogin(Username); + m_Client->HandleLogin(); } @@ -2704,20 +2605,20 @@ void cProtocol_1_8_0::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) HandleVanillaPluginMessage(a_ByteBuffer, Channel); // Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692): - if (a_ByteBuffer.GetReadableSpace() > 1) + if (a_ByteBuffer.GetReadableSpace() > 0) { LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes", - static_cast<unsigned>(a_ByteBuffer.GetReadableSpace() - 1) + static_cast<unsigned>(a_ByteBuffer.GetReadableSpace()) ); - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); } return; } // Read the plugin message and relay to clienthandle: - AString Data; - VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds + ContiguousByteBuffer Data; + VERIFY(a_ByteBuffer.ReadSome(Data, a_ByteBuffer.GetReadableSpace())); // Always succeeds m_Client->HandlePluginMessage(Channel, Data); } @@ -2976,7 +2877,7 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand); m_Client->SetClientBrand(Brand); // Send back our brand, including the length: - SendPluginMessage("MC|Brand", "\x08""Cuberite"); + m_Client->SendPluginMessage("MC|Brand", "\x08""Cuberite"); return; } else if (a_Channel == "MC|Beacon") @@ -3001,8 +2902,8 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str()); // Read the payload and send it through to the clienthandle: - AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1)); + ContiguousByteBuffer Message; + VERIFY(a_ByteBuffer.ReadSome(Message, a_ByteBuffer.GetReadableSpace() - 1)); m_Client->HandlePluginMessage(a_Channel, Message); } @@ -3010,23 +2911,24 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con -void cProtocol_1_8_0::SendData(const char * a_Data, size_t a_Size) +void cProtocol_1_8_0::SendData(ContiguousByteBufferView a_Data) { if (m_IsEncrypted) { - Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) - while (a_Size > 0) + std::byte Encrypted[8 KiB]; // Larger buffer, we may be sending lots of data (chunks) + + while (a_Data.size() > 0) { - size_t NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, reinterpret_cast<const Byte *>(a_Data), NumBytes); - m_Client->SendData(reinterpret_cast<const char *>(Encrypted), NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; + const auto NumBytes = (a_Data.size() > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Data.size(); + m_Encryptor.ProcessData(Encrypted, a_Data.data(), NumBytes); + m_Client->SendData({ Encrypted, NumBytes }); + + a_Data = a_Data.substr(NumBytes); } } else { - m_Client->SendData(a_Data, a_Size); + m_Client->SendData(a_Data); } } @@ -3054,8 +2956,8 @@ bool cProtocol_1_8_0::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_ a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; @@ -3069,10 +2971,10 @@ bool cProtocol_1_8_0::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_ -void cProtocol_1_8_0::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +void cProtocol_1_8_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) { // Parse into NBT: - cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); + cParsedNBT NBT(a_Metadata); if (!NBT.IsValid()) { AString HexDump; @@ -3189,33 +3091,34 @@ eBlockFace cProtocol_1_8_0::FaceIntToBlockFace(const Int32 a_BlockFace) void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt) { - UInt32 PacketLen = static_cast<UInt32>(m_OutPacketBuffer.GetUsedSpace()); - AString PacketData, CompressedPacket; - m_OutPacketBuffer.ReadAll(PacketData); + ASSERT(m_OutPacketBuffer.GetReadableSpace() == m_OutPacketBuffer.GetUsedSpace()); + + m_Compressor.ReadFrom(m_OutPacketBuffer); m_OutPacketBuffer.CommitRead(); + const auto PacketData = m_Compressor.GetView(); + if (m_State == 3) { + ContiguousByteBuffer CompressedPacket; + // Compress the packet payload: - if (!cProtocol_1_8_0::CompressPacket(PacketData, CompressedPacket)) - { - return; - } + cProtocol_1_8_0::CompressPacket(m_Compressor, CompressedPacket); // Send the packet's payload compressed: - SendData(CompressedPacket.data(), CompressedPacket.size()); + SendData(CompressedPacket); } else { // Compression doesn't apply to this state, send raw data: - m_OutPacketLenBuffer.WriteVarInt32(PacketLen); - AString LengthData; + m_OutPacketLenBuffer.WriteVarInt32(static_cast<UInt32>(PacketData.size())); + ContiguousByteBuffer LengthData; m_OutPacketLenBuffer.ReadAll(LengthData); - SendData(LengthData.data(), LengthData.size()); + m_OutPacketLenBuffer.CommitRead(); + SendData(LengthData); // Send the packet's payload directly: - m_OutPacketLenBuffer.CommitRead(); - SendData(PacketData.data(), PacketData.size()); + SendData(PacketData); } // Log the comm into logfile: @@ -3226,7 +3129,7 @@ void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt) CreateHexDump(Hex, PacketData.data(), PacketData.size(), 16); m_CommLogFile.Printf("Outgoing packet: type %s (translated to 0x%02x), length %u (0x%04x), state %d. Payload (incl. type):\n%s\n", cPacketizer::PacketTypeToStr(a_Pkt.GetPacketType()), GetPacketID(a_Pkt.GetPacketType()), - PacketLen, PacketLen, m_State, Hex + PacketData.size(), PacketData.size(), m_State, Hex ); /* // Useful for debugging a new protocol: @@ -3346,13 +3249,13 @@ void cProtocol_1_8_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) } Writer.Finish(); - AString Result = Writer.GetResult(); - if (Result.size() == 0) + const auto Result = Writer.GetResult(); + if (Result.empty()) { a_Pkt.WriteBEInt8(0); return; } - a_Pkt.WriteBuf(Result.data(), Result.size()); + a_Pkt.WriteBuf(Result); } @@ -3479,7 +3382,7 @@ void cProtocol_1_8_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } @@ -3978,6 +3881,82 @@ void cProtocol_1_8_0::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & +void cProtocol_1_8_0::HandlePacket(cByteBuffer & a_Buffer) +{ + UInt32 PacketType; + if (!a_Buffer.ReadVarInt(PacketType)) + { + // Not enough data + return; + } + + // Log the packet info into the comm log file: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + ContiguousByteBuffer PacketData; + a_Buffer.ReadAll(PacketData); + a_Buffer.ResetRead(); + a_Buffer.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again + ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read + PacketData.resize(PacketData.size() - 1); + AString PacketDataHex; + CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16); + m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n", + PacketType, PacketType, a_Buffer.GetUsedSpace(), a_Buffer.GetUsedSpace(), m_State, PacketDataHex.c_str() + ); + } + + if (!HandlePacket(a_Buffer, PacketType)) + { + // Unknown packet, already been reported, but without the length. Log the length here: + LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, a_Buffer.GetUsedSpace()); + +#ifdef _DEBUG + // Dump the packet contents into the log: + a_Buffer.ResetRead(); + ContiguousByteBuffer Packet; + a_Buffer.ReadAll(Packet); + Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection + AString Out; + CreateHexDump(Out, Packet.data(), Packet.size(), 24); + LOGD("Packet contents:\n%s", Out.c_str()); +#endif // _DEBUG + + // Put a message in the comm log: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n"); + } + + return; + } + + // The packet should have nothing left in the buffer: + if (a_Buffer.GetReadableSpace() != 0) + { + // Read more or less than packet length, report as error + LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read %zu bytes, packet contained %u bytes", + PacketType, m_State, a_Buffer.GetUsedSpace() - a_Buffer.GetReadableSpace(), a_Buffer.GetUsedSpace() + ); + + // Put a message in the comm log: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %zu left) ^^^^^^\n\n\n", + 1, a_Buffer.GetReadableSpace() + ); + m_CommLogFile.Flush(); + } + + ASSERT(!"Read wrong number of bytes!"); + m_Client->PacketError(PacketType); + } +} + + + + + void cProtocol_1_8_0::SendEntityTeleport(const cEntity & a_Entity) { cPacketizer Pkt(*this, pktTeleportEntity); diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index 44415758f..f5800cb21 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -20,6 +20,9 @@ Declares the 1.8 protocol classes: #include "../mbedTLS++/AesCfb128Decryptor.h" #include "../mbedTLS++/AesCfb128Encryptor.h" +#include "CircularBufferCompressor.h" +#include "StringCompression.h" + @@ -46,7 +49,7 @@ public: virtual void SendChat (const AString & a_Message, eChatType a_Type) override; virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override; virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) override; - virtual void SendChunkData (const std::string_view a_ChunkData) override; + virtual void SendChunkData (ContiguousByteBufferView a_ChunkData) override; virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override; @@ -88,7 +91,7 @@ public: virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; + virtual void SendPluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendResetTitle (void) override; virtual void SendResourcePack (const AString & a_ResourcePackUrl) override; @@ -125,9 +128,8 @@ public: virtual AString GetAuthServerID(void) override { return m_AuthServerID; } /** Compress the packet. a_Packet must be without packet length. - a_Compressed will be set to the compressed packet includes packet length and data length. - If compression fails, the function returns false. */ - static bool CompressPacket(const AString & a_Packet, AString & a_Compressed); + a_Compressed will be set to the compressed packet includes packet length and data length. */ + static void CompressPacket(CircularBufferCompressor & a_Packet, ContiguousByteBuffer & a_Compressed); /** The 1.8 protocol use a particle id instead of a string. This function converts the name to the id. If the name is incorrect, it returns 0. */ static int GetParticleID(const AString & a_ParticleName); @@ -194,7 +196,7 @@ protected: virtual void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel); /** Sends the data to the client, encrypting them if needed. */ - virtual void SendData(const char * a_Data, size_t a_Size) override; + virtual void SendData(ContiguousByteBufferView a_Size) override; /** Sends the packet to the client. Called by the cPacketizer's destructor. */ virtual void SendPacket(cPacketizer & a_Packet) override; @@ -205,7 +207,7 @@ protected: virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes = 0); /** Parses item metadata as read by ReadItem(), into the item enchantments. */ - virtual void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + virtual void ParseItemMetadata(cItem & a_Item, ContiguousByteBufferView a_Metadata); virtual void StartEncryption(const Byte * a_Key); @@ -242,9 +244,15 @@ private: cAesCfb128Decryptor m_Decryptor; cAesCfb128Encryptor m_Encryptor; + CircularBufferCompressor m_Compressor; + CircularBufferExtractor m_Extractor; + /** The logfile where the comm is logged, when g_ShouldLogComm is true */ cFile m_CommLogFile; + /** Handle a complete packet stored in the given buffer. */ + void HandlePacket(cByteBuffer & a_Buffer); + /** Sends an entity teleport packet. Mitigates a 1.8 bug where the position in the entity spawn packet is ignored, and so entities don't show up until a teleport is sent. */ diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 72d3fd4b1..67e920925 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -1031,10 +1031,10 @@ void cProtocol_1_9_0::HandlePacketWindowClick(cByteBuffer & a_ByteBuffer) -void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) { // Parse into NBT: - cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); + cParsedNBT NBT(a_Metadata); if (!NBT.IsValid()) { AString HexDump; @@ -1438,13 +1438,13 @@ void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) Writer.Finish(); - AString Result = Writer.GetResult(); - if (Result.size() == 0) + const auto Result = Writer.GetResult(); + if (Result.empty()) { a_Pkt.WriteBEInt8(0); return; } - a_Pkt.WriteBuf(Result.data(), Result.size()); + a_Pkt.WriteBuf(Result); } @@ -1550,7 +1550,7 @@ void cProtocol_1_9_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } @@ -2289,7 +2289,7 @@ void cProtocol_1_9_4::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, c Writer.AddString("Text4", JsonUtils::WriteFastString(Line4)); Writer.Finish(); - Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index 0339ddeb3..27b005bd4 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -98,7 +98,7 @@ protected: virtual void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer) override; /** Parses item metadata as read by ReadItem(), into the item enchantments. */ - virtual void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) override; + virtual void ParseItemMetadata(cItem & a_Item, ContiguousByteBufferView a_Metadata) override; /** Converts the hand parameter received by the protocol into eHand constants. If the received value doesn't match any of the know value, raise an assertion fail or return hMain. */ diff --git a/src/Server.cpp b/src/Server.cpp index 114c38946..eb8b3de36 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -24,11 +24,6 @@ #include <sstream> #include <iostream> -extern "C" -{ - #include "zlib/zlib.h" -} - diff --git a/src/Server.h b/src/Server.h index 5ac7fc998..d9d3a09af 100644 --- a/src/Server.h +++ b/src/Server.h @@ -133,7 +133,7 @@ public: const AString & GetFaviconData(void) const { return m_FaviconData; } cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } - const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } + ContiguousByteBufferView GetPublicKeyDER(void) const { return m_PublicKeyDER; } /** Returns true if authentication has been turned on in server settings. */ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export @@ -214,7 +214,7 @@ private: cRsaPrivateKey m_PrivateKey; /** Public key for m_PrivateKey, ASN1-DER-encoded */ - AString m_PublicKeyDER; + ContiguousByteBuffer m_PublicKeyDER; cRCONServer m_RCONServer; diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp index ff434bbb1..6678fe1bd 100644 --- a/src/StringCompression.cpp +++ b/src/StringCompression.cpp @@ -1,242 +1,251 @@ // StringCompression.cpp -// Implements the wrapping functions for compression and decompression using AString as their data +// Implements the wrapping functions for compression and decompression #include "Globals.h" +#include "ByteBuffer.h" #include "StringCompression.h" +#include <libdeflate.h> -int CompressString(const char * a_Data, size_t a_Length, AString & a_Compressed, int a_Factor) + +std::string_view Compression::Result::GetStringView() const { - uLongf CompressedSize = compressBound(static_cast<uLong>(a_Length)); - - // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer! - // It saves us one allocation and one memcpy of the entire compressed data - // It may not work on some STL implementations! (Confirmed working on all currently used MSVC, GCC and Clang versions) - a_Compressed.resize(CompressedSize); - int errorcode = compress2(reinterpret_cast<Bytef *>(const_cast<char *>(a_Compressed.data())), &CompressedSize, reinterpret_cast<const Bytef *>(a_Data), static_cast<uLong>(a_Length), a_Factor); - if (errorcode != Z_OK) + const auto View = GetView(); + return { reinterpret_cast<const char *>(View.data()), View.size() }; +} + + + + + +ContiguousByteBufferView Compression::Result::GetView() const +{ + // Get a generic std::byte * to what the variant is currently storing: + return { - return errorcode; - } - a_Compressed.resize(CompressedSize); - return Z_OK; + std::visit([](const auto & Buffer) -> const std::byte * + { + using Variant = std::decay_t<decltype(Buffer)>; + + if constexpr (std::is_same_v<Variant, Compression::Result::Static>) + { + return Buffer.data(); + } + else + { + return Buffer.get(); + } + }, Storage), Size + }; } -int UncompressString(const char * a_Data, size_t a_Length, AString & a_Uncompressed, size_t a_UncompressedSize) +Compression::Compressor::Compressor(int CompressionFactor) { - // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer! - // It saves us one allocation and one memcpy of the entire compressed data - // It may not work on some STL implementations! (Confirmed working on all currently used MSVC, GCC and Clang versions) - a_Uncompressed.resize(a_UncompressedSize); - uLongf UncompressedSize = static_cast<uLongf>(a_UncompressedSize); // On some architectures the uLongf is different in size to int, that may be the cause of the -5 error - int errorcode = uncompress(reinterpret_cast<Bytef *>(const_cast<char *>(a_Uncompressed.data())), &UncompressedSize, reinterpret_cast<const Bytef *>(a_Data), static_cast<uLong>(a_Length)); - if (errorcode != Z_OK) + m_Handle = libdeflate_alloc_compressor(CompressionFactor); + + if (m_Handle == nullptr) { - return errorcode; + throw std::bad_alloc(); } - a_Uncompressed.resize(UncompressedSize); - return Z_OK; } -int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Compressed) +Compression::Compressor::~Compressor() { - // Compress a_Data into a_Compressed using GZIP; return Z_XXX error constants same as zlib's compress2() + libdeflate_free_compressor(m_Handle); +} + + - a_Compressed.reserve(a_Length); - char Buffer[64 KiB]; - z_stream strm; - memset(&strm, 0, sizeof(strm)); - strm.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(a_Data)); - strm.avail_in = static_cast<uInt>(a_Length); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - int res = deflateInit2(&strm, 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY); - if (res != Z_OK) +template <auto Algorithm> +Compression::Result Compression::Compressor::Compress(const void * const Input, const size_t Size) +{ + // First see if the stack buffer has enough space: { - LOG("%s: compression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - return res; + Result::Static Buffer; + const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Buffer.data(), Buffer.size()); + + if (BytesWrittenOut != 0) + { + return { Buffer, BytesWrittenOut }; + } } - for (;;) + // No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2. + // This will either succeed, or except with bad_alloc. + + auto DynamicCapacity = Result::StaticCapacity * 2; + while (true) { - res = deflate(&strm, Z_FINISH); - switch (res) + auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity); + const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Dynamic.get(), DynamicCapacity); + + if (BytesWrittenOut != 0) { - case Z_OK: - { - // Some data has been compressed. Consume the buffer and continue compressing - a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - if (strm.avail_in == 0) - { - // All data has been compressed - deflateEnd(&strm); - return Z_OK; - } - break; - } + return { std::move(Dynamic), BytesWrittenOut }; + } - case Z_STREAM_END: - { - // Finished compressing. Consume the rest of the buffer and return - a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - deflateEnd(&strm); - return Z_OK; - } + DynamicCapacity *= 2; + } +} - default: - { - // An error has occurred, log it and return the error value - LOG("%s: compression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - deflateEnd(&strm); - return res; - } - } // switch (res) - } // while (true) + + + + +Compression::Result Compression::Compressor::CompressGZip(const ContiguousByteBufferView Input) +{ + return Compress<&libdeflate_gzip_compress>(Input.data(), Input.size()); } -extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed) +Compression::Result Compression::Compressor::CompressZLib(const ContiguousByteBufferView Input) { - // Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib + return Compress<&libdeflate_zlib_compress>(Input.data(), Input.size()); +} - a_Uncompressed.reserve(a_Length); - char Buffer[64 KiB]; - z_stream strm; - memset(&strm, 0, sizeof(strm)); - strm.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(a_Data)); - strm.avail_in = static_cast<uInt>(a_Length); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - int res = inflateInit2(&strm, 31); // Force GZIP decoding - if (res != Z_OK) + + +Compression::Result Compression::Compressor::CompressZLib(const void * const Input, const size_t Size) +{ + return Compress<&libdeflate_zlib_compress>(Input, Size); +} + + + + + +Compression::Extractor::Extractor() +{ + m_Handle = libdeflate_alloc_decompressor(); + + if (m_Handle == nullptr) { - LOG("%s: uncompression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - return res; + throw std::bad_alloc(); } +} - for (;;) - { - res = inflate(&strm, Z_NO_FLUSH); - switch (res) - { - case Z_OK: - { - // Some data has been uncompressed. Consume the buffer and continue uncompressing - a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - if (strm.avail_in == 0) - { - // All data has been uncompressed - inflateEnd(&strm); - return Z_OK; - } - break; - } - case Z_STREAM_END: - { - // Finished uncompressing. Consume the rest of the buffer and return - a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - inflateEnd(&strm); - return Z_OK; - } - default: - { - // An error has occurred, log it and return the error value - LOG("%s: uncompression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - inflateEnd(&strm); - return res; - } - } // switch (res) - } // while (true) + + +Compression::Extractor::~Extractor() +{ + libdeflate_free_decompressor(m_Handle); +} + + + + + +Compression::Result Compression::Extractor::ExtractGZip(ContiguousByteBufferView Input) +{ + return Extract<&libdeflate_gzip_decompress>(Input); +} + + + + + +Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input) +{ + return Extract<&libdeflate_zlib_decompress>(Input); } -extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed) +Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input, size_t UncompressedSize) { - a_Uncompressed.reserve(a_Length); - - char Buffer[64 KiB]; - z_stream strm; - memset(&strm, 0, sizeof(strm)); - strm.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(a_Data)); - strm.avail_in = static_cast<uInt>(a_Length); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - - int res = inflateInit(&strm); // Force GZIP decoding - if (res != Z_OK) + return Extract<&libdeflate_zlib_decompress>(Input, UncompressedSize); +} + + + + + +template <auto Algorithm> +Compression::Result Compression::Extractor::Extract(const ContiguousByteBufferView Input) +{ + // First see if the stack buffer has enough space: { - LOG("%s: inflation initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - return res; + Result::Static Buffer; + size_t BytesWrittenOut; + + switch (Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), Buffer.size(), &BytesWrittenOut)) + { + case LIBDEFLATE_SUCCESS: return { Buffer, BytesWrittenOut }; + case LIBDEFLATE_INSUFFICIENT_SPACE: break; + default: throw std::runtime_error("Data extraction failed."); + } } - for (;;) + // No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2. + + auto DynamicCapacity = Result::StaticCapacity * 2; + while (true) { - res = inflate(&strm, Z_NO_FLUSH); - switch (res) + size_t BytesWrittenOut; + auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity); + + switch (Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), DynamicCapacity, &BytesWrittenOut)) { - case Z_OK: + case libdeflate_result::LIBDEFLATE_SUCCESS: return { std::move(Dynamic), BytesWrittenOut }; + case libdeflate_result::LIBDEFLATE_INSUFFICIENT_SPACE: { - // Some data has been uncompressed. Consume the buffer and continue uncompressing - a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - strm.next_out = reinterpret_cast<Bytef *>(Buffer); - strm.avail_out = sizeof(Buffer); - if (strm.avail_in == 0) - { - // All data has been uncompressed - inflateEnd(&strm); - return Z_OK; - } - break; + DynamicCapacity *= 2; + continue; } + default: throw std::runtime_error("Data extraction failed."); + } + } +} - case Z_STREAM_END: - { - // Finished uncompressing. Consume the rest of the buffer and return - a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); - inflateEnd(&strm); - return Z_OK; - } - default: - { - // An error has occurred, log it and return the error value - LOG("%s: inflation failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); - inflateEnd(&strm); - return res; - } - } // switch (res) - } // while (true) -} +template <auto Algorithm> +Compression::Result Compression::Extractor::Extract(const ContiguousByteBufferView Input, size_t UncompressedSize) +{ + // Here we have the expected size after extraction, so directly use a suitable buffer size: + if (UncompressedSize <= Result::StaticCapacity) + { + if ( + Result::Static Buffer; + Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS + ) + { + return { Buffer, UncompressedSize }; + } + } + else if ( + auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(UncompressedSize); + Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS + ) + { + return { std::move(Dynamic), UncompressedSize }; + } + throw std::runtime_error("Data extraction failed."); +} diff --git a/src/StringCompression.h b/src/StringCompression.h index 10c00a804..c091c27a0 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -1,27 +1,85 @@ // StringCompression.h -// Interfaces to the wrapping functions for compression and decompression using AString as their data +// Interfaces to the wrapping functions for compression and decompression -#include "zlib/zlib.h" // Needed for the Z_XXX return values +#pragma once -/** Compresses a_Data into a_Compressed using ZLIB; returns Z_XXX error constants same as zlib's compress2() */ -extern int CompressString(const char * a_Data, size_t a_Length, AString & a_Compressed, int a_Factor); +class cByteBuffer; -/** Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress() */ -extern int UncompressString(const char * a_Data, size_t a_Length, AString & a_Uncompressed, size_t a_UncompressedSize); +struct libdeflate_compressor; +struct libdeflate_decompressor; -/** Compresses a_Data into a_Compressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib */ -extern int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Compressed); -/** Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib */ -extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed); -/** Uncompresses a_Data into a_Uncompressed using Inflate; returns Z_OK for success or Z_XXX error constants same as zlib */ -extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed); +namespace Compression +{ + /** Contains the result of a compression or extraction operation. */ + struct Result + { + using Static = std::array<std::byte, 128 KiB>; + using Dynamic = std::unique_ptr<std::byte[]>; + + static constexpr size_t StaticCapacity = sizeof(Compression::Result::Static) / sizeof(Compression::Result::Static::value_type); + + /** Returns a view (of type char) of the internal store. */ + std::string_view GetStringView() const; + + /** Returns a view (of type std::byte) of the internal store. */ + ContiguousByteBufferView GetView() const; + + /** A store allocated on either the stack or heap. */ + std::variant<Static, Dynamic> Storage; + + /** The length of valid data in the store. */ + size_t Size; + }; + + /** Contains routines for data compression. */ + class Compressor + { + public: + + /** Creates a new compressor instance with a compression factor [0-12]. */ + Compressor(int CompressionFactor = 6); + ~Compressor(); + + Result CompressGZip(ContiguousByteBufferView Input); + Result CompressZLib(ContiguousByteBufferView Input); + Result CompressZLib(const void * Input, size_t Size); + + private: + + template <auto Algorithm> + Result Compress(const void * Input, size_t Size); + + libdeflate_compressor * m_Handle; + }; + + /** Contains routines for data extraction. */ + class Extractor + { + public: + + /** Creates a new extractor instance. */ + Extractor(); + ~Extractor(); + + Result ExtractGZip(ContiguousByteBufferView Input); + Result ExtractZLib(ContiguousByteBufferView Input); + Result ExtractZLib(ContiguousByteBufferView Input, size_t UncompressedSize); + + private: + + template <auto Algorithm> Result Extract(ContiguousByteBufferView Input); + template <auto Algorithm> Result Extract(ContiguousByteBufferView Input, size_t UncompressedSize); + + libdeflate_decompressor * m_Handle; + }; +} diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index e6fbcc6fe..c55456e24 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -972,10 +972,12 @@ AString Base64Encode(const AString & a_Input) -short GetBEShort(const char * a_Mem) +short GetBEShort(const std::byte * const a_Mem) { - const Byte * Bytes = reinterpret_cast<const Byte *>(a_Mem); - return static_cast<short>((Bytes[0] << 8) | Bytes[1]); + return static_cast<short>( + (static_cast<short>(a_Mem[0]) << 8) | + static_cast<short>(a_Mem[1]) + ); } @@ -992,22 +994,26 @@ unsigned short GetBEUShort(const char * a_Mem) -int GetBEInt(const char * a_Mem) +int GetBEInt(const std::byte * const a_Mem) { - const Byte * Bytes = reinterpret_cast<const Byte *>(a_Mem); - return (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | Bytes[3]; + return + (static_cast<int>(a_Mem[0]) << 24) | + (static_cast<int>(a_Mem[1]) << 16) | + (static_cast<int>(a_Mem[2]) << 8) | + static_cast<int>(a_Mem[3]) + ; } -void SetBEInt(char * a_Mem, Int32 a_Value) +void SetBEInt(std::byte * a_Mem, Int32 a_Value) { - a_Mem[0] = a_Value >> 24; - a_Mem[1] = static_cast<char>((a_Value >> 16) & 0xff); - a_Mem[2] = static_cast<char>((a_Value >> 8) & 0xff); - a_Mem[3] = static_cast<char>(a_Value & 0xff); + a_Mem[0] = std::byte(a_Value >> 24); + a_Mem[1] = std::byte((a_Value >> 16) & 0xff); + a_Mem[2] = std::byte((a_Value >> 8) & 0xff); + a_Mem[3] = std::byte(a_Value & 0xff); } diff --git a/src/StringUtils.h b/src/StringUtils.h index a8cd41090..94e44c3f6 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -126,16 +126,16 @@ extern AString Base64Decode(const AString & a_Base64String); // Exported manual extern AString Base64Encode(const AString & a_Input); // Exported manually due to embedded NULs and extra parameter /** Reads two bytes from the specified memory location and interprets them as BigEndian short */ -extern short GetBEShort(const char * a_Mem); +extern short GetBEShort(const std::byte * a_Mem); /** Reads two bytes from the specified memory location and interprets them as BigEndian unsigned short */ extern unsigned short GetBEUShort(const char * a_Mem); /** Reads four bytes from the specified memory location and interprets them as BigEndian int */ -extern int GetBEInt(const char * a_Mem); +extern int GetBEInt(const std::byte * a_Mem); /** Writes four bytes to the specified memory location so that they interpret as BigEndian int */ -extern void SetBEInt(char * a_Mem, Int32 a_Value); +extern void SetBEInt(std::byte * a_Mem, Int32 a_Value); /** Splits a string that has embedded \0 characters, on those characters. a_Output is first cleared and then each separate string is pushed back into a_Output. diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp index ec43d2f12..d5b9fd0f7 100644 --- a/src/WorldStorage/FastNBT.cpp +++ b/src/WorldStorage/FastNBT.cpp @@ -137,7 +137,7 @@ std::error_code make_error_code(eNBTParseError a_Err) noexcept #define NEEDBYTES(N, ERR) \ do { \ - if (m_Length - m_Pos < static_cast<size_t>(N)) \ + if (m_Data.size() - m_Pos < static_cast<size_t>(N)) \ { \ return ERR; \ } \ @@ -147,9 +147,8 @@ std::error_code make_error_code(eNBTParseError a_Err) noexcept -cParsedNBT::cParsedNBT(const char * a_Data, size_t a_Length) : +cParsedNBT::cParsedNBT(const ContiguousByteBufferView a_Data) : m_Data(a_Data), - m_Length(a_Length), m_Pos(0) { m_Error = Parse(); @@ -161,12 +160,12 @@ cParsedNBT::cParsedNBT(const char * a_Data, size_t a_Length) : eNBTParseError cParsedNBT::Parse(void) { - if (m_Length < 3) + if (m_Data.size() < 3) { // Data too short return eNBTParseError::npNeedBytes; } - if (m_Data[0] != TAG_Compound) + if (m_Data[0] != std::byte(TAG_Compound)) { // The top-level tag must be a Compound return eNBTParseError::npNoTopLevelCompound; @@ -190,7 +189,7 @@ eNBTParseError cParsedNBT::ReadString(size_t & a_StringStart, size_t & a_StringL { NEEDBYTES(2, eNBTParseError::npStringMissingLength); a_StringStart = m_Pos + 2; - a_StringLen = static_cast<size_t>(GetBEShort(m_Data + m_Pos)); + a_StringLen = static_cast<size_t>(GetBEShort(m_Data.data() + m_Pos)); NEEDBYTES(2 + a_StringLen, eNBTParseError::npStringInvalidLength); m_Pos += 2 + a_StringLen; return eNBTParseError::npSuccess; @@ -210,8 +209,8 @@ eNBTParseError cParsedNBT::ReadCompound(void) for (;;) { NEEDBYTES(1, eNBTParseError::npCompoundImbalancedTag); - const char TagTypeNum = m_Data[m_Pos]; - if ((TagTypeNum < TAG_Min) || (TagTypeNum > TAG_Max)) + const auto TagTypeNum = m_Data[m_Pos]; + if ((TagTypeNum < std::byte(TAG_Min)) || (TagTypeNum > std::byte(TAG_Max))) { return eNBTParseError::npUnknownTag; } @@ -248,10 +247,10 @@ eNBTParseError cParsedNBT::ReadList(eTagType a_ChildrenType) // Read the count: NEEDBYTES(4, eNBTParseError::npListMissingLength); - int Count = GetBEInt(m_Data + m_Pos); + int Count = GetBEInt(m_Data.data() + m_Pos); m_Pos += 4; auto MinChildSize = GetMinTagSize(a_ChildrenType); - if ((Count < 0) || (Count > static_cast<int>((m_Length - m_Pos) / MinChildSize))) + if ((Count < 0) || (Count > static_cast<int>((m_Data.size() - m_Pos) / MinChildSize))) { return eNBTParseError::npListInvalidLength; } @@ -312,7 +311,7 @@ eNBTParseError cParsedNBT::ReadTag(void) case TAG_ByteArray: { NEEDBYTES(4, eNBTParseError::npArrayMissingLength); - int len = GetBEInt(m_Data + m_Pos); + int len = GetBEInt(m_Data.data() + m_Pos); m_Pos += 4; if (len < 0) { @@ -344,7 +343,7 @@ eNBTParseError cParsedNBT::ReadTag(void) case TAG_IntArray: { NEEDBYTES(4, eNBTParseError::npArrayMissingLength); - int len = GetBEInt(m_Data + m_Pos); + int len = GetBEInt(m_Data.data() + m_Pos); m_Pos += 4; if (len < 0) { @@ -392,7 +391,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen { if ( (m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) && - (memcmp(m_Data + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0) + (memcmp(m_Data.data() + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0) ) { return Child; @@ -470,9 +469,9 @@ cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) : m_CurrentStack(0) { m_Stack[0].m_Type = TAG_Compound; - m_Result.reserve(100 * 1024); - m_Result.push_back(TAG_Compound); - WriteString(a_RootTagName.data(), static_cast<UInt16>(a_RootTagName.size())); + m_Result.reserve(100 KiB); + m_Result.push_back(std::byte(TAG_Compound)); + WriteString(a_RootTagName); } @@ -502,7 +501,7 @@ void cFastNBTWriter::EndCompound(void) ASSERT(m_CurrentStack > 0); ASSERT(IsStackTopCompound()); - m_Result.push_back(TAG_End); + m_Result.push_back(std::byte(TAG_End)); --m_CurrentStack; } @@ -520,8 +519,8 @@ void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType) TagCommon(a_Name, TAG_List); - m_Result.push_back(static_cast<char>(a_ChildrenType)); - m_Result.append(4, static_cast<char>(0)); + m_Result.push_back(std::byte(a_ChildrenType)); + m_Result.append(4, std::byte(0)); ++m_CurrentStack; m_Stack[m_CurrentStack].m_Type = TAG_List; @@ -540,7 +539,7 @@ void cFastNBTWriter::EndList(void) ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); // Update the list count: - SetBEInt(const_cast<char *>(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count); + SetBEInt(m_Result.data() + m_Stack[m_CurrentStack].m_Pos, m_Stack[m_CurrentStack].m_Count); --m_CurrentStack; } @@ -552,7 +551,7 @@ void cFastNBTWriter::EndList(void) void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) { TagCommon(a_Name, TAG_Byte); - m_Result.push_back(static_cast<char>(a_Value)); + m_Result.push_back(std::byte(a_Value)); } @@ -563,7 +562,7 @@ void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) { TagCommon(a_Name, TAG_Short); UInt16 Value = htons(static_cast<UInt16>(a_Value)); - m_Result.append(reinterpret_cast<const char *>(&Value), 2); + m_Result.append(reinterpret_cast<const std::byte *>(&Value), 2); } @@ -574,7 +573,7 @@ void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) { TagCommon(a_Name, TAG_Int); UInt32 Value = htonl(static_cast<UInt32>(a_Value)); - m_Result.append(reinterpret_cast<const char *>(&Value), 4); + m_Result.append(reinterpret_cast<const std::byte *>(&Value), 4); } @@ -585,7 +584,7 @@ void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) { TagCommon(a_Name, TAG_Long); UInt64 Value = HostToNetwork8(&a_Value); - m_Result.append(reinterpret_cast<const char *>(&Value), 8); + m_Result.append(reinterpret_cast<const std::byte *>(&Value), 8); } @@ -596,7 +595,7 @@ void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) { TagCommon(a_Name, TAG_Float); UInt32 Value = HostToNetwork4(&a_Value); - m_Result.append(reinterpret_cast<const char *>(&Value), 4); + m_Result.append(reinterpret_cast<const std::byte *>(&Value), 4); } @@ -607,7 +606,7 @@ void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) { TagCommon(a_Name, TAG_Double); UInt64 Value = HostToNetwork8(&a_Value); - m_Result.append(reinterpret_cast<const char *>(&Value), 8); + m_Result.append(reinterpret_cast<const std::byte *>(&Value), 8); } @@ -618,8 +617,8 @@ void cFastNBTWriter::AddString(const AString & a_Name, const std::string_view a_ { TagCommon(a_Name, TAG_String); const UInt16 Length = htons(static_cast<UInt16>(a_Value.size())); - m_Result.append(reinterpret_cast<const char *>(&Length), sizeof(Length)); - m_Result.append(a_Value); + m_Result.append(reinterpret_cast<const std::byte *>(&Length), sizeof(Length)); + m_Result.append({ reinterpret_cast<const std::byte *>(a_Value.data()), a_Value.size() }); } @@ -630,8 +629,8 @@ void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, { TagCommon(a_Name, TAG_ByteArray); UInt32 len = htonl(static_cast<UInt32>(a_NumElements)); - m_Result.append(reinterpret_cast<const char *>(&len), 4); - m_Result.append(a_Value, a_NumElements); + m_Result.append(reinterpret_cast<const std::byte *>(&len), 4); + m_Result.append(reinterpret_cast<const std::byte *>(a_Value), a_NumElements); } @@ -648,11 +647,11 @@ void cFastNBTWriter::AddIntArray(const AString & a_Name, const Int32 * a_Value, { m_Result.reserve(size + 4 + (a_NumElements * 4)); } - m_Result.append(reinterpret_cast<const char *>(&len), 4); + m_Result.append(reinterpret_cast<const std::byte *>(&len), sizeof(len)); for (size_t i = 0; i < a_NumElements; i++) { UInt32 Element = htonl(static_cast<UInt32>(a_Value[i])); - m_Result.append(reinterpret_cast<const char *>(&Element), 4); + m_Result.append(reinterpret_cast<const std::byte *>(&Element), sizeof(Element)); } } @@ -663,20 +662,17 @@ void cFastNBTWriter::AddIntArray(const AString & a_Name, const Int32 * a_Value, void cFastNBTWriter::Finish(void) { ASSERT(m_CurrentStack == 0); - m_Result.push_back(TAG_End); + m_Result.push_back(std::byte(TAG_End)); } -void cFastNBTWriter::WriteString(const char * a_Data, UInt16 a_Length) +void cFastNBTWriter::WriteString(const std::string_view a_Data) { - UInt16 Len = htons(a_Length); - m_Result.append(reinterpret_cast<const char *>(&Len), 2); - m_Result.append(a_Data, a_Length); + // TODO check size <= short max + UInt16 Len = htons(static_cast<unsigned short>(a_Data.size())); + m_Result.append(reinterpret_cast<const std::byte *>(&Len), sizeof(Len)); + m_Result.append(reinterpret_cast<const std::byte *>(a_Data.data()), a_Data.size()); } - - - - diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index 257115fe1..0df520e21 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -152,7 +152,7 @@ Each primitive tag also stores the length of the contained data, in bytes. class cParsedNBT { public: - cParsedNBT(const char * a_Data, size_t a_Length); + cParsedNBT(ContiguousByteBufferView a_Data); bool IsValid(void) const { return (m_Error == eNBTParseError::npSuccess); } @@ -179,7 +179,7 @@ public: /** Returns the length of the tag's data, in bytes. Not valid for Compound or List tags! */ - size_t GetDataLength (int a_Tag) const + size_t GetDataLength(int a_Tag) const { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_List); ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound); @@ -188,11 +188,11 @@ public: /** Returns the data stored in this tag. Not valid for Compound or List tags! */ - const char * GetData(int a_Tag) const + const std::byte * GetData(int a_Tag) const { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_List); ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound); - return m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart; + return m_Data.data() + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart; } /** Returns the direct child tag of the specified name, or -1 if no such tag. */ @@ -227,21 +227,21 @@ public: inline Int16 GetShort(int a_Tag) const { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Short); - return GetBEShort(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart); + return GetBEShort(GetData(a_Tag)); } /** Returns the value stored in an Int tag. Not valid for any other tag type. */ inline Int32 GetInt(int a_Tag) const { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Int); - return GetBEInt(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart); + return GetBEInt(GetData(a_Tag)); } /** Returns the value stored in a Long tag. Not valid for any other tag type. */ inline Int64 GetLong(int a_Tag) const { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Long); - return NetworkToHostLong8(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart); + return NetworkToHostLong8(GetData(a_Tag)); } /** Returns the value stored in a Float tag. Not valid for any other tag type. */ @@ -256,7 +256,7 @@ public: UNUSED_VAR(Check1); UNUSED_VAR(Check2); - Int32 i = GetBEInt(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart); + Int32 i = GetBEInt(GetData(a_Tag)); float f; memcpy(&f, &i, sizeof(f)); return f; @@ -273,29 +273,33 @@ public: UNUSED_VAR(Check2); ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Double); - return NetworkToHostDouble8(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart); + return NetworkToHostDouble8(GetData(a_Tag)); } /** Returns the value stored in a String tag. Not valid for any other tag type. */ inline AString GetString(int a_Tag) const { + return AString(GetStringView(a_Tag)); + } + + /** Returns the value stored in a String tag. Not valid for any other tag type. */ + inline std::string_view GetStringView(int a_Tag) const + { ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_String); - AString res; - res.assign(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart, static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_DataLength)); - return res; + return { reinterpret_cast<const char *>(GetData(a_Tag)), GetDataLength(a_Tag) }; } /** Returns the tag's name. For tags that are not named, returns an empty string. */ inline AString GetName(int a_Tag) const { AString res; - res.assign(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_NameStart, static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_NameLength)); + res.assign(reinterpret_cast<const char *>(m_Data.data()) + m_Tags[static_cast<size_t>(a_Tag)].m_NameStart, static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_NameLength)); return res; } protected: - const char * m_Data; - size_t m_Length; + + ContiguousByteBufferView m_Data; std::vector<cFastNBTTag> m_Tags; eNBTParseError m_Error; // npSuccess if parsing succeeded @@ -343,7 +347,7 @@ public: AddByteArray(a_Name, a_Value.data(), a_Value.size()); } - const AString & GetResult(void) const {return m_Result; } + ContiguousByteBufferView GetResult(void) const { return m_Result; } void Finish(void); @@ -363,11 +367,11 @@ protected: sParent m_Stack[MAX_STACK]; int m_CurrentStack; - AString m_Result; + ContiguousByteBuffer m_Result; bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); } - void WriteString(const char * a_Data, UInt16 a_Length); + void WriteString(std::string_view a_Data); inline void TagCommon(const AString & a_Name, eTagType a_Type) { @@ -377,8 +381,8 @@ protected: if (IsStackTopCompound()) { // Compound: add the type and name: - m_Result.push_back(static_cast<char>(a_Type)); - WriteString(a_Name.c_str(), static_cast<UInt16>(a_Name.length())); + m_Result.push_back(std::byte(a_Type)); + WriteString(a_Name); } else { @@ -387,7 +391,3 @@ protected: } } } ; - - - - diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp index 7cf3c3eea..fa411b4e9 100644 --- a/src/WorldStorage/FireworksSerializer.cpp +++ b/src/WorldStorage/FireworksSerializer.cpp @@ -105,7 +105,7 @@ void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNB continue; } - const char * ColourData = (a_NBT.GetData(explosiontag)); + const auto * ColourData = (a_NBT.GetData(explosiontag)); for (size_t i = 0; i < DataLength; i += 4) { a_FireworkItem.m_Colours.push_back(GetBEInt(ColourData + i)); @@ -121,7 +121,7 @@ void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNB continue; } - const char * FadeColourData = (a_NBT.GetData(explosiontag)); + const auto * FadeColourData = (a_NBT.GetData(explosiontag)); for (size_t i = 0; i < DataLength; i += 4) { a_FireworkItem.m_FadeColours.push_back(GetBEInt(FadeColourData + i)); diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index d030b5a32..40d428dab 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -4,8 +4,7 @@ #include "Globals.h" #include "MapSerializer.h" -#include "../StringCompression.h" -#include "zlib/zlib.h" +#include "OSSupport/GZipFile.h" #include "FastNBT.h" #include "../Map.h" @@ -32,22 +31,9 @@ cMapSerializer::cMapSerializer(const AString & a_WorldName, cMap * a_Map): bool cMapSerializer::Load(void) { - AString Data = cFile::ReadWholeFile(m_Path); - if (Data.empty()) - { - return false; - } + const auto Data = GZipFile::ReadRestOfFile(m_Path); + const cParsedNBT NBT(Data.GetView()); - AString Uncompressed; - int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed); - - if (res != Z_OK) - { - return false; - } - - // Parse the NBT data: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); if (!NBT.IsValid()) { // NBT Parsing failed @@ -64,32 +50,15 @@ bool cMapSerializer::Load(void) bool cMapSerializer::Save(void) { cFastNBTWriter Writer; - SaveMapToNBT(Writer); - Writer.Finish(); #ifdef _DEBUG - cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + cParsedNBT TestParse(Writer.GetResult()); ASSERT(TestParse.IsValid()); #endif // _DEBUG - cFile File; - if (!File.Open(m_Path, cFile::fmWrite)) - { - return false; - } - - AString Compressed; - int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); - - if (res != Z_OK) - { - return false; - } - - File.Write(Compressed.data(), Compressed.size()); - File.Close(); + GZipFile::Write(m_Path, Writer.GetResult()); return true; } @@ -225,7 +194,7 @@ bool cIDCountSerializer::Load(void) // NOTE: idcounts.dat is not compressed (raw format) // Parse the NBT data: - cParsedNBT NBT(Data.data(), Data.size()); + cParsedNBT NBT({ reinterpret_cast<const std::byte *>(Data.data()), Data.size() }); if (!NBT.IsValid()) { // NBT Parsing failed @@ -261,7 +230,7 @@ bool cIDCountSerializer::Save(void) Writer.Finish(); #ifdef _DEBUG - cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + cParsedNBT TestParse(Writer.GetResult()); ASSERT(TestParse.IsValid()); #endif // _DEBUG @@ -278,11 +247,3 @@ bool cIDCountSerializer::Save(void) return true; } - - - - - - - - diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 0e3cdde76..2542cd2da 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -1177,17 +1177,14 @@ public: //////////////////////////////////////////////////////////////////////////////// // NBTChunkSerializer: -bool NBTChunkSerializer::serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter) +void NBTChunkSerializer::Serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter) { SerializerCollector serializer(aWriter); aWriter.BeginCompound("Level"); aWriter.AddInt("xPos", aCoords.m_ChunkX); aWriter.AddInt("zPos", aCoords.m_ChunkZ); - if (!aWorld.GetChunkData(aCoords, serializer)) - { - aWriter.EndCompound(); // "Level" - return false; - } + [[maybe_unused]] const bool Result = aWorld.GetChunkData(aCoords, serializer); // Chunk must be present in order to save + ASSERT(Result); serializer.Finish(); // Close NBT tags // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): @@ -1240,5 +1237,4 @@ bool NBTChunkSerializer::serialize(const cWorld & aWorld, cChunkCoords aCoords, aWriter.AddByte("TerrainPopulated", 1); aWriter.EndCompound(); // "Level" - return true; } diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index aeac9fc46..c2de79269 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -21,7 +21,6 @@ class NBTChunkSerializer { public: - /** Serializes the chunk into the specified writer. - Returns true on success, false on failure (chunk not present etc.) */ - static bool serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter); + /** Serializes the chunk into the specified writer. The chunk must be present. */ + static void Serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter); }; diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index 44ce7ca9b..656a98709 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -7,7 +7,6 @@ #include "FastNBT.h" #include "SchematicFileSerializer.h" -#include "../StringCompression.h" #include "../OSSupport/GZipFile.h" @@ -17,118 +16,59 @@ //////////////////////////////////////////////////////////////////////////////// // cSchematicFileSerializer: -bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) +void cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const std::string & a_FileName) { - // Un-GZip the contents: - AString Contents; - cGZipFile File; - if (!File.Open(a_FileName, cGZipFile::fmRead)) - { - LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str()); - return false; - } - int NumBytesRead = File.ReadRestOfFile(Contents); - if (NumBytesRead < 0) - { - LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead); - return false; - } - File.Close(); + const auto Data = GZipFile::ReadRestOfFile(a_FileName); + const cParsedNBT NBT(Data.GetView()); - // Parse the NBT: - cParsedNBT NBT(Contents.data(), Contents.size()); if (!NBT.IsValid()) { - LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str()); - return false; + throw std::runtime_error(fmt::format("Cannot parse the NBT in the schematic file \"{}\".", a_FileName)); } - return LoadFromSchematicNBT(a_BlockArea, NBT); + LoadFromSchematicNBT(a_BlockArea, NBT); } -bool cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData) +void cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const ContiguousByteBufferView a_SchematicData) { - // Uncompress the data: - AString UngzippedData; - if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK) - { - LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__); - return false; - } + const auto Extracted = Compression::Extractor().ExtractGZip(a_SchematicData); + const cParsedNBT NBT(Extracted.GetView()); - // Parse the NBT: - cParsedNBT NBT(UngzippedData.data(), UngzippedData.size()); if (!NBT.IsValid()) { - LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__); - return false; + throw std::runtime_error("Cannot parse the NBT in the schematic data."); } - return LoadFromSchematicNBT(a_BlockArea, NBT); + LoadFromSchematicNBT(a_BlockArea, NBT); } -bool cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName) +void cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const std::string & a_FileName) { - // Serialize into NBT data: - AString NBT = SaveToSchematicNBT(a_BlockArea); - if (NBT.empty()) - { - LOG("%s: Cannot serialize the area into an NBT representation for file \"%s\".", __FUNCTION__, a_FileName.c_str()); - return false; - } - - // Save to file - cGZipFile File; - if (!File.Open(a_FileName, cGZipFile::fmWrite)) - { - LOG("%s: Cannot open file \"%s\" for writing.", __FUNCTION__, a_FileName.c_str()); - return false; - } - if (!File.Write(NBT)) - { - LOG("%s: Cannot write data to file \"%s\".", __FUNCTION__, a_FileName.c_str()); - return false; - } - return true; + GZipFile::Write(a_FileName, SaveToSchematicNBT(a_BlockArea)); } -bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out) +Compression::Result cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea) { - // Serialize into NBT data: - AString NBT = SaveToSchematicNBT(a_BlockArea); - if (NBT.empty()) - { - LOG("%s: Cannot serialize the area into an NBT representation.", __FUNCTION__); - return false; - } - - // Gzip the data: - int res = CompressStringGZIP(NBT.data(), NBT.size(), a_Out); - if (res != Z_OK) - { - LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res); - return false; - } - return true; + return Compression::Compressor().CompressGZip(SaveToSchematicNBT(a_BlockArea)); } -bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT) +void cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, const cParsedNBT & a_NBT) { int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String)) @@ -136,8 +76,7 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP AString Materials = a_NBT.GetString(TMaterials); if (Materials.compare("Alpha") != 0) { - LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str()); - return false; + throw std::runtime_error(fmt::format("Materials tag is present and \"{}\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials)); } } int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width"); @@ -150,13 +89,13 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP (a_NBT.GetType(TSizeZ) != TAG_Short) ) { - LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", + throw std::runtime_error(fmt::format( + "Dimensions are missing from the schematic file ({}, {}, {}), ({}, {}, {})", TSizeX, TSizeY, TSizeZ, (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1, (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1, (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1 - ); - return false; + )); } int SizeX = a_NBT.GetShort(TSizeX); @@ -164,16 +103,14 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP int SizeZ = a_NBT.GetShort(TSizeZ); if ((SizeX < 1) || (SizeX > 65535) || (SizeY < 1) || (SizeY > 65535) || (SizeZ < 1) || (SizeZ > 65535)) { - LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ); - return false; + throw std::runtime_error(fmt::format("Dimensions are invalid in the schematic file: {}, {}, {}", SizeX, SizeY, SizeZ)); } int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks"); int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data"); if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray)) { - LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes); - return false; + throw std::runtime_error(fmt::format("BlockTypes are invalid in the schematic file: {}", TBlockTypes)); } bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray); @@ -222,15 +159,13 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP } memcpy(a_BlockArea.GetBlockMetas(), a_NBT.GetData(TBlockMetas), NumMetaBytes); } - - return true; } -AString cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea) +ContiguousByteBuffer cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea) { cFastNBTWriter Writer("Schematic"); Writer.AddShort("Width", static_cast<Int16>(a_BlockArea.m_Size.x)); @@ -267,9 +202,5 @@ AString cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockA Writer.EndList(); Writer.Finish(); - return Writer.GetResult(); + return ContiguousByteBuffer(Writer.GetResult()); } - - - - diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h index 6aaa0662f..d52f80da1 100644 --- a/src/WorldStorage/SchematicFileSerializer.h +++ b/src/WorldStorage/SchematicFileSerializer.h @@ -10,6 +10,7 @@ #pragma once #include "../BlockArea.h" +#include "../StringCompression.h" @@ -26,29 +27,23 @@ class cSchematicFileSerializer { public: - /** Loads an area from a .schematic file. Returns true if successful. */ - static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); + /** Loads an area from a .schematic file. */ + static void LoadFromSchematicFile(cBlockArea & a_BlockArea, const std::string & a_FileName); - /** Loads an area from a string containing the .schematic file data. Returns true if successful. */ - static bool LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData); + /** Loads an area from a string containing the .schematic file data. */ + static void LoadFromSchematicString(cBlockArea & a_BlockArea, ContiguousByteBufferView a_SchematicData); - /** Saves the area into a .schematic file. Returns true if successful. */ - static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName); + /** Saves the area into a .schematic file. */ + static void SaveToSchematicFile(const cBlockArea & a_BlockArea, const std::string & a_FileName); - /** Saves the area into a string containing the .schematic file data. - Returns true if successful, false on failure. The data is stored into a_Out. */ - static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out); + /** Saves the area into a string containing the .schematic file data. */ + static Compression::Result SaveToSchematicString(const cBlockArea & a_BlockArea); private: - /** Loads the area from a schematic file uncompressed and parsed into a NBT tree. - Returns true if successful. */ - static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT); - - /** Saves the area into a NBT representation and returns the NBT data as a string. - Returns an empty string if failed. */ - static AString SaveToSchematicNBT(const cBlockArea & a_BlockArea); -}; - - + /** Loads the area from a schematic file uncompressed and parsed into a NBT tree. */ + static void LoadFromSchematicNBT(cBlockArea & a_BlockArea, const cParsedNBT & a_NBT); + /** Saves the area into a NBT representation and returns the NBT data as a string. */ + static ContiguousByteBuffer SaveToSchematicNBT(const cBlockArea & a_BlockArea); +}; diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp index 7b41f92b1..3ad4d42ee 100644 --- a/src/WorldStorage/ScoreboardSerializer.cpp +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -4,8 +4,7 @@ #include "Globals.h" #include "ScoreboardSerializer.h" -#include "../StringCompression.h" -#include "zlib/zlib.h" +#include "OSSupport/GZipFile.h" #include "FastNBT.h" #include "../Scoreboard.h" @@ -31,29 +30,24 @@ cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScore bool cScoreboardSerializer::Load(void) { - AString Data = cFile::ReadWholeFile(m_Path); - if (Data.empty()) + try { - return false; - } + const auto Data = GZipFile::ReadRestOfFile(m_Path); + const cParsedNBT NBT(Data.GetView()); - AString Uncompressed; - int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } - if (res != Z_OK) - { - return false; + return LoadScoreboardFromNBT(NBT); } - - // Parse the NBT data: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); - if (!NBT.IsValid()) + catch (const std::exception & Oops) { - // NBT Parsing failed + LOGWARNING("Failed to load scoreboard from \"%s\": %s", m_Path.c_str(), Oops.what()); return false; } - - return LoadScoreboardFromNBT(NBT); } @@ -63,32 +57,15 @@ bool cScoreboardSerializer::Load(void) bool cScoreboardSerializer::Save(void) { cFastNBTWriter Writer; - SaveScoreboardToNBT(Writer); - Writer.Finish(); - #ifdef _DEBUG - cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); +#ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult()); ASSERT(TestParse.IsValid()); - #endif // _DEBUG - - cFile File; - if (!File.Open(m_Path, cFile::fmWrite)) - { - return false; - } - - AString Compressed; - int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); - - if (res != Z_OK) - { - return false; - } +#endif // _DEBUG - File.Write(Compressed.data(), Compressed.size()); - File.Close(); + GZipFile::Write(m_Path, Writer.GetResult()); return true; } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 0e874df7c..78320d636 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -8,8 +8,8 @@ #include "NBTChunkSerializer.h" #include "EnchantmentSerializer.h" #include "NamespaceSerializer.h" -#include "zlib/zlib.h" #include "json/json.h" +#include "OSSupport/GZipFile.h" #include "../World.h" #include "../Item.h" #include "../ItemGrid.h" @@ -86,7 +86,7 @@ Since only the header is actually in the memory, this number can be high, but st cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : Super(a_World), - m_CompressionFactor(a_CompressionFactor) + m_Compressor(a_CompressionFactor) { // Create a level.dat file for mapping tools, if it doesn't already exist: AString fnam; @@ -117,12 +117,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : Writer.EndCompound(); Writer.Finish(); - gzFile gz = gzopen((fnam).c_str(), "wb"); - if (gz != nullptr) - { - gzwrite(gz, Writer.GetResult().data(), static_cast<unsigned>(Writer.GetResult().size())); - } - gzclose(gz); + GZipFile::Write(fnam, Writer.GetResult()); } } @@ -145,7 +140,7 @@ cWSSAnvil::~cWSSAnvil() bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) { - AString ChunkData; + ContiguousByteBuffer ChunkData; if (!GetChunkData(a_Chunk, ChunkData)) { // The reason for failure is already printed in GetChunkData() @@ -161,15 +156,17 @@ bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) { - AString ChunkData; - if (!SaveChunkToData(a_Chunk, ChunkData)) + try { - LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; + if (!SetChunkData(a_Chunk, SaveChunkToData(a_Chunk).GetView())) + { + LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } } - if (!SetChunkData(a_Chunk, ChunkData)) + catch (const std::exception & Oops) { - LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + LOGWARNING("Cannot serialize chunk [%d, %d] into data: %s", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Oops.what()); return false; } @@ -181,7 +178,7 @@ bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) -void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Reason, const AString & a_ChunkDataToSave) +void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Reason, const ContiguousByteBufferView a_ChunkDataToSave) { // Construct the filename for offloading: AString OffloadFileName; @@ -202,7 +199,7 @@ void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Re // Log the warning to console: const int RegionX = FAST_FLOOR_DIV(a_ChunkX, 32); const int RegionZ = FAST_FLOOR_DIV(a_ChunkZ, 32); - AString Info = Printf("Loading chunk [%d, %d] for world %s from file r.%d.%d.mca failed: %s. Offloading old chunk data to file %s and regenerating chunk.", + AString Info = Printf("Loading chunk [%d, %d] for world %s from file r.%d.%d.mca failed: %s Offloading old chunk data to file %s and regenerating chunk.", a_ChunkX, a_ChunkZ, m_World->GetName().c_str(), RegionX, RegionZ, a_Reason.c_str(), OffloadFileName.c_str() ); LOGWARNING("%s", Info.c_str()); @@ -231,7 +228,7 @@ void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Re -bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data) { cCSLock Lock(m_CS); cMCAFile * File = LoadMCAFile(a_Chunk); @@ -246,7 +243,7 @@ bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) -bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const ContiguousByteBufferView a_Data) { cCSLock Lock(m_CS); cMCAFile * File = LoadMCAFile(a_Chunk); @@ -314,55 +311,47 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) -bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) +bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const ContiguousByteBufferView a_Data) { - // Uncompress the data: - AString Uncompressed; - int res = InflateString(a_Data.data(), a_Data.size(), Uncompressed); - if (res != Z_OK) + try { - LOGWARNING("Uncompressing chunk [%d, %d] failed: %d", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, res); - ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "InflateString() failed", a_Data); - return false; - } + const auto Extracted = m_Extractor.ExtractZLib(a_Data); + cParsedNBT NBT(Extracted.GetView()); - // Parse the NBT data: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); - if (!NBT.IsValid()) + if (!NBT.IsValid()) + { + // NBT Parsing failed: + throw std::runtime_error(fmt::format("NBT parsing failed. {} at position {}.", NBT.GetErrorCode().message(), NBT.GetErrorPos())); + } + + // Load the data from NBT: + return LoadChunkFromNBT(a_Chunk, NBT, a_Data); + } + catch (const std::exception & Oops) { - // NBT Parsing failed - auto msg = fmt::format("NBT parsing failed: {}, pos {}", NBT.GetErrorCode().message(), NBT.GetErrorPos()); - ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, msg, a_Data); + ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Oops.what(), a_Data); return false; } - - // Load the data from NBT: - return LoadChunkFromNBT(a_Chunk, NBT, a_Data); } -bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) +Compression::Result cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk) { cFastNBTWriter Writer; - if (!SaveChunkToNBT(a_Chunk, Writer)) - { - LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } + NBTChunkSerializer::Serialize(*m_World, a_Chunk, Writer); Writer.Finish(); - CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data, m_CompressionFactor); - return true; + return m_Compressor.CompressZLib(Writer.GetResult()); } -bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, const AString & a_RawChunkData) +bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, const ContiguousByteBufferView a_RawChunkData) { // The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data) cChunkDef::BlockTypes BlockTypes; @@ -496,20 +485,6 @@ void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & -bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer) -{ - if (!NBTChunkSerializer::serialize(*m_World, a_Chunk, a_Writer)) - { - LOGWARNING("Failed to save chunk %s.", a_Chunk.ToString()); - return false; - } - return true; -} - - - - - cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) { if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray)) @@ -549,7 +524,7 @@ cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_Bio // The biomes stored don't match in size return nullptr; } - const char * BiomeData = (a_NBT.GetData(a_TagIdx)); + const auto * BiomeData = a_NBT.GetData(a_TagIdx); for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) { (*a_BiomeMap)[i] = static_cast<EMCSBiome>(GetBEInt(&BiomeData[i * 4])); @@ -587,7 +562,7 @@ void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & try { - LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID)); + LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetStringView(sID)); } catch (...) { @@ -690,14 +665,9 @@ OwnedBlockEntity cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int // All the other blocktypes should have no entities assigned to them. Report an error: // Get the "id" tag: int TagID = a_NBT.FindChildByName(a_Tag, "id"); - AString TypeName("<unknown>"); - if (TagID >= 0) - { - TypeName.assign(a_NBT.GetData(TagID), static_cast<size_t>(a_NBT.GetDataLength(TagID))); - } FLOGINFO("WorldLoader({0}): Block entity mismatch: block type {1} ({2}), type \"{3}\", at {4}; the entity will be lost.", m_World->GetName(), - ItemTypeToString(a_BlockType), a_BlockType, TypeName, + ItemTypeToString(a_BlockType), a_BlockType, (TagID >= 0) ? a_NBT.GetStringView(TagID) : "unknown", a_Pos ); return nullptr; @@ -887,10 +857,16 @@ bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, con return false; } + // Check if the "id" tag is a string: + if (a_NBT.GetType(TagID) != eTagType::TAG_String) + { + return false; + } + // Compare the value: for (const auto & et: a_ExpectedTypes) { - if (strncmp(a_NBT.GetData(TagID), et.c_str(), static_cast<size_t>(a_NBT.GetDataLength(TagID))) == 0) + if (a_NBT.GetStringView(TagID) == et) { return true; } @@ -906,8 +882,7 @@ bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, con } FLOGWARNING("Block entity type mismatch: exp {0}, got \"{1}\". The block entity at {2} will lose all its properties.", expectedTypes.c_str() + 2, // Skip the first ", " that is extra in the string - AString(a_NBT.GetData(TagID), static_cast<size_t>(a_NBT.GetDataLength(TagID))), - a_Pos + a_NBT.GetStringView(TagID), a_Pos ); return false; } @@ -1364,7 +1339,7 @@ OwnedBlockEntity cWSSAnvil::LoadMobSpawnerFromNBT(const cParsedNBT & a_NBT, int int Type = a_NBT.FindChildByName(a_TagIdx, "EntityId"); if ((Type >= 0) && (a_NBT.GetType(Type) == TAG_String)) { - const auto StatInfo = NamespaceSerializer::SplitNamespacedID(a_NBT.GetString(Type)); + const auto StatInfo = NamespaceSerializer::SplitNamespacedID(a_NBT.GetStringView(Type)); if (StatInfo.first == NamespaceSerializer::Namespace::Unknown) { return nullptr; @@ -1571,10 +1546,10 @@ OwnedBlockEntity cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagI -void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength) +void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const std::string_view a_EntityName) { typedef void (cWSSAnvil::*EntityLoaderFunc)(cEntityList &, const cParsedNBT &, int a_EntityTagIdx); - typedef std::map<AString, EntityLoaderFunc> EntityLoaderMap; + typedef std::map<std::string_view, EntityLoaderFunc> EntityLoaderMap; static const EntityLoaderMap EntityTypeToFunction { { "Boat", &cWSSAnvil::LoadBoatFromNBT }, @@ -1624,14 +1599,14 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a // TODO: flatten monster\projectile into one entity type enum - auto it = EntityTypeToFunction.find(AString(a_IDTag, a_IDTagLength)); + const auto it = EntityTypeToFunction.find(a_EntityName); if (it != EntityTypeToFunction.end()) { (this->*it->second)(a_Entities, a_NBT, a_EntityTagIdx); return; } - const auto StatInfo = NamespaceSerializer::SplitNamespacedID({ a_IDTag, a_IDTagLength }); + const auto StatInfo = NamespaceSerializer::SplitNamespacedID(a_EntityName); if (StatInfo.first == NamespaceSerializer::Namespace::Unknown) { return; @@ -3947,7 +3922,7 @@ bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) -bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data) { if (!OpenFile(true)) { @@ -3976,21 +3951,21 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a UInt32 ChunkSize = 0; if (m_File.Read(&ChunkSize, 4) != 4) { - m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Cannot read chunk size", ""); + m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Cannot read chunk size", {}); return false; } ChunkSize = ntohl(ChunkSize); if (ChunkSize < 1) { // Chunk size too small - m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Chunk size too small", ""); + m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Chunk size too small", {}); return false; } char CompressionType = 0; if (m_File.Read(&CompressionType, 1) != 1) { - m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Cannot read chunk compression", ""); + m_ParentSchema.ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "Cannot read chunk compression", {}); return false; } ChunkSize--; @@ -4015,7 +3990,7 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a -bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const ContiguousByteBufferView a_Data) { if (!OpenFile(false)) { @@ -4034,7 +4009,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri LocalZ = 32 + LocalZ; } - unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); + unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data.size()); // Store the chunk data: m_File.Seek(static_cast<int>(ChunkSector * 4096)); @@ -4103,12 +4078,12 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri -unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data) +unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const size_t a_DataSize) { // See if it fits the current location: unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); unsigned ChunkLen = ChunkLocation & 0xff; - if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) + if (a_DataSize + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) { return ChunkLocation >> 8; } @@ -4126,7 +4101,3 @@ unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const } // for i - m_Header[] return MaxLocation >> 8; } - - - - diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 6d9b49788..1751c6761 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -11,6 +11,7 @@ #include "../BlockEntities/BlockEntity.h" #include "WorldStorage.h" #include "FastNBT.h" +#include "StringCompression.h" @@ -62,9 +63,8 @@ protected: cMCAFile(cWSSAnvil & a_ParentSchema, const AString & a_FileName, int a_RegionX, int a_RegionZ); - bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); - bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); - bool EraseChunkData(const cChunkCoords & a_Chunk); + bool GetChunkData (const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data); + bool SetChunkData (const cChunkCoords & a_Chunk, ContiguousByteBufferView a_Data); int GetRegionX (void) const {return m_RegionX; } int GetRegionZ (void) const {return m_RegionZ; } @@ -86,8 +86,8 @@ protected: // Chunk timestamps, following the chunk headers unsigned m_TimeStamps[MCA_MAX_CHUNKS]; - /** Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. */ - unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); + /** Finds a free location large enough to hold a_Data. Returns the sector number. */ + unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, size_t a_DataSize); /** Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found) */ bool OpenFile(bool a_IsForReading); @@ -97,30 +97,27 @@ protected: cCriticalSection m_CS; cMCAFiles m_Files; // a MRU cache of MCA files - int m_CompressionFactor; - + Compression::Extractor m_Extractor; + Compression::Compressor m_Compressor; /** Reports that the specified chunk failed to load and saves the chunk data to an external file. */ - void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Reason, const AString & a_ChunkDataToSave); + void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Reason, ContiguousByteBufferView a_ChunkDataToSave); /** Gets chunk data from the correct file; locks file CS as needed */ - bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); + bool GetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data); /** Sets chunk data into the correct file; locks file CS as needed */ - bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data); + bool SetChunkData(const cChunkCoords & a_Chunk, ContiguousByteBufferView a_Data); /** Loads the chunk from the data (no locking needed) */ - bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data); + bool LoadChunkFromData(const cChunkCoords & a_Chunk, ContiguousByteBufferView a_Data); /** Saves the chunk into datastream (no locking needed) */ - bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); + Compression::Result SaveChunkToData(const cChunkCoords & a_Chunk); /** Loads the chunk from NBT data (no locking needed). a_RawChunkData is the raw (compressed) chunk data, used for offloading when chunk loading fails. */ - bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, const AString & a_RawChunkData); - - /** Saves the chunk into NBT data using a_Writer; returns true on success */ - bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer); + bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, ContiguousByteBufferView a_RawChunkData); /** Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, nullptr otherwise */ cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); @@ -175,7 +172,7 @@ protected: OwnedBlockEntity LoadNoteBlockFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos); OwnedBlockEntity LoadSignFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos); - void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength); + void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, std::string_view a_EntityName); void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadEnderCrystalFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); @@ -309,7 +306,3 @@ protected: virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; virtual const AString GetName(void) const override {return "anvil"; } } ; - - - - diff --git a/src/mbedTLS++/AesCfb128Decryptor.cpp b/src/mbedTLS++/AesCfb128Decryptor.cpp index 0a5896e52..523e06161 100644 --- a/src/mbedTLS++/AesCfb128Decryptor.cpp +++ b/src/mbedTLS++/AesCfb128Decryptor.cpp @@ -43,13 +43,8 @@ void cAesCfb128Decryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) -void cAesCfb128Decryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length) +void cAesCfb128Decryptor::ProcessData(std::byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length) { ASSERT(IsValid()); // Must Init() first - mbedtls_aes_crypt_cfb8(&m_Aes, MBEDTLS_AES_DECRYPT, a_Length, m_IV, a_EncryptedIn, a_DecryptedOut); + mbedtls_aes_crypt_cfb8(&m_Aes, MBEDTLS_AES_DECRYPT, a_Length, m_IV, a_EncryptedIn, reinterpret_cast<unsigned char *>(a_DecryptedOut)); } - - - - - diff --git a/src/mbedTLS++/AesCfb128Decryptor.h b/src/mbedTLS++/AesCfb128Decryptor.h index 99d479381..601699998 100644 --- a/src/mbedTLS++/AesCfb128Decryptor.h +++ b/src/mbedTLS++/AesCfb128Decryptor.h @@ -27,7 +27,7 @@ public: void Init(const Byte a_Key[16], const Byte a_IV[16]); /** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */ - void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length); + void ProcessData(std::byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length); /** Returns true if the object has been initialized with the Key / IV */ bool IsValid(void) const { return m_IsValid; } diff --git a/src/mbedTLS++/AesCfb128Encryptor.cpp b/src/mbedTLS++/AesCfb128Encryptor.cpp index a7b423a77..7c2ae8b2b 100644 --- a/src/mbedTLS++/AesCfb128Encryptor.cpp +++ b/src/mbedTLS++/AesCfb128Encryptor.cpp @@ -43,13 +43,8 @@ void cAesCfb128Encryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) -void cAesCfb128Encryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length) +void cAesCfb128Encryptor::ProcessData(std::byte * const a_EncryptedOut, const std::byte * const a_PlainIn, size_t a_Length) { ASSERT(IsValid()); // Must Init() first - mbedtls_aes_crypt_cfb8(&m_Aes, MBEDTLS_AES_ENCRYPT, a_Length, m_IV, a_PlainIn, a_EncryptedOut); + mbedtls_aes_crypt_cfb8(&m_Aes, MBEDTLS_AES_ENCRYPT, a_Length, m_IV, reinterpret_cast<const unsigned char *>(a_PlainIn), reinterpret_cast<unsigned char *>(a_EncryptedOut)); } - - - - - diff --git a/src/mbedTLS++/AesCfb128Encryptor.h b/src/mbedTLS++/AesCfb128Encryptor.h index 8048e2d61..f25998c15 100644 --- a/src/mbedTLS++/AesCfb128Encryptor.h +++ b/src/mbedTLS++/AesCfb128Encryptor.h @@ -19,6 +19,7 @@ class cAesCfb128Encryptor { public: + cAesCfb128Encryptor(void); ~cAesCfb128Encryptor(); @@ -26,7 +27,7 @@ public: void Init(const Byte a_Key[16], const Byte a_IV[16]); /** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */ - void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length); + void ProcessData(std::byte * a_EncryptedOut, const std::byte * a_PlainIn, size_t a_Length); /** Returns true if the object has been initialized with the Key / IV */ bool IsValid(void) const { return m_IsValid; } diff --git a/src/mbedTLS++/RsaPrivateKey.cpp b/src/mbedTLS++/RsaPrivateKey.cpp index 704a2a1d0..3fd429dc0 100644 --- a/src/mbedTLS++/RsaPrivateKey.cpp +++ b/src/mbedTLS++/RsaPrivateKey.cpp @@ -55,7 +55,7 @@ bool cRsaPrivateKey::Generate(unsigned a_KeySizeBits) -AString cRsaPrivateKey::GetPubKeyDER(void) +ContiguousByteBuffer cRsaPrivateKey::GetPubKeyDER(void) { class cPubKey { @@ -96,21 +96,21 @@ AString cRsaPrivateKey::GetPubKeyDER(void) int res = mbedtls_pk_write_pubkey_der(PkCtx, buf, sizeof(buf)); if (res < 0) { - return AString(); + return {}; } - return AString(reinterpret_cast<const char *>(buf + sizeof(buf) - res), static_cast<size_t>(res)); + return { reinterpret_cast<const std::byte *>(buf + sizeof(buf) - res), static_cast<size_t>(res) }; } -int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) +int cRsaPrivateKey::Decrypt(const ContiguousByteBufferView a_EncryptedData, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) { - if (a_EncryptedLength < m_Rsa.len) + if (a_EncryptedData.size() < m_Rsa.len) { LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u", - __FUNCTION__, static_cast<unsigned>(a_EncryptedLength), static_cast<unsigned>(m_Rsa.len) + __FUNCTION__, static_cast<unsigned>(a_EncryptedData.size()), static_cast<unsigned>(m_Rsa.len) ); ASSERT(!"Invalid a_DecryptedMaxLength!"); return -1; @@ -126,7 +126,7 @@ int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLeng size_t DecryptedLength; int res = mbedtls_rsa_pkcs1_decrypt( &m_Rsa, mbedtls_ctr_drbg_random, m_CtrDrbg.GetInternal(), MBEDTLS_RSA_PRIVATE, &DecryptedLength, - a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength + reinterpret_cast<const unsigned char *>(a_EncryptedData.data()), a_DecryptedData, a_DecryptedMaxLength ); if (res != 0) { @@ -134,41 +134,3 @@ int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLeng } return static_cast<int>(DecryptedLength); } - - - - - -int cRsaPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) -{ - if (a_EncryptedMaxLength < m_Rsa.len) - { - LOGD("%s: Invalid a_EncryptedMaxLength: got %u, exp at least %u", - __FUNCTION__, static_cast<unsigned>(a_EncryptedMaxLength), static_cast<unsigned>(m_Rsa.len) - ); - ASSERT(!"Invalid a_DecryptedMaxLength!"); - return -1; - } - if (a_PlainLength < m_Rsa.len) - { - LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u", - __FUNCTION__, static_cast<unsigned>(a_PlainLength), static_cast<unsigned>(m_Rsa.len) - ); - ASSERT(!"Invalid a_PlainLength!"); - return -1; - } - int res = mbedtls_rsa_pkcs1_encrypt( - &m_Rsa, mbedtls_ctr_drbg_random, m_CtrDrbg.GetInternal(), MBEDTLS_RSA_PRIVATE, - a_PlainLength, a_PlainData, a_EncryptedData - ); - if (res != 0) - { - return -1; - } - return static_cast<int>(m_Rsa.len); -} - - - - - diff --git a/src/mbedTLS++/RsaPrivateKey.h b/src/mbedTLS++/RsaPrivateKey.h index 63e648b60..33a016edc 100644 --- a/src/mbedTLS++/RsaPrivateKey.h +++ b/src/mbedTLS++/RsaPrivateKey.h @@ -35,17 +35,12 @@ public: bool Generate(unsigned a_KeySizeBits = 1024); /** Returns the public key part encoded in ASN1 DER encoding */ - AString GetPubKeyDER(void); + ContiguousByteBuffer GetPubKeyDER(void); /** Decrypts the data using RSAES-PKCS#1 algorithm. Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. Returns the number of bytes decrypted, or negative number for error. */ - int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); - - /** Encrypts the data using RSAES-PKCS#1 algorithm. - Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. - Returns the number of bytes decrypted, or negative number for error. */ - int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); + int Decrypt(ContiguousByteBufferView a_EncryptedData, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); protected: /** The mbedTLS key context */ diff --git a/tests/ByteBuffer/ByteBufferTest.cpp b/tests/ByteBuffer/ByteBufferTest.cpp index 7278c5f0f..9aecc2e3c 100644 --- a/tests/ByteBuffer/ByteBufferTest.cpp +++ b/tests/ByteBuffer/ByteBufferTest.cpp @@ -36,7 +36,7 @@ static void TestWrite(void) buf.WriteVarInt32(5); buf.WriteVarInt32(300); buf.WriteVarInt32(0); - AString All; + ContiguousByteBuffer All; buf.ReadAll(All); TEST_EQUAL(All.size(), 4); TEST_EQUAL(memcmp(All.data(), "\x05\xac\x02\x00", All.size()), 0); diff --git a/tests/Generating/CMakeLists.txt b/tests/Generating/CMakeLists.txt index ac8a9be42..207b71e1a 100644 --- a/tests/Generating/CMakeLists.txt +++ b/tests/Generating/CMakeLists.txt @@ -158,7 +158,7 @@ add_library(GeneratorTestingSupport STATIC ${GENERATING_HDRS} ${STUBS} ) -target_link_libraries(GeneratorTestingSupport tolualib zlib fmt::fmt jsoncpp_lib) +target_link_libraries(GeneratorTestingSupport fmt::fmt jsoncpp_lib tolualib libdeflate) source_group("Stubs" FILES ${STUBS}) source_group("Generating" FILES ${GENERATING_HDRS} ${GENERATING_SRCS}) diff --git a/tests/LuaThreadStress/CMakeLists.txt b/tests/LuaThreadStress/CMakeLists.txt index 32ff4b32e..83bd8a0ca 100644 --- a/tests/LuaThreadStress/CMakeLists.txt +++ b/tests/LuaThreadStress/CMakeLists.txt @@ -78,7 +78,7 @@ source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS}) source_group("Sources" FILES ${SRCS}) source_group("Lua files" FILES Test.lua) add_executable(LuaThreadStress ${SRCS} ${SHARED_SRCS} ${SHARED_HDRS} Test.lua) -target_link_libraries(LuaThreadStress tolualib zlib fmt::fmt Threads::Threads) +target_link_libraries(LuaThreadStress fmt::fmt Threads::Threads tolualib libdeflate) add_test(NAME LuaThreadStress-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND LuaThreadStress) diff --git a/tests/SchematicFileSerializer/CMakeLists.txt b/tests/SchematicFileSerializer/CMakeLists.txt index d05d06a02..033002f6b 100644 --- a/tests/SchematicFileSerializer/CMakeLists.txt +++ b/tests/SchematicFileSerializer/CMakeLists.txt @@ -69,7 +69,7 @@ endif() source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS}) source_group("Sources" FILES ${SRCS}) add_executable(SchematicFileSerializer-exe ${SRCS} ${SHARED_SRCS} ${SHARED_HDRS}) -target_link_libraries(SchematicFileSerializer-exe zlib fmt::fmt) +target_link_libraries(SchematicFileSerializer-exe fmt::fmt libdeflate) add_test(NAME SchematicFileSerializer-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND SchematicFileSerializer-exe) diff --git a/tests/SchematicFileSerializer/SchematicFileSerializerTest.cpp b/tests/SchematicFileSerializer/SchematicFileSerializerTest.cpp index f4db9852c..0e1232ec6 100644 --- a/tests/SchematicFileSerializer/SchematicFileSerializerTest.cpp +++ b/tests/SchematicFileSerializer/SchematicFileSerializerTest.cpp @@ -16,10 +16,9 @@ static void DoTest(void) cBlockArea ba; ba.Create(21, 256, 21); ba.RelLine(0, 0, 0, 9, 8, 7, cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_WOODEN_STAIRS, 1); - AString Schematic; - TEST_TRUE(cSchematicFileSerializer::SaveToSchematicString(ba, Schematic)); + const auto Schematic = cSchematicFileSerializer::SaveToSchematicString(ba); cBlockArea ba2; - TEST_TRUE(cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic)); + cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic.GetView()); } |