1
0
Fork 0

zlib -> libdeflate (#5085)

+ Use libdeflate
+ Use std::byte
* Fix passing temporary to string_view
+ Emulate make_unique_for_overwrite
This commit is contained in:
Tiger Wang 2021-01-11 16:39:43 +00:00 committed by GitHub
parent 00c0a23ace
commit eeb63b8901
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 1419 additions and 2040 deletions

7
.gitmodules vendored
View File

@ -55,10 +55,6 @@
path = lib/tolua++ path = lib/tolua++
url = https://github.com/cuberite/toluapp.git url = https://github.com/cuberite/toluapp.git
ignore = dirty ignore = dirty
[submodule "lib/zlib"]
path = lib/zlib
url = https://github.com/cuberite/zlib.git
ignore = dirty
[submodule "lib/fmt"] [submodule "lib/fmt"]
path = lib/fmt path = lib/fmt
url = https://github.com/fmtlib/fmt.git url = https://github.com/fmtlib/fmt.git
@ -66,3 +62,6 @@
[submodule "Tools/BlockTypePaletteGenerator/lib/lunajson"] [submodule "Tools/BlockTypePaletteGenerator/lib/lunajson"]
path = Tools/BlockTypePaletteGenerator/lib/lunajson path = Tools/BlockTypePaletteGenerator/lib/lunajson
url = https://github.com/grafi-tt/lunajson.git url = https://github.com/grafi-tt/lunajson.git
[submodule "lib/libdeflate"]
path = lib/libdeflate
url = https://github.com/cuberite/libdeflate

View File

@ -28,7 +28,7 @@ function(build_dependencies)
# Enumerate all submodule libraries # Enumerate all submodule libraries
# SQLiteCpp needs to be included before sqlite so the lsqlite target is available: # 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}) foreach(DEPENDENCY ${DEPENDENCIES})
# Check that the libraries are present: # Check that the libraries are present:
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/${DEPENDENCY}/CMakeLists.txt") if (NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/${DEPENDENCY}/CMakeLists.txt")
@ -62,13 +62,13 @@ function(link_dependencies TARGET)
event_extra event_extra
fmt::fmt fmt::fmt
jsoncpp_lib jsoncpp_lib
libdeflate
lsqlite lsqlite
lualib lualib
luaexpat luaexpat
mbedtls mbedtls
SQLiteCpp SQLiteCpp
tolualib tolualib
zlib
) )
# Link process information library: # Link process information library:

View File

@ -9,6 +9,7 @@ function(group_sources)
expat expat
fmt fmt
jsoncpp_lib jsoncpp_lib
libdeflate
lualib lualib
luaexpat luaexpat
mbedcrypto mbedcrypto
@ -18,7 +19,6 @@ function(group_sources)
sqlite3 sqlite3
SQLiteCpp SQLiteCpp
tolualib tolualib
zlib
PROPERTIES FOLDER Libraries PROPERTIES FOLDER Libraries
) )

View File

@ -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).", 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 = SetViewDistance =
{ {
Params = Params =
@ -14459,7 +14448,7 @@ end
dtMagma = dtMagma =
{ {
Notes = "Damage from contact with a magma block" Notes = "Damage from contact with a magma block"
}, },
dtDrown = dtDrown =
{ {
Notes = "Damage received by drowning in water / lava" Notes = "Damage received by drowning in water / lava"

View File

@ -17,10 +17,6 @@ function(flatten_files arg1)
endfunction() endfunction()
# Include the libraries:
add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib)
# Include the shared files: # Include the shared files:
set(SHARED_SRC set(SHARED_SRC
../../src/StringCompression.cpp ../../src/StringCompression.cpp
@ -66,11 +62,9 @@ source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR})
# Include the main source files: # Include the main source files:
set(SOURCES set(SOURCES
MCADefrag.cpp MCADefrag.cpp
Globals.cpp
) )
set(HEADERS set(HEADERS
MCADefrag.h MCADefrag.h
Globals.h
) )
source_group("" FILES ${SOURCES} ${HEADERS}) source_group("" FILES ${SOURCES} ${HEADERS})
@ -84,7 +78,7 @@ add_executable(MCADefrag
${SHARED_OSS_HDR} ${SHARED_OSS_HDR}
) )
target_link_libraries(MCADefrag zlib fmt::fmt Threads::Threads) target_link_libraries(MCADefrag fmt::fmt libdeflate Threads::Threads)
include(../../SetFlags.cmake) include(../../SetFlags.cmake)
set_exe_flags(MCADefrag) set_exe_flags(MCADefrag)

View File

@ -1,10 +0,0 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

View File

@ -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))

View File

@ -8,7 +8,6 @@
#include "Logger.h" #include "Logger.h"
#include "LoggerSimple.h" #include "LoggerSimple.h"
#include "LoggerListeners.h" #include "LoggerListeners.h"
#include "zlib/zlib.h"
@ -129,7 +128,8 @@ AString cMCADefrag::GetNextFileName(void)
cMCADefrag::cThread::cThread(cMCADefrag & a_Parent) : cMCADefrag::cThread::cThread(cMCADefrag & a_Parent) :
super("MCADefrag thread"), super("MCADefrag thread"),
m_Parent(a_Parent), 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) bool cMCADefrag::cThread::UncompressChunkZlib(void)
{ {
// Uncompress the data: try
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)
{ {
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; 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) bool cMCADefrag::cThread::CompressChunk(void)
{ {
// Check that the compressed data can fit: try
uLongf CompressedSize = compressBound(static_cast<uLong>(m_RawChunkDataSize));
if (CompressedSize > sizeof(m_CompressedChunkData))
{ {
LOGINFO("Too much data for the internal compression buffer!"); // Compress the data (using the highest compression factor, as set in the constructor)
return false;
}
// Compress the data using the highest compression factor: const auto CompressedData = m_Compressor.CompressZLib(
int errorcode = compress2(m_CompressedChunkData + 1, &CompressedSize, m_RawChunkData, static_cast<uLong>(m_RawChunkDataSize), Z_BEST_COMPRESSION); {
if (errorcode != Z_OK) 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;
return true;
}
catch (const std::exception & Oops)
{ {
LOGINFO("Recompression failed: %d", errorcode); LOGWARNING("Recompression failed. %s", Oops.what());
return false; 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;
} }

View File

@ -13,10 +13,18 @@
#include "OSSupport/IsThread.h"
#include "StringCompression.h"
class cMCADefrag class cMCADefrag
{ {
public: public:
enum enum
{ {
MAX_COMPRESSED_CHUNK_DATA_SIZE = (1 MiB), MAX_COMPRESSED_CHUNK_DATA_SIZE = (1 MiB),
@ -33,6 +41,7 @@ public:
void Run(void); void Run(void);
protected: protected:
/** A single thread processing MCA files from the queue */ /** A single thread processing MCA files from the queue */
class cThread : class cThread :
public cIsThread public cIsThread
@ -40,9 +49,11 @@ protected:
typedef cIsThread super; typedef cIsThread super;
public: public:
cThread(cMCADefrag & a_Parent); cThread(cMCADefrag & a_Parent);
protected: protected:
/** The compression methods, as specified by the MCA compression method byte. */ /** The compression methods, as specified by the MCA compression method byte. */
enum enum
{ {
@ -75,6 +86,12 @@ protected:
WriteChunk() tests this flag to decide whether to call Compress(). */ WriteChunk() tests this flag to decide whether to call Compress(). */
bool m_IsChunkUncompressed; 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. */ /** Processes the specified file. */
void ProcessFile(const AString & a_FileName); void ProcessFile(const AString & a_FileName);

View File

@ -36,11 +36,9 @@ source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR})
# Include the main source files: # Include the main source files:
set(SOURCES set(SOURCES
NoiseSpeedTest.cpp NoiseSpeedTest.cpp
Globals.cpp
) )
set(HEADERS set(HEADERS
NoiseSpeedTest.h NoiseSpeedTest.h
Globals.h
SimplexNoise.h SimplexNoise.h
) )

View File

@ -1,10 +0,0 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

View File

@ -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);
}

View File

@ -15,8 +15,6 @@ function(flatten_files arg1)
set(${arg1} "${res}" PARENT_SCOPE) set(${arg1} "${res}" PARENT_SCOPE)
endfunction() endfunction()
add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib)
# Include the shared files: # Include the shared files:
set(SHARED_SRC set(SHARED_SRC
../../src/ByteBuffer.cpp ../../src/ByteBuffer.cpp
@ -33,6 +31,7 @@ set(SHARED_SRC
) )
set(SHARED_HDR set(SHARED_HDR
../../src/ByteBuffer.h ../../src/ByteBuffer.h
../../src/Globals.h
../../src/StringUtils.h ../../src/StringUtils.h
../../src/UUID.h ../../src/UUID.h
../../src/mbedTLS++/AesCfb128Decryptor.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: # Include the main source files:
set(SOURCES set(SOURCES
Connection.cpp Connection.cpp
Globals.cpp
ProtoProxy.cpp ProtoProxy.cpp
Server.cpp Server.cpp
) )
set(HEADERS set(HEADERS
Connection.h Connection.h
Globals.h
Server.h Server.h
) )
source_group("" FILES ${SOURCES} ${HEADERS}) source_group("" FILES ${SOURCES} ${HEADERS})
@ -92,7 +89,7 @@ add_executable(ProtoProxy
${SHARED_OSS_HDR} ${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) include(../../SetFlags.cmake)
set_exe_flags(ProtoProxy) set_exe_flags(ProtoProxy)

View File

@ -12,6 +12,8 @@
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> // For _mkdir() #include <direct.h> // For _mkdir()
#else
#include <sys/stat.h> // for mkdir
#endif #endif
@ -59,25 +61,25 @@
#define COPY_TO_SERVER() \ #define COPY_TO_SERVER() \
do { \ do { \
AString ToServer; \ ContiguousByteBuffer ToServer; \
m_ClientBuffer.ReadAgain(ToServer); \ m_ClientBuffer.ReadAgain(ToServer); \
switch (m_ServerState) \ switch (m_ServerState) \
{ \ { \
case csUnencrypted: \ case csUnencrypted: \
{ \ { \
SERVERSEND(ToServer.data(), ToServer.size()); \ SERVERSEND(ToServer); \
break; \ break; \
} \ } \
case csEncryptedUnderstood: \ case csEncryptedUnderstood: \
case csEncryptedUnknown: \ case csEncryptedUnknown: \
{ \ { \
SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ SERVERENCRYPTSEND(ToServer); \
break; \ break; \
} \ } \
case csWaitingForEncryption: \ case csWaitingForEncryption: \
{ \ { \
Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \ Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \
m_ServerEncryptionBuffer.append(ToServer.data(), ToServer.size()); \ m_ServerEncryptionBuffer += ToServer; \
break; \ break; \
} \ } \
} \ } \
@ -86,19 +88,19 @@
#define COPY_TO_CLIENT() \ #define COPY_TO_CLIENT() \
do { \ do { \
AString ToClient; \ ContiguousByteBuffer ToClient; \
m_ServerBuffer.ReadAgain(ToClient); \ m_ServerBuffer.ReadAgain(ToClient); \
switch (m_ClientState) \ switch (m_ClientState) \
{ \ { \
case csUnencrypted: \ case csUnencrypted: \
{ \ { \
CLIENTSEND(ToClient.data(), ToClient.size()); \ CLIENTSEND(ToClient); \
break; \ break; \
} \ } \
case csEncryptedUnderstood: \ case csEncryptedUnderstood: \
case csEncryptedUnknown: \ case csEncryptedUnknown: \
{ \ { \
CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ CLIENTENCRYPTSEND(ToClient); \
break; \ break; \
} \ } \
case csWaitingForEncryption: \ case csWaitingForEncryption: \
@ -114,7 +116,7 @@
do { \ do { \
if (!Proc) \ if (!Proc) \
{ \ { \
AString Leftover; \ ContiguousByteBuffer Leftover; \
m_ClientBuffer.ReadAgain(Leftover); \ m_ClientBuffer.ReadAgain(Leftover); \
DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \ DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \
m_ClientBuffer.ResetRead(); \ m_ClientBuffer.ResetRead(); \
@ -374,15 +376,15 @@ bool cConnection::RelayFromServer(void)
} }
case csEncryptedUnderstood: 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); DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res);
return DecodeServersPackets(Buffer, res); return DecodeServersPackets(Buffer, res);
} }
case csEncryptedUnknown: 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); 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"); ASSERT(!"Unhandled server state while relaying from server");
@ -419,8 +421,8 @@ bool cConnection::RelayFromClient(void)
case csEncryptedUnknown: case csEncryptedUnknown:
{ {
DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the CLIENT", res); 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)); m_ServerEncryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res));
return SERVERSEND(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"); 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) if (res <= 0)
{ {
Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, SocketError); 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) bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer)
{ {
AString All; ContiguousByteBuffer All;
a_Data.ReadAll(All); a_Data.ReadAll(All);
a_Data.CommitRead(); 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); DataLog(a_Data.data(), a_Data.size(), "Encrypting %zu bytes to %s", a_Data.size(), a_Peer);
const Byte * Data = reinterpret_cast<const Byte *>(a_Data); while (a_Data.size() > 0)
while (a_Size > 0)
{ {
Byte Buffer[64 KiB]; std::byte Buffer[64 KiB];
size_t NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size; size_t NumBytes = (a_Data.size() > sizeof(Buffer)) ? sizeof(Buffer) : a_Data.size();
a_Encryptor.ProcessData(Buffer, Data, NumBytes); a_Encryptor.ProcessData(Buffer, a_Data.data(), NumBytes);
bool res = SendData(a_Socket, reinterpret_cast<const char *>(Buffer), NumBytes, a_Peer); bool res = SendData(a_Socket, { Buffer, NumBytes }, a_Peer);
if (!res) if (!res)
{ {
return false; return false;
} }
Data += NumBytes; a_Data = a_Data.substr(NumBytes);
a_Size -= NumBytes;
} }
return true; 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) 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.ReadAll(All);
a_Data.CommitRead(); 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) if (PacketLen == 0)
{ {
m_ServerBuffer.ResetRead(); m_ServerBuffer.ResetRead();
AString All; ContiguousByteBuffer All;
m_ServerBuffer.ReadAll(All); m_ServerBuffer.ReadAll(All);
DataLog(All.data(), All.size(), "====== Received a bad packet length? Inspect the contents below ======"); 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 m_ServerBuffer.CommitRead(); // Try to recover by marking everything as read
@ -798,10 +798,11 @@ bool cConnection::HandleClientHandshake(void)
Packet.WriteVarUTF8String(ServerHost); Packet.WriteVarUTF8String(ServerHost);
Packet.WriteBEUInt16(m_Server.GetConnectPort()); Packet.WriteBEUInt16(m_Server.GetConnectPort());
Packet.WriteVarInt32(NextState); Packet.WriteVarInt32(NextState);
AString Pkt; ContiguousByteBuffer Pkt;
Packet.ReadAll(Pkt); Packet.ReadAll(Pkt);
cByteBuffer ToServer(512); cByteBuffer ToServer(512);
ToServer.WriteVarUTF8String(Pkt); ToServer.WriteVarInt32(static_cast<UInt32>(Pkt.size()));
ToServer.Write(Pkt.data(), Pkt.size());
SERVERSEND(ToServer); SERVERSEND(ToServer);
m_ClientProtocolState = static_cast<int>(NextState); 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(ReadVarUTF8String, AString, ChannelName);
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, Length); HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, Length);
AString Data; ContiguousByteBuffer Data;
if (!m_ClientBuffer.ReadString(Data, Length)) if (!m_ClientBuffer.ReadSome(Data, Length))
{ {
return false; return false;
} }
@ -1253,8 +1254,8 @@ bool cConnection::HandleClientWindowClose(void)
bool cConnection::HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar) bool cConnection::HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
{ {
AString Data; ContiguousByteBuffer Data;
if (!m_ClientBuffer.ReadString(Data, a_PacketLen - a_PacketReadSoFar)) if (!m_ClientBuffer.ReadSome(Data, a_PacketLen - a_PacketReadSoFar))
{ {
return false; return false;
} }
@ -1288,14 +1289,14 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
// Read the packet from the server: // Read the packet from the server:
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID); HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID);
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PublicKeyLength); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PublicKeyLength);
AString PublicKey; ContiguousByteBuffer PublicKey;
if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength)) if (!m_ServerBuffer.ReadSome(PublicKey, PublicKeyLength))
{ {
return false; return false;
} }
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NonceLength); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NonceLength);
AString Nonce; ContiguousByteBuffer Nonce;
if (!m_ServerBuffer.ReadString(Nonce, NonceLength)) if (!m_ServerBuffer.ReadSome(Nonce, NonceLength))
{ {
return false; return false;
} }
@ -1304,7 +1305,7 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
DataLog(PublicKey.data(), PublicKey.size(), " Public key (%u bytes)", static_cast<unsigned>(PublicKey.size())); DataLog(PublicKey.data(), PublicKey.size(), " Public key (%u bytes)", static_cast<unsigned>(PublicKey.size()));
// Reply to the server: // 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 // Do not send to client - we want the client connection open
return true; return true;
@ -1330,7 +1331,7 @@ bool cConnection::HandleServerLoginSuccess(void)
Log("Server communication is now encrypted"); Log("Server communication is now encrypted");
m_ServerState = csEncryptedUnderstood; m_ServerState = csEncryptedUnderstood;
DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size()); 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(); m_ServerEncryptionBuffer.clear();
} }
COPY_TO_CLIENT(); COPY_TO_CLIENT();
@ -1827,8 +1828,8 @@ bool cConnection::HandleServerKick(void)
AString PacketStart("\xff"); AString PacketStart("\xff");
PacketStart.push_back(static_cast<char>(ReasonBE16.size() / 256)); PacketStart.push_back(static_cast<char>(ReasonBE16.size() / 256));
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 std::byte *>(PacketStart.data()), PacketStart.size() });
CLIENTSEND(reinterpret_cast<const char *>(ReasonBE16.data()), ReasonBE16.size() * sizeof(char16_t)); CLIENTSEND({ reinterpret_cast<const std::byte *>(ReasonBE16.data()), ReasonBE16.size() * sizeof(char16_t) });
return true; return true;
} }
else else
@ -1856,8 +1857,8 @@ bool cConnection::HandleServerMapChunk(void)
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PrimaryBitmap); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PrimaryBitmap);
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, AdditionalBitmap); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, AdditionalBitmap);
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
AString CompressedData; ContiguousByteBuffer CompressedData;
if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize))
{ {
return false; return false;
} }
@ -1880,8 +1881,8 @@ bool cConnection::HandleServerMapChunkBulk(void)
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ChunkCount); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ChunkCount);
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent); HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent);
AString CompressedData; ContiguousByteBuffer CompressedData;
if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize))
{ {
return false; return false;
} }
@ -1930,8 +1931,8 @@ bool cConnection::HandleServerMultiBlockChange(void)
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ); HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ);
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumBlocks); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumBlocks);
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataSize); HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataSize);
AString BlockChangeData; ContiguousByteBuffer BlockChangeData;
if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize)) if (!m_ServerBuffer.ReadSome(BlockChangeData, DataSize))
{ {
return false; return false;
} }
@ -2039,8 +2040,8 @@ bool cConnection::HandleServerPluginMessage(void)
{ {
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName); HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Length); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Length);
AString Data; ContiguousByteBuffer Data;
if (!m_ServerBuffer.ReadString(Data, Length)) if (!m_ServerBuffer.ReadSome(Data, Length))
{ {
return false; return false;
} }
@ -2278,7 +2279,7 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
#ifdef _DEBUG #ifdef _DEBUG
// DEBUG: // DEBUG:
// This packet is still troublesome when DataIndicator != 0 // This packet is still troublesome when DataIndicator != 0
AString Buffer; ContiguousByteBuffer Buffer;
m_ServerBuffer.ResetRead(); m_ServerBuffer.ResetRead();
m_ServerBuffer.ReadAll(Buffer); m_ServerBuffer.ReadAll(Buffer);
m_ServerBuffer.ResetRead(); m_ServerBuffer.ResetRead();
@ -2328,7 +2329,7 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
} }
// TODO: Splash potions // TODO: Splash potions
} }
if ((ExtraLen > 0) && !m_ServerBuffer.ReadString(ExtraData, ExtraLen)) if ((ExtraLen > 0) && !m_ServerBuffer.ReadSome(ExtraData, ExtraLen))
{ {
return false; return false;
} }
@ -2449,10 +2450,11 @@ bool cConnection::HandleServerStatusResponse(void)
cByteBuffer Packet(Response.size() + 50); cByteBuffer Packet(Response.size() + 50);
Packet.WriteVarInt32(0); // Packet type - status response Packet.WriteVarInt32(0); // Packet type - status response
Packet.WriteVarUTF8String(Response); Packet.WriteVarUTF8String(Response);
AString Pkt; ContiguousByteBuffer Pkt;
Packet.ReadAll(Pkt); Packet.ReadAll(Pkt);
cByteBuffer ToClient(Response.size() + 50); cByteBuffer ToClient(Response.size() + 50);
ToClient.WriteVarUTF8String(Pkt); ToClient.WriteVarInt32(static_cast<UInt32>(Pkt.size()));
ToClient.Write(Pkt.data(), Pkt.size());
CLIENTSEND(ToClient); CLIENTSEND(ToClient);
return true; return true;
} }
@ -2545,8 +2547,8 @@ bool cConnection::HandleServerUpdateTileEntity(void)
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Action); HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Action);
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, DataLength); HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, DataLength);
AString Data; ContiguousByteBuffer Data;
if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength)) if ((DataLength > 0) && !m_ServerBuffer.ReadSome(Data, DataLength))
{ {
return false; return false;
} }
@ -2662,9 +2664,9 @@ bool cConnection::HandleServerWindowOpen(void)
bool cConnection::HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar) bool cConnection::HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
{ {
AString Data; ContiguousByteBuffer Data;
ASSERT(a_PacketLen >= a_PacketReadSoFar); 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; return false;
} }
@ -2764,9 +2766,9 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
rs = rs - static_cast<int>(a_Buffer.GetReadableSpace()); rs = rs - static_cast<int>(a_Buffer.GetReadableSpace());
cByteBuffer LenBuf(8); cByteBuffer LenBuf(8);
LenBuf.WriteVarInt32(Len); LenBuf.WriteVarInt32(Len);
AString VarLen; ContiguousByteBuffer VarLen;
LenBuf.ReadAll(VarLen); LenBuf.ReadAll(VarLen);
a_Metadata.append(VarLen); a_Metadata += { reinterpret_cast<const char *>(VarLen.data()), VarLen.size() };
Length = Len; Length = Len;
break; break;
} }
@ -2794,12 +2796,12 @@ bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
} // switch (Type) } // switch (Type)
// Read the data in this item: // Read the data in this item:
AString data; ContiguousByteBuffer data;
if (!a_Buffer.ReadString(data, Length)) if (!a_Buffer.ReadSome(data, Length))
{ {
return false; return false;
} }
a_Metadata.append(data); a_Metadata += { reinterpret_cast<const char *>(data.data()), data.size() };
if (!a_Buffer.ReadBEUInt8(x)) if (!a_Buffer.ReadBEUInt8(x))
{ {
return false; return false;

View File

@ -13,6 +13,10 @@
#include "mbedTLS++/AesCfb128Decryptor.h" #include "mbedTLS++/AesCfb128Decryptor.h"
#include "mbedTLS++/AesCfb128Encryptor.h" #include "mbedTLS++/AesCfb128Encryptor.h"
#ifndef _WIN32
typedef int SOCKET;
#endif
@ -85,7 +89,7 @@ protected:
cAesCfb128Decryptor m_ServerDecryptor; cAesCfb128Decryptor m_ServerDecryptor;
cAesCfb128Encryptor m_ServerEncryptor; 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 */ /** Set to true when PACKET_PING is received from the client; will cause special parsing for server kick */
bool m_HasClientPinged; bool m_HasClientPinged;
@ -119,13 +123,13 @@ protected:
double GetRelativeTime(void); double GetRelativeTime(void);
/** Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. */ /** 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. */ /** 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); 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 */ /** 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 */ /** 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); bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);

View File

@ -1,10 +0,0 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

View File

@ -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))

View File

@ -9,8 +9,20 @@
#pragma once #pragma once
#include "Globals.h"
#include "mbedTLS++/RsaPrivateKey.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; SOCKET m_ListenSocket;
cRsaPrivateKey m_PrivateKey; cRsaPrivateKey m_PrivateKey;
AString m_PublicKeyDER; ContiguousByteBuffer m_PublicKeyDER;
UInt16 m_ConnectPort; UInt16 m_ConnectPort;
public: public:
@ -30,7 +42,7 @@ public:
void Run(void); void Run(void);
cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } 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; } UInt16 GetConnectPort(void) const { return m_ConnectPort; }
} ; } ;

1
lib/libdeflate Submodule

@ -0,0 +1 @@
Subproject commit e0f46c3c9fbb4213ac7534b2bfe5cfc78e07c1f5

@ -1 +0,0 @@
Subproject commit a9c7b30641ed8b86590141a1b4025263bce4c881

View File

@ -863,7 +863,7 @@ void cLuaState::Push(const AStringVector & a_Vector)
int index = 1; int index = 1;
for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) 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); 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) void cLuaState::Push(const Vector3d & a_Vector)
{ {
ASSERT(IsValid()); 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) bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
{ {
if (lua_isnumber(m_LuaState, a_StackPos)) if (lua_isnumber(m_LuaState, a_StackPos))

View File

@ -619,6 +619,7 @@ public:
void Push(const cItem & a_Item); void Push(const cItem & a_Item);
void Push(const cNil & a_Nil); void Push(const cNil & a_Nil);
void Push(const cRef & a_Ref); void Push(const cRef & a_Ref);
void Push(ContiguousByteBufferView a_Data);
void Push(const Vector3d & a_Vector); void Push(const Vector3d & a_Vector);
void Push(const Vector3i & 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, cTrackedRef & a_Ref);
bool GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref); bool GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref);
bool GetStackValue(int a_StackPos, cTrackedRefSharedPtr & 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, double & a_Value);
bool GetStackValue(int a_StackPos, eBlockFace & a_Value); bool GetStackValue(int a_StackPos, eBlockFace & a_Value);
bool GetStackValue(int a_StackPos, eWeather & a_Value); bool GetStackValue(int a_StackPos, eWeather & a_Value);

View File

@ -188,9 +188,7 @@ static int tolua_CompressStringZLIB(lua_State * tolua_S)
S.GetStackValues(1, ToCompress, CompressionLevel); S.GetStackValues(1, ToCompress, CompressionLevel);
// Compress the string: // Compress the string:
AString res; S.Push(Compression::Compressor(CompressionLevel).CompressZLib(ToCompress.data(), ToCompress.size()).GetView());
CompressString(ToCompress.data(), ToCompress.size(), res, CompressionLevel);
S.Push(res);
return 1; return 1;
} }
@ -211,14 +209,21 @@ static int tolua_UncompressStringZLIB(lua_State * tolua_S)
} }
// Get the params: // Get the params:
AString ToUncompress; ContiguousByteBuffer ToUncompress;
size_t UncompressedSize = 0; size_t UncompressedSize = 0;
S.GetStackValues(1, ToUncompress, UncompressedSize); S.GetStackValues(1, ToUncompress, UncompressedSize);
// Compress the string: try
AString res; {
UncompressString(ToUncompress.data(), ToUncompress.size(), res, UncompressedSize); // Decompress the string:
S.Push(res); S.Push(Compression::Extractor().ExtractZLib(ToUncompress, UncompressedSize).GetView());
}
catch (const std::exception & Oops)
{
LOGWARNING(Oops.what());
cLuaState::LogStackTrace(tolua_S);
return 0;
}
return 1; return 1;
} }
@ -239,40 +244,11 @@ static int tolua_CompressStringGZIP(lua_State * tolua_S)
} }
// Get the params: // Get the params:
AString ToCompress; ContiguousByteBuffer ToCompress;
S.GetStackValues(1, ToCompress); S.GetStackValues(1, ToCompress);
// Compress the string: // Compress the string:
AString res; S.Push(Compression::Compressor().CompressGZip(ToCompress).GetView());
CompressStringGZIP(ToCompress.data(), ToCompress.size(), res);
S.Push(res);
return 1;
}
static int tolua_UncompressStringGZIP(lua_State * tolua_S)
{
cLuaState S(tolua_S);
if (
!S.CheckParamString(1) ||
!S.CheckParamEnd(2)
)
{
cLuaState::LogStackTrace(tolua_S);
return 0;
}
// Get the params:
AString ToUncompress;
S.GetStackValues(1, ToUncompress);
// Compress the string:
AString res;
UncompressStringGZIP(ToUncompress.data(), ToUncompress.size(), res);
S.Push(res);
return 1; return 1;
} }
@ -293,13 +269,20 @@ static int tolua_InflateString(lua_State * tolua_S)
} }
// Get the params: // Get the params:
AString ToUncompress; ContiguousByteBuffer ToUncompress;
S.GetStackValues(1, ToUncompress); S.GetStackValues(1, ToUncompress);
// Compress the string: try
AString res; {
InflateString(ToUncompress.data(), ToUncompress.size(), res); // Decompress the string:
S.Push(res); S.Push(Compression::Extractor().ExtractZLib(ToUncompress).GetView());
}
catch (const std::exception & Oops)
{
LOGWARNING(Oops.what());
cLuaState::LogStackTrace(tolua_S);
return 0;
}
return 1; return 1;
} }
@ -4552,7 +4535,7 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB); tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB);
tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB); tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB);
tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP); 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_function(tolua_S, "InflateString", tolua_InflateString);
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);

View File

@ -435,7 +435,17 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * a_LuaState)
return L.ApiParamError("Invalid 'self', must not be nil"); 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; return 1;
} }
@ -457,7 +467,7 @@ static int tolua_cBlockArea_LoadFromSchematicString(lua_State * a_LuaState)
return 0; return 0;
} }
cBlockArea * self; cBlockArea * self;
AString data; ContiguousByteBuffer data;
if (!L.GetStackValues(1, self, data)) if (!L.GetStackValues(1, self, data))
{ {
return L.ApiParamError("Cannot read the parameters"); 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"); 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; return 1;
} }
@ -625,7 +645,17 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * a_LuaState)
return L.ApiParamError("Invalid 'self', must not be nil"); 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; return 1;
} }
@ -655,13 +685,17 @@ static int tolua_cBlockArea_SaveToSchematicString(lua_State * a_LuaState)
return L.ApiParamError("Invalid 'self', must not be nil"); return L.ApiParamError("Invalid 'self', must not be nil");
} }
AString data; try
if (cSchematicFileSerializer::SaveToSchematicString(*self, data))
{ {
L.Push(data); L.Push(cSchematicFileSerializer::SaveToSchematicString(*self).GetView());
return 1; return 1;
} }
return 0; catch (const std::exception & Oops)
{
LOGWARNING(Oops.what());
L.LogStackTrace();
return 0;
}
} }

View File

@ -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 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 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 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 OnPluginsLoaded (void) = 0;
virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 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; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0;

View File

@ -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); return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message);
} }

View File

@ -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 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 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 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 OnPluginsLoaded (void) override;
virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) 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; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;

View File

@ -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) return GenericCallHook(HOOK_PLUGIN_MESSAGE, [&](cPlugin * a_Plugin)
{ {

View File

@ -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 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 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 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 CallHookPluginsLoaded (void);
bool CallHookPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);

View File

@ -83,7 +83,7 @@ Unfortunately it is very slow, so it is disabled even for regular DEBUG builds.
// cByteBuffer: // cByteBuffer:
cByteBuffer::cByteBuffer(size_t a_BufferSize) : 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_BufferSize(a_BufferSize + 1),
m_DataStart(0), m_DataStart(0),
m_WritePos(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); 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) bool cByteBuffer::WriteBEUInt8(UInt8 a_Value)
{ {
CHECK_THREAD 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 CHECK_THREAD
CheckValid(); 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 CHECK_THREAD
CheckValid(); 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) // 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 // 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; return Count;
} }

View File

@ -11,6 +11,8 @@
// fwd: // fwd:
class cUUID; class cUUID;
@ -87,6 +89,7 @@ public:
// Write the specified datatype; return true if successfully written // Write the specified datatype; return true if successfully written
bool WriteBEInt8 (Int8 a_Value); bool WriteBEInt8 (Int8 a_Value);
bool WriteBEInt8 (std::byte a_Value);
bool WriteBEInt16 (Int16 a_Value); bool WriteBEInt16 (Int16 a_Value);
bool WriteBEInt32 (Int32 a_Value); bool WriteBEInt32 (Int32 a_Value);
bool WriteBEInt64 (Int64 a_Value); bool WriteBEInt64 (Int64 a_Value);
@ -111,13 +114,13 @@ public:
bool WriteBuf(const void * a_Buffer, size_t a_Count); bool WriteBuf(const void * a_Buffer, size_t a_Count);
/** Reads a_Count bytes into a_String; returns true if successful */ /** 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 */ /** Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer */
bool SkipRead(size_t a_Count); bool SkipRead(size_t a_Count);
/** Reads all available data into a_Data */ /** 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. */ /** 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); bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes);
@ -129,7 +132,7 @@ public:
void ResetRead(void); 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 */ /** 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 */ /** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */
void CheckValid(void) const; void CheckValid(void) const;
@ -138,7 +141,8 @@ public:
static size_t GetVarIntSize(UInt32 a_Value); static size_t GetVarIntSize(UInt32 a_Value);
protected: protected:
char * m_Buffer;
std::byte * m_Buffer;
size_t m_BufferSize; // Total size of the ringbuffer size_t m_BufferSize; // Total size of the ringbuffer
size_t m_DataStart; // Where the data starts in 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 */ /** Advances the m_ReadPos by a_Count bytes */
void AdvanceReadPos(size_t a_Count); void AdvanceReadPos(size_t a_Count);
} ; } ;

View File

@ -18,6 +18,7 @@ target_sources(
ChunkMap.cpp ChunkMap.cpp
ChunkSender.cpp ChunkSender.cpp
ChunkStay.cpp ChunkStay.cpp
CircularBufferCompressor.cpp
ClientHandle.cpp ClientHandle.cpp
Color.cpp Color.cpp
CommandOutput.cpp CommandOutput.cpp
@ -88,6 +89,7 @@ target_sources(
ChunkMap.h ChunkMap.h
ChunkSender.h ChunkSender.h
ChunkStay.h ChunkStay.h
CircularBufferCompressor.h
ClientHandle.h ClientHandle.h
Color.h Color.h
CommandOutput.h CommandOutput.h

View File

@ -11,7 +11,6 @@
#include "World.h" #include "World.h"
#include "ClientHandle.h" #include "ClientHandle.h"
#include "Server.h" #include "Server.h"
#include "zlib/zlib.h"
#include "Defines.h" #include "Defines.h"
#include "BlockEntities/BeaconEntity.h" #include "BlockEntities/BeaconEntity.h"
#include "BlockEntities/BedEntity.h" #include "BlockEntities/BedEntity.h"

View File

@ -22,9 +22,6 @@
#include "DeadlockDetect.h" #include "DeadlockDetect.h"
#include "BlockEntities/BlockEntity.h" #include "BlockEntities/BlockEntity.h"
#include "zlib/zlib.h"
#include "json/json.h"

View File

@ -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);
}

View File

@ -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;
};

View File

@ -678,7 +678,7 @@ void cClientHandle::HandlePing(void)
bool cClientHandle::HandleLogin(const AString & a_Username) bool cClientHandle::HandleLogin()
{ {
{ {
cCSLock lock(m_CSState); 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()); // 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: // 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!"); SendDisconnect("Login Rejected!");
return false; 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") if (a_Channel == "REGISTER")
{ {
@ -903,7 +901,7 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
} }
else if (a_Channel == "FML|HS") 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)) 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. // Break the string on each NUL character.
// Note that StringSplit() doesn't work on this because NUL is a special char - string terminator // 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; AStringVector res;
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
{ {
if (a_PluginChannels[i] != 0) if (a_PluginChannels[i] != std::byte(0))
{ {
continue; continue;
} }
if (i > first) 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; first = i + 1;
} // for i - a_PluginChannels[] } // for i - a_PluginChannels[]
if (first < len) 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; 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) if (m_HasSentDC)
{ {
@ -2010,7 +2010,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
} }
cCSLock Lock(m_CSOutgoingData); 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); 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 player has been kicked, terminate the connection:
if (m_State == csKicked) 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); 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: // Add the chunk to the list of chunks sent to the player:
{ {
cCSLock Lock(m_CSChunkLists); 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); 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: // Send any queued outgoing data:
AString OutgoingData; ContiguousByteBuffer OutgoingData;
{ {
cCSLock Lock(m_CSOutgoingData); cCSLock Lock(m_CSOutgoingData);
std::swap(OutgoingData, m_OutgoingData); std::swap(OutgoingData, m_OutgoingData);

View File

@ -153,7 +153,7 @@ public: // tolua_export
void SendChatAboveActionBar (const cCompositeChat & a_Message); void SendChatAboveActionBar (const cCompositeChat & a_Message);
void SendChatSystem (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChatSystem (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
void SendChatSystem (const cCompositeChat & a_Message); 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 SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count);
void SendDestroyEntity (const cEntity & a_Entity); void SendDestroyEntity (const cEntity & a_Entity);
void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle); void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle);
@ -192,7 +192,8 @@ public: // tolua_export
void SendPlayerMoveLook (void); void SendPlayerMoveLook (void);
void SendPlayerPosition (void); void SendPlayerPosition (void);
void SendPlayerSpawn (const cPlayer & a_Player); 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 SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
void SendResourcePack (const AString & a_ResourcePackUrl); void SendResourcePack (const AString & a_ResourcePackUrl);
void SendResetTitle (void); // tolua_export void SendResetTitle (void); // tolua_export
@ -231,9 +232,10 @@ public: // tolua_export
void SendWindowOpen (const cWindow & a_Window); void SendWindowOpen (const cWindow & a_Window);
void SendWindowProperty (const cWindow & a_Window, size_t a_Property, short a_Value); 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 // 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()); } 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 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 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 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); void HandleSlotSelected (Int16 a_SlotNum);
@ -379,11 +381,10 @@ public: // tolua_export
void HandleCraftRecipe (UInt32 a_RecipeId); void HandleCraftRecipe (UInt32 a_RecipeId);
/** Called when the protocol has finished logging the user in. /** Called when the protocol has finished logging the user in.
Return true to allow the user in; false to kick them. Return true to allow the user in; false to kick them. */
*/ bool HandleLogin();
bool HandleLogin(const AString & a_Username);
void SendData(const char * a_Data, size_t a_Size); void SendData(ContiguousByteBufferView a_Data);
/** Called when the player moves into a different world. /** Called when the player moves into a different world.
Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */ 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). /** Buffer for storing outgoing data from any thread; will get sent in Tick() (to prevent deadlocks).
Protected by m_CSOutgoingData. */ Protected by m_CSOutgoingData. */
AString m_OutgoingData; ContiguousByteBuffer m_OutgoingData;
Vector3d m_ConfirmPosition; Vector3d m_ConfirmPosition;
@ -580,7 +581,7 @@ private:
void FinishDigAnimation(); void FinishDigAnimation();
/** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ /** 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. */ /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */
void RegisterPluginChannels(const AStringVector & a_ChannelList); void RegisterPluginChannels(const AStringVector & a_ChannelList);

View File

@ -430,10 +430,8 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(finisher, "DirtPockets") == 0) else if (NoCaseCompare(finisher, "DirtPockets") == 0)
{ {
auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 1, cFinishGenOrePockets::DefaultNaturalPatches()); auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 1, cFinishGenOrePockets::DefaultNaturalPatches());
if (gen->Initialize(a_IniFile, "DirtPockets")) gen->Initialize(a_IniFile, "DirtPockets");
{ m_FinishGens.push_back(gen);
m_FinishGens.push_back(gen);
}
} }
else if (NoCaseCompare(finisher, "DistortedMembraneOverhangs") == 0) 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))); 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) else if (NoCaseCompare(finisher, "NetherOreNests") == 0)
{ {
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(m_Seed + 2, cFinishGenOreNests::DefaultNetherOres())); 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) else if (NoCaseCompare(finisher, "OrePockets") == 0)
{ {
auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 2, cFinishGenOrePockets::DefaultOverworldOres()); auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 2, cFinishGenOrePockets::DefaultOverworldOres());
if (gen->Initialize(a_IniFile, "OrePockets")) gen->Initialize(a_IniFile, "OrePockets");
{ m_FinishGens.push_back(gen);
m_FinishGens.push_back(gen);
}
} }
else if (NoCaseCompare(finisher, "OverworldClumpFlowers") == 0) 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))); 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) else if (NoCaseCompare(finisher, "Ravines") == 0)
{ {
m_FinishGens.push_back(cFinishGenPtr(new cStructGenRavines(m_Seed, 128))); 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))); 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) else if (NoCaseCompare(finisher, "Villages") == 0)
{ {
int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384); int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384);

View File

@ -1985,7 +1985,7 @@ void cFinishGenOreNests::GenerateOre(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cFinishGenOrePockets: // 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: // Read the OreInfos configuration:
auto valueName = a_GenName + "Blocks"; 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): // Read the optional seed configuration (but do not store the default):
valueName = a_GenName + "Seed"; valueName = a_GenName + "Seed";
SetSeed(a_IniFile.GetValueI("Generator", valueName, m_Noise.GetSeed())); SetSeed(a_IniFile.GetValueI("Generator", valueName, m_Noise.GetSeed()));
return true;
} }

View File

@ -589,9 +589,8 @@ public:
{} {}
/** Reads the configuration from the specified INI file. /** 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). 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. */ void Initialize(cIniFile & a_IniFile, const AString & a_GenName);
bool Initialize(cIniFile & a_IniFile, const AString & a_GenName);
protected: protected:

View File

@ -78,15 +78,6 @@ cPrefabPiecePool::cPrefabPiecePool(
cPrefabPiecePool::cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings)
{
LoadFromFile(a_FileName, a_LogWarnings);
}
cPrefabPiecePool::~cPrefabPiecePool() cPrefabPiecePool::~cPrefabPiecePool()
{ {
Clear(); 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 the contents start with GZip signature, ungzip and retry:
if (a_Contents.substr(0, 3) == "\x1f\x8b\x08") if (a_Contents.substr(0, 3) == "\x1f\x8b\x08")
{ {
AString Uncompressed; try
auto res = UncompressStringGZIP(a_Contents.data(), a_Contents.size(), Uncompressed);
if (res == Z_OK)
{ {
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; return false;
} }
} }
// Search the first 8 KiB of the file for the format auto-detection string: // 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) if (Header.find("CubesetFormatVersion =") != AString::npos)
{ {
return LoadFromCubeset(a_Contents, a_FileName, a_LogWarnings); 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; SchematicFileName = a_FileName.substr(0, PathEnd) + SchematicFileName;
} }
cBlockArea area; cBlockArea area;
if (!cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName)) try
{ {
CONDWARNING(a_LogWarnings, "Cannot load schematic file \"%s\" for piece %s in cubeset %s.", cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName);
SchematicFileName.c_str(), a_PieceName.c_str(), a_FileName.c_str() }
catch (const std::exception & Oops)
{
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; return nullptr;
} }

View File

@ -44,10 +44,6 @@ public:
int a_DefaultStartingPieceHeight = -1 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. */ /** Destroys the pool, freeing all pieces. */
virtual ~cPrefabPiecePool() override; virtual ~cPrefabPiecePool() override;

View File

@ -75,39 +75,8 @@
#endif #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 // 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 // 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: // OS-dependent stuff:
#ifdef _WIN32 #ifdef _WIN32
@ -161,6 +131,7 @@ template class SizeChecker<UInt8, 1>;
#include <cstdio> #include <cstdio>
#include <cmath> #include <cmath>
#include <cstdarg> #include <cstdarg>
#include <cstddef>
@ -188,10 +159,43 @@ template class SizeChecker<UInt8, 1>;
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #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): // Common headers (part 1, without macros):
#include "fmt.h" #include "fmt.h"
#include "StringUtils.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. */ /** Clamp X to the specified range. */
template <typename T> template <typename T>
T Clamp(T a_Value, T a_Min, T a_Max) 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 cTickTime = std::chrono::duration<int, std::ratio_multiply<std::chrono::milliseconds::period, std::ratio<50>>>;
using cTickTimeLong = std::chrono::duration<Int64, cTickTime::period>; 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 #ifndef TOLUA_TEMPLATE_BIND
#define TOLUA_TEMPLATE_BIND(x) #define TOLUA_TEMPLATE_BIND(x)
#endif #endif
@ -355,4 +376,3 @@ auto ToUnsigned(T a_Val)
// Common headers (part 2, with macros): // Common headers (part 2, with macros):
#include "Vector3.h" #include "Vector3.h"

View File

@ -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()); ASSERT(IsOpen());
if (!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. ContiguousByteBuffer res;
AString res; res.resize(a_NumBytes); // TODO: investigate if worth hacking around std::string internals to avoid initialisation
res.resize(a_NumBytes); auto newSize = fread(res.data(), sizeof(std::byte), a_NumBytes, m_File);
auto newSize = fread(const_cast<char *>(res.data()), 1, a_NumBytes, m_File);
res.resize(newSize); res.resize(newSize);
return res; return res;
} }
@ -284,9 +283,8 @@ int cFile::ReadRestOfFile(AString & a_Contents)
auto DataSize = static_cast<size_t>(TotalSize - Position); 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.resize(DataSize); // TODO: investigate if worth hacking around std::string internals to avoid initialisation
a_Contents.assign(DataSize, '\0'); return Read(a_Contents.data(), DataSize);
return Read(static_cast<void *>(const_cast<char *>(a_Contents.data())), DataSize);
} }
@ -709,3 +707,54 @@ void cFile::Flush(void)
{ {
fflush(m_File); 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

View File

@ -75,7 +75,7 @@ public:
int Read(void * a_Buffer, size_t a_NumBytes); 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 */ /** 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 */ /** 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); int Write(const void * a_Buffer, size_t a_NumBytes);
@ -192,17 +192,8 @@ class FileStream final : public StreamType
{ {
public: public:
FileStream(const std::string & Path) FileStream(const std::string & Path);
{ 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);
// Only subsequently except on serious errors, and not on conditions like EOF or malformed input:
FileStream::exceptions(FileStream::badbit);
}
}; };
@ -211,3 +202,15 @@ public:
using InputFileStream = FileStream<std::ifstream>; using InputFileStream = FileStream<std::ifstream>;
using OutputFileStream = FileStream<std::ofstream>; 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

View File

@ -4,107 +4,27 @@
// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines // Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
#include "Globals.h" #include "Globals.h"
#include "File.h"
#include "GZipFile.h" #include "GZipFile.h"
cGZipFile::cGZipFile(void) : Compression::Result GZipFile::ReadRestOfFile(const std::string & a_FileName)
m_File(nullptr), m_Mode(fmRead)
{ {
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() };
return Compression::Extractor().ExtractGZip(Data);
} }
cGZipFile::~cGZipFile() void GZipFile::Write(const std::string & a_FileName, ContiguousByteBufferView a_Contents)
{ {
Close(); OutputFileStream(a_FileName, OutputFileStream::binary) << Compression::Compressor().CompressGZip(a_Contents).GetStringView();
} }
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);
}
void cGZipFile::Close(void)
{
if (m_File != nullptr)
{
gzclose(m_File);
m_File = nullptr;
}
}
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);
}

View File

@ -1,7 +1,7 @@
// GZipFile.h // 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 #pragma once
#include "zlib/zlib.h" #include "StringCompression.h"
class cGZipFile namespace GZipFile
{ {
public: /** Reads the rest of the file and returns the decompressed contents. */
enum eMode Compression::Result ReadRestOfFile(const std::string & a_FileName);
{
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
} ;
cGZipFile(void); /** Writes a_Contents into file, compressing it along the way. */
~cGZipFile(); void Write(const std::string & a_FileName, ContiguousByteBufferView a_Contents);
/** 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;
} ; } ;

View File

@ -1,6 +1,5 @@
#include "Globals.h" #include "Globals.h"
#include "ChunkDataSerializer.h" #include "ChunkDataSerializer.h"
#include "zlib/zlib.h"
#include "Protocol_1_8.h" #include "Protocol_1_8.h"
#include "Protocol_1_9.h" #include "Protocol_1_9.h"
#include "../ClientHandle.h" #include "../ClientHandle.h"
@ -543,14 +542,10 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh
inline void cChunkDataSerializer::CompressPacketInto(ChunkDataCache & a_Cache) inline void cChunkDataSerializer::CompressPacketInto(ChunkDataCache & a_Cache)
{ {
m_Packet.ReadAll(a_Cache.PacketData); m_Compressor.ReadFrom(m_Packet);
m_Packet.CommitRead(); m_Packet.CommitRead();
if (!cProtocol_1_8_0::CompressPacket(a_Cache.PacketData, a_Cache.ToSend)) cProtocol_1_8_0::CompressPacket(m_Compressor, a_Cache.ToSend);
{
ASSERT(!"Packet compression failed.");
return;
}
a_Cache.Engaged = true; a_Cache.Engaged = true;
} }

View File

@ -3,6 +3,8 @@
#include "../ByteBuffer.h" #include "../ByteBuffer.h"
#include "../ChunkData.h" #include "../ChunkData.h"
#include "../Defines.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. */ /** A single cache entry containing the raw data, compressed data, and a validity flag. */
struct ChunkDataCache struct ChunkDataCache
{ {
std::string PacketData; ContiguousByteBuffer ToSend;
std::string ToSend;
bool Engaged = false; 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. */ 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); 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. /** 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. */ 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. */ /** A staging area used to construct the chunk packet, persistent to avoid reallocating. */
cByteBuffer m_Packet; cByteBuffer m_Packet;
/** A compressor used to compress the chunk data. */
CircularBufferCompressor m_Compressor;
/** The dimension for the World this Serializer is tied to. */ /** The dimension for the World this Serializer is tied to. */
const eDimension m_Dimension; const eDimension m_Dimension;
@ -81,7 +85,3 @@ protected:
It is used during a single invocation of SendToClients with more than one client. */ 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; std::array<ChunkDataCache, static_cast<size_t>(CacheVersion::Last) + 1> m_Cache;
} ; } ;

View File

@ -36,8 +36,8 @@ namespace ClientPhase
/** Server handshake state phases. */ /** Server handshake state phases. */
namespace ServerPhase namespace ServerPhase
{ {
static const Int8 WAITINGCACK = 2; static const auto WAITINGCACK = std::byte(2);
static const Int8 COMPLETE = 3; 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_UUID = a_UUID;
m_Properties = a_Properties; m_Properties = a_Properties;
static const std::array<AString, 5> Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; static const std::array<std::string_view, 5> Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }};
AString ChannelsString; ContiguousByteBuffer ChannelsString;
for (auto & Channel: Channels) for (auto & Channel: Channels)
{ {
ChannelsString.append(Channel); ChannelsString.append({ reinterpret_cast<const std::byte *>(Channel.data()), Channel.size() });
ChannelsString.push_back('\0'); ChannelsString.push_back(std::byte(0));
} }
m_Client->SendPluginMessage("REGISTER", ChannelsString); m_Client->SendPluginMessage("REGISTER", ChannelsString);
@ -123,7 +123,6 @@ void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID &
void cForgeHandshake::SendServerHello() void cForgeHandshake::SendServerHello()
{ {
AString Message;
cByteBuffer Buf(6); cByteBuffer Buf(6);
// Discriminator | Byte | Always 0 for ServerHello // Discriminator | Byte | Always 0 for ServerHello
Buf.WriteBEInt8(Discriminator::ServerHello); Buf.WriteBEInt8(Discriminator::ServerHello);
@ -131,6 +130,8 @@ void cForgeHandshake::SendServerHello()
Buf.WriteBEInt8(2); Buf.WriteBEInt8(2);
// Dimension TODO // Dimension TODO
Buf.WriteBEInt32(0); Buf.WriteBEInt32(0);
ContiguousByteBuffer Message;
Buf.ReadAll(Message); Buf.ReadAll(Message);
m_Client->SendPluginMessage("FML|HS", 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; 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; return Mods;
} }
cByteBuffer Buf(a_Size); cByteBuffer Buf(a_Data.size());
Buf.Write(a_Data, a_Size); Buf.Write(a_Data.data(), a_Data.size());
UInt32 NumMods; UInt32 NumMods;
if (!Buf.ReadVarInt32(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); LOGD("Received ClientHello with FML protocol version %d", FmlProtocolVersion);
if (FmlProtocolVersion != 2) if (FmlProtocolVersion != 2)
{ {
@ -195,7 +196,7 @@ void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a
} }
else 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"); LOGD("Received ModList");
auto ClientMods = ParseModList(a_Data + 1, a_Size - 1); auto ClientMods = ParseModList(a_Data.substr(1));
AString ClientModsString; AString ClientModsString;
for (auto & item: ClientMods) 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.first); // name
Buf.WriteVarUTF8String(item.second); // version Buf.WriteVarUTF8String(item.second); // version
} }
AString ServerModList;
ContiguousByteBuffer ServerModList;
Buf.ReadAll(ServerModList); Buf.ReadAll(ServerModList);
m_Client->SendPluginMessage("FML|HS", 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; return;
} }
auto Phase = a_Data[1]; const auto Phase = static_cast<Int8>(a_Data[1]);
LOGD("Received client HandshakeAck with phase = %d", Phase); LOGD("Received client HandshakeAck with phase = %d", Phase);
switch (Phase) switch (Phase)
@ -282,7 +284,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char *
Buf.WriteVarInt32(NumSubstitutions); Buf.WriteVarInt32(NumSubstitutions);
Buf.WriteVarInt32(NumDummies); Buf.WriteVarInt32(NumDummies);
AString RegistryData; ContiguousByteBuffer RegistryData;
Buf.ReadAll(RegistryData); Buf.ReadAll(RegistryData);
m_Client->SendPluginMessage("FML|HS", RegistryData); m_Client->SendPluginMessage("FML|HS", RegistryData);
break; break;
@ -292,8 +294,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char *
{ {
LOGD("Client finished receiving registry data; acknowledging"); LOGD("Client finished receiving registry data; acknowledging");
AString Ack; ContiguousByteBuffer Ack;
Ack.push_back(Discriminator::HandshakeAck); Ack.push_back(std::byte(Discriminator::HandshakeAck));
Ack.push_back(ServerPhase::WAITINGCACK); Ack.push_back(ServerPhase::WAITINGCACK);
m_Client->SendPluginMessage("FML|HS", Ack); m_Client->SendPluginMessage("FML|HS", Ack);
break; break;
@ -303,8 +305,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char *
{ {
LOGD("Client is pending completion; sending complete ack"); LOGD("Client is pending completion; sending complete ack");
AString Ack; ContiguousByteBuffer Ack;
Ack.push_back(Discriminator::HandshakeAck); Ack.push_back(std::byte(Discriminator::HandshakeAck));
Ack.push_back(ServerPhase::COMPLETE); Ack.push_back(ServerPhase::COMPLETE);
m_Client->SendPluginMessage("FML|HS", Ack); m_Client->SendPluginMessage("FML|HS", Ack);
@ -320,7 +322,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char *
default: default:
{ {
SetError(Printf("Received unknown phase in Forge handshake acknowledgement: %d", Phase)); SetError(fmt::format("Received unknown phase in Forge handshake acknowledgement: {}", Phase));
break; 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) 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; return;
} }
if (m_Errored) if (m_Errored)
@ -343,19 +345,18 @@ void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data
return; 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; return;
} }
auto Discriminator = a_Data[0]; const auto Discriminator = static_cast<Int8>(a_Data[0]);
switch (Discriminator) switch (Discriminator)
{ {
case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data, a_Size); break; case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data); break;
case Discriminator::ModList: HandleModList(a_Client, a_Data, a_Size); break; case Discriminator::ModList: HandleModList(a_Client, a_Data); break;
case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data, a_Size); break; case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data); break;
default: default:
{ {

View File

@ -34,7 +34,7 @@ public:
void SendServerHello(); void SendServerHello();
/** Process received data from the client advancing the Forge handshake. */ /** 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: private:
/** True if the Forge handshake is in an errored state. */ /** True if the Forge handshake is in an errored state. */
@ -48,13 +48,13 @@ private:
cUUID m_UUID; cUUID m_UUID;
Json::Value m_Properties; Json::Value m_Properties;
void HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size); void HandleClientHello(cClientHandle * a_Client, ContiguousByteBufferView a_Data);
void HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size); void HandleModList(cClientHandle * a_Client, ContiguousByteBufferView a_Data);
void HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size); void HandleHandshakeAck(cClientHandle * a_Client, ContiguousByteBufferView a_Data);
/** Set errored state to prevent further handshake message processing. */ /** Set errored state to prevent further handshake message processing. */
void SetError(const AString & message); void SetError(const AString & message);
/** Parse the client ModList packet of installed Forge mods and versions. */ /** 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);
}; };

View File

@ -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()));
} }

View File

@ -365,7 +365,7 @@ public:
virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0; 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 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 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 SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) = 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 SendPlayerMoveLook (void) = 0;
virtual void SendPlayerPosition (void) = 0; virtual void SendPlayerPosition (void) = 0;
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 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 SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
virtual void SendResetTitle (void) = 0; virtual void SendResetTitle (void) = 0;
virtual void SendResourcePack (const AString & a_ResourcePackUrl) = 0; virtual void SendResourcePack (const AString & a_ResourcePackUrl) = 0;
@ -468,7 +468,7 @@ protected:
virtual Version GetProtocolVersion() = 0; 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. */ /** 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. /** 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). */ The cPacketizer's destructor calls this to send the contained packet; protocol may transform the data (compression in 1.8 etc). */

View File

@ -123,11 +123,6 @@ void cMultiVersionProtocol::HandleIncomingDataInRecognitionStage(cClientHandle &
HandleIncomingDataInOldPingResponseStage(a_Clyent, a_In); 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: // Explicitly process any remaining data with the new handler:
HandleIncomingData(a_Client, a_Data); 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: // Compression doesn't apply to this state, send raw data:
VERIFY(OutPacketLenBuffer.WriteVarInt32(PacketLen)); VERIFY(OutPacketLenBuffer.WriteVarInt32(PacketLen));
AString LengthData; ContiguousByteBuffer LengthData;
OutPacketLenBuffer.ReadAll(LengthData); OutPacketLenBuffer.ReadAll(LengthData);
a_Client.SendData(LengthData.data(), LengthData.size()); a_Client.SendData(LengthData);
// Send the packet's payload: // Send the packet's payload:
AString PacketData; ContiguousByteBuffer PacketData;
a_OutPacketBuffer.ReadAll(PacketData); a_OutPacketBuffer.ReadAll(PacketData);
a_OutPacketBuffer.CommitRead(); a_OutPacketBuffer.CommitRead();
a_Client.SendData(PacketData.data(), PacketData.size()); a_Client.SendData(PacketData);
} }

View File

@ -679,7 +679,7 @@ void cProtocol_1_10_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity
} }
Writer.Finish(); Writer.Finish();
a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); a_Pkt.WriteBuf(Writer.GetResult());
} }

View File

@ -520,7 +520,7 @@ void cProtocol_1_11_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity
} }
Writer.Finish(); Writer.Finish();
a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); a_Pkt.WriteBuf(Writer.GetResult());
} }

View File

@ -1045,7 +1045,7 @@ void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer)
{ {
// TODO not yet used, not sure if it is needed // TODO not yet used, not sure if it is needed
// https://wiki.vg/index.php?title=Protocol&oldid=14204#Crafting_Book_Data // 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) 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."); m_Client->GetPlayer()->SendMessageInfo("The new advancements are not implemented.");
} }

View File

@ -298,13 +298,14 @@ void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
m_Client->SetClientBrand(Brand); m_Client->SetClientBrand(Brand);
// Send back our brand, including the length: // Send back our brand, including the length:
SendPluginMessage("minecraft:brand", "\x08""Cuberite"); m_Client->SendPluginMessage("minecraft:brand", "\x08""Cuberite");
return; return;
} }
ContiguousByteBuffer Data;
// Read the plugin message and relay to clienthandle: // Read the plugin message and relay to clienthandle:
AString Data; VERIFY(a_ByteBuffer.ReadSome(Data, a_ByteBuffer.GetReadableSpace())); // Always succeeds
VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds
m_Client->HandlePluginMessage(Channel, Data); 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(); a_Item.Empty();
} }
AString Metadata; ContiguousByteBuffer Metadata;
if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0)))
{ {
// No metadata // No metadata
return true; return true;
@ -1423,8 +1424,8 @@ bool cProtocol_1_13_2::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size
a_Item.Empty(); a_Item.Empty();
} }
AString Metadata; ContiguousByteBuffer Metadata;
if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0)))
{ {
// No metadata // No metadata
return true; return true;

View File

@ -231,8 +231,7 @@ bool cProtocol_1_14::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketTyp
// Game // Game
switch (a_PacketType) switch (a_PacketType)
{ {
default: AString dum; a_ByteBuffer.ReadAll(dum); a_ByteBuffer.CommitRead(); a_ByteBuffer.Write(" ", 1); default: a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); a_ByteBuffer.CommitRead(); return true;
return true;
} }
} }

View File

@ -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 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. 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) if (m_IsEncrypted)
{ {
Byte Decrypted[512]; std::byte Decrypted[512];
while (a_Size > 0) while (a_Size > 0)
{ {
size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; 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? ASSERT(m_State == 3); // In game mode?
cCSLock Lock(m_CSPacket); 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? ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktPluginMessage); cPacketizer Pkt(*this, pktPluginMessage);
Pkt.WriteString(a_Channel); 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. /* 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 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 LengthHeaderBuffer(
cByteBuffer::GetVarIntSize(PacketSize) + 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(PacketSize);
LengthHeaderBuffer.WriteVarInt32(DataSize); LengthHeaderBuffer.WriteVarInt32(DataSize);
AString LengthData; ContiguousByteBuffer LengthData;
LengthHeaderBuffer.ReadAll(LengthData); LengthHeaderBuffer.ReadAll(LengthData);
a_CompressedData.reserve(LengthData.size() + UncompressedSize); a_CompressedData.reserve(LengthData.size() + Uncompressed.size());
a_CompressedData.assign(LengthData.data(), LengthData.size()); a_CompressedData = LengthData;
a_CompressedData.append(a_Packet); a_CompressedData += Uncompressed;
return true; return;
} }
/* Definitely worth compressing. /* Definitely worth compressing.
@ -1765,28 +1764,11 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr
---------------------------------------------- ----------------------------------------------
*/ */
// Compress the data: const auto CompressedData = a_Packet.Compress();
char CompressedData[MAX_COMPRESSED_PACKET_LEN]; const auto Compressed = CompressedData.GetView();
uLongf CompressedSize = compressBound(static_cast<uLongf>(a_Packet.size())); const UInt32 DataSize = static_cast<UInt32>(Uncompressed.size());
if (CompressedSize >= MAX_COMPRESSED_PACKET_LEN) const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + Compressed.size());
{
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 UInt32 DataSize = static_cast<UInt32>(UncompressedSize);
const auto PacketSize = static_cast<UInt32>(cByteBuffer::GetVarIntSize(DataSize) + CompressedSize);
cByteBuffer LengthHeaderBuffer( cByteBuffer LengthHeaderBuffer(
cByteBuffer::GetVarIntSize(PacketSize) + 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(PacketSize);
LengthHeaderBuffer.WriteVarInt32(DataSize); LengthHeaderBuffer.WriteVarInt32(DataSize);
AString LengthData; ContiguousByteBuffer LengthData;
LengthHeaderBuffer.ReadAll(LengthData); LengthHeaderBuffer.ReadAll(LengthData);
a_CompressedData.reserve(LengthData.size() + CompressedSize); a_CompressedData.reserve(LengthData.size() + Compressed.size());
a_CompressedData.assign(LengthData.data(), LengthData.size()); a_CompressedData = LengthData;
a_CompressedData.append(CompressedData, CompressedSize); a_CompressedData += Compressed;
return true;
} }
@ -1947,7 +1927,7 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat
{ {
if (a_Buffer.GetReadableSpace() > 0) if (a_Buffer.GetReadableSpace() > 0)
{ {
AString AllData; ContiguousByteBuffer AllData;
size_t OldReadableSpace = a_Buffer.GetReadableSpace(); size_t OldReadableSpace = a_Buffer.GetReadableSpace();
a_Buffer.ReadAll(AllData); a_Buffer.ReadAll(AllData);
a_Buffer.ResetRead(); a_Buffer.ResetRead();
@ -1992,12 +1972,11 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat
} }
// Check packet for compression: // Check packet for compression:
UInt32 UncompressedSize = 0;
AString UncompressedData;
if (m_State == 3) if (m_State == 3)
{ {
UInt32 NumBytesRead = static_cast<UInt32>(a_Buffer.GetReadableSpace()); UInt32 NumBytesRead = static_cast<UInt32>(a_Buffer.GetReadableSpace());
UInt32 UncompressedSize;
if (!a_Buffer.ReadVarInt(UncompressedSize)) if (!a_Buffer.ReadVarInt(UncompressedSize))
{ {
m_Client->Kick("Compression packet incomplete"); 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) if (UncompressedSize > 0)
{ {
// Decompress the data: // Decompress the data:
AString CompressedData; m_Extractor.ReadFrom(a_Buffer, PacketLen);
VERIFY(a_Buffer.ReadString(CompressedData, PacketLen)); a_Buffer.CommitRead();
if (InflateString(CompressedData.data(), PacketLen, UncompressedData) != Z_OK)
{ const auto UncompressedData = m_Extractor.Extract(UncompressedSize);
m_Client->Kick("Compression failure"); const auto Uncompressed = UncompressedData.GetView();
return; cByteBuffer bb(Uncompressed.size());
}
PacketLen = static_cast<UInt32>(UncompressedData.size()); // Compression was used, move the uncompressed data:
if (PacketLen != UncompressedSize) VERIFY(bb.Write(Uncompressed.data(), Uncompressed.size()));
{
m_Client->Kick("Wrong uncompressed packet size given"); HandlePacket(bb);
return; continue;
}
} }
} }
// Move the packet payload to a separate cByteBuffer, bb: // Move the packet payload to a separate cByteBuffer, bb:
cByteBuffer bb(PacketLen + 1); cByteBuffer bb(PacketLen);
if (UncompressedSize == 0)
{ // No compression was used, move directly:
// No compression was used, move directly VERIFY(a_Buffer.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen)));
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(); a_Buffer.CommitRead();
UInt32 PacketType; HandlePacket(bb);
if (!bb.ReadVarInt(PacketType))
{
// Not enough data
break;
}
// Write one NUL extra, so that we can detect over-reads
bb.Write("\0", 1);
// 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()
);
}
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");
}
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
);
// 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();
}
ASSERT(!"Read wrong number of bytes!");
m_Client->PacketError(PacketType);
}
} // for (ever) } // for (ever)
// Log any leftover bytes into the logfile: // Log any leftover bytes into the logfile:
if (g_ShouldLogCommIn && (a_Buffer.GetReadableSpace() > 0) && m_CommLogFile.IsOpen()) if (g_ShouldLogCommIn && (a_Buffer.GetReadableSpace() > 0) && m_CommLogFile.IsOpen())
{ {
AString AllData; ContiguousByteBuffer AllData;
size_t OldReadableSpace = a_Buffer.GetReadableSpace(); size_t OldReadableSpace = a_Buffer.GetReadableSpace();
a_Buffer.ReadAll(AllData); a_Buffer.ReadAll(AllData);
a_Buffer.ResetRead(); a_Buffer.ResetRead();
@ -2367,8 +2268,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu
{ {
return; return;
} }
AString EncKey; ContiguousByteBuffer EncKey;
if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength)) if (!a_ByteBuffer.ReadSome(EncKey, EncKeyLength))
{ {
return; return;
} }
@ -2376,8 +2277,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu
{ {
return; return;
} }
AString EncNonce; ContiguousByteBuffer EncNonce;
if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) if (!a_ByteBuffer.ReadSome(EncNonce, EncNonceLength))
{ {
return; return;
} }
@ -2391,7 +2292,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu
// Decrypt EncNonce using privkey // Decrypt EncNonce using privkey
cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
UInt32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; 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) if (res != 4)
{ {
LOGD("Bad nonce length: got %d, exp %d", 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: // Decrypt the symmetric encryption key using privkey:
Byte DecryptedKey[MAX_ENC_LEN]; 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) if (res != 16)
{ {
LOGD("Bad key length"); LOGD("Bad key length");
@ -2416,7 +2317,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu
} }
StartEncryption(DecryptedKey); 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; return;
} }
cServer * Server = cRoot::Get()->GetServer(); m_Client->SetUsername(std::move(Username));
// If auth is required, then send the encryption request: // 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); cPacketizer Pkt(*this, pktEncryptionRequest);
Pkt.WriteString(Server->GetServerID()); Pkt.WriteString(Server->GetServerID());
const AString & PubKeyDer = Server->GetPublicKeyDER(); const auto PubKeyDer = Server->GetPublicKeyDER();
Pkt.WriteVarInt32(static_cast<UInt32>(PubKeyDer.size())); Pkt.WriteVarInt32(static_cast<UInt32>(PubKeyDer.size()));
Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size()); Pkt.WriteBuf(PubKeyDer);
Pkt.WriteVarInt32(4); 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 :) 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; 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); HandleVanillaPluginMessage(a_ByteBuffer, Channel);
// Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692): // 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", 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; return;
} }
// Read the plugin message and relay to clienthandle: // Read the plugin message and relay to clienthandle:
AString Data; ContiguousByteBuffer 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); 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); HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand);
m_Client->SetClientBrand(Brand); m_Client->SetClientBrand(Brand);
// Send back our brand, including the length: // Send back our brand, including the length:
SendPluginMessage("MC|Brand", "\x08""Cuberite"); m_Client->SendPluginMessage("MC|Brand", "\x08""Cuberite");
return; return;
} }
else if (a_Channel == "MC|Beacon") 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()); LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
// Read the payload and send it through to the clienthandle: // Read the payload and send it through to the clienthandle:
AString Message; ContiguousByteBuffer Message;
VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1)); VERIFY(a_ByteBuffer.ReadSome(Message, a_ByteBuffer.GetReadableSpace() - 1));
m_Client->HandlePluginMessage(a_Channel, Message); 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) if (m_IsEncrypted)
{ {
Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) std::byte Encrypted[8 KiB]; // Larger buffer, we may be sending lots of data (chunks)
while (a_Size > 0)
while (a_Data.size() > 0)
{ {
size_t NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; const auto NumBytes = (a_Data.size() > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Data.size();
m_Encryptor.ProcessData(Encrypted, reinterpret_cast<const Byte *>(a_Data), NumBytes); m_Encryptor.ProcessData(Encrypted, a_Data.data(), NumBytes);
m_Client->SendData(reinterpret_cast<const char *>(Encrypted), NumBytes); m_Client->SendData({ Encrypted, NumBytes });
a_Size -= NumBytes;
a_Data += NumBytes; a_Data = a_Data.substr(NumBytes);
} }
} }
else 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(); a_Item.Empty();
} }
AString Metadata; ContiguousByteBuffer Metadata;
if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0)))
{ {
// No metadata // No metadata
return true; 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: // Parse into NBT:
cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); cParsedNBT NBT(a_Metadata);
if (!NBT.IsValid()) if (!NBT.IsValid())
{ {
AString HexDump; 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) void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt)
{ {
UInt32 PacketLen = static_cast<UInt32>(m_OutPacketBuffer.GetUsedSpace()); ASSERT(m_OutPacketBuffer.GetReadableSpace() == m_OutPacketBuffer.GetUsedSpace());
AString PacketData, CompressedPacket;
m_OutPacketBuffer.ReadAll(PacketData); m_Compressor.ReadFrom(m_OutPacketBuffer);
m_OutPacketBuffer.CommitRead(); m_OutPacketBuffer.CommitRead();
const auto PacketData = m_Compressor.GetView();
if (m_State == 3) if (m_State == 3)
{ {
ContiguousByteBuffer CompressedPacket;
// Compress the packet payload: // Compress the packet payload:
if (!cProtocol_1_8_0::CompressPacket(PacketData, CompressedPacket)) cProtocol_1_8_0::CompressPacket(m_Compressor, CompressedPacket);
{
return;
}
// Send the packet's payload compressed: // Send the packet's payload compressed:
SendData(CompressedPacket.data(), CompressedPacket.size()); SendData(CompressedPacket);
} }
else else
{ {
// Compression doesn't apply to this state, send raw data: // Compression doesn't apply to this state, send raw data:
m_OutPacketLenBuffer.WriteVarInt32(PacketLen); m_OutPacketLenBuffer.WriteVarInt32(static_cast<UInt32>(PacketData.size()));
AString LengthData; ContiguousByteBuffer LengthData;
m_OutPacketLenBuffer.ReadAll(LengthData); m_OutPacketLenBuffer.ReadAll(LengthData);
SendData(LengthData.data(), LengthData.size()); m_OutPacketLenBuffer.CommitRead();
SendData(LengthData);
// Send the packet's payload directly: // Send the packet's payload directly:
m_OutPacketLenBuffer.CommitRead(); SendData(PacketData);
SendData(PacketData.data(), PacketData.size());
} }
// Log the comm into logfile: // 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); 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", 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()), 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: // 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(); Writer.Finish();
AString Result = Writer.GetResult(); const auto Result = Writer.GetResult();
if (Result.size() == 0) if (Result.empty())
{ {
a_Pkt.WriteBEInt8(0); a_Pkt.WriteBEInt8(0);
return; 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(); 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) void cProtocol_1_8_0::SendEntityTeleport(const cEntity & a_Entity)
{ {
cPacketizer Pkt(*this, pktTeleportEntity); cPacketizer Pkt(*this, pktTeleportEntity);

View File

@ -20,6 +20,9 @@ Declares the 1.8 protocol classes:
#include "../mbedTLS++/AesCfb128Decryptor.h" #include "../mbedTLS++/AesCfb128Decryptor.h"
#include "../mbedTLS++/AesCfb128Encryptor.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 AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) 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 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 SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) 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 SendPlayerMoveLook (void) override;
virtual void SendPlayerPosition (void) override; virtual void SendPlayerPosition (void) override;
virtual void SendPlayerSpawn (const cPlayer & a_Player) 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 SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendResetTitle (void) override; virtual void SendResetTitle (void) override;
virtual void SendResourcePack (const AString & a_ResourcePackUrl) override; virtual void SendResourcePack (const AString & a_ResourcePackUrl) override;
@ -125,9 +128,8 @@ public:
virtual AString GetAuthServerID(void) override { return m_AuthServerID; } virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
/** Compress the packet. a_Packet must be without packet length. /** 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. a_Compressed will be set to the compressed packet includes packet length and data length. */
If compression fails, the function returns false. */ static void CompressPacket(CircularBufferCompressor & a_Packet, ContiguousByteBuffer & a_Compressed);
static bool CompressPacket(const AString & a_Packet, AString & 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. */ /** 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); static int GetParticleID(const AString & a_ParticleName);
@ -194,7 +196,7 @@ protected:
virtual void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel); virtual void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel);
/** Sends the data to the client, encrypting them if needed. */ /** 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. */ /** Sends the packet to the client. Called by the cPacketizer's destructor. */
virtual void SendPacket(cPacketizer & a_Packet) override; 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); 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. */ /** 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); virtual void StartEncryption(const Byte * a_Key);
@ -242,9 +244,15 @@ private:
cAesCfb128Decryptor m_Decryptor; cAesCfb128Decryptor m_Decryptor;
cAesCfb128Encryptor m_Encryptor; cAesCfb128Encryptor m_Encryptor;
CircularBufferCompressor m_Compressor;
CircularBufferExtractor m_Extractor;
/** The logfile where the comm is logged, when g_ShouldLogComm is true */ /** The logfile where the comm is logged, when g_ShouldLogComm is true */
cFile m_CommLogFile; cFile m_CommLogFile;
/** Handle a complete packet stored in the given buffer. */
void HandlePacket(cByteBuffer & a_Buffer);
/** Sends an entity teleport packet. /** Sends an entity teleport packet.
Mitigates a 1.8 bug where the position in the entity spawn packet is ignored, 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. */ and so entities don't show up until a teleport is sent. */

View File

@ -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: // Parse into NBT:
cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); cParsedNBT NBT(a_Metadata);
if (!NBT.IsValid()) if (!NBT.IsValid())
{ {
AString HexDump; AString HexDump;
@ -1438,13 +1438,13 @@ void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
Writer.Finish(); Writer.Finish();
AString Result = Writer.GetResult(); const auto Result = Writer.GetResult();
if (Result.size() == 0) if (Result.empty())
{ {
a_Pkt.WriteBEInt8(0); a_Pkt.WriteBEInt8(0);
return; 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(); 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.AddString("Text4", JsonUtils::WriteFastString(Line4));
Writer.Finish(); Writer.Finish();
Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); Pkt.WriteBuf(Writer.GetResult());
} }

View File

@ -98,7 +98,7 @@ protected:
virtual void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer) override; virtual void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer) override;
/** Parses item metadata as read by ReadItem(), into the item enchantments. */ /** 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. /** 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. */ If the received value doesn't match any of the know value, raise an assertion fail or return hMain. */

View File

@ -24,11 +24,6 @@
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
extern "C"
{
#include "zlib/zlib.h"
}

View File

@ -133,7 +133,7 @@ public:
const AString & GetFaviconData(void) const { return m_FaviconData; } const AString & GetFaviconData(void) const { return m_FaviconData; }
cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } 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. */ /** Returns true if authentication has been turned on in server settings. */
bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export
@ -214,7 +214,7 @@ private:
cRsaPrivateKey m_PrivateKey; cRsaPrivateKey m_PrivateKey;
/** Public key for m_PrivateKey, ASN1-DER-encoded */ /** Public key for m_PrivateKey, ASN1-DER-encoded */
AString m_PublicKeyDER; ContiguousByteBuffer m_PublicKeyDER;
cRCONServer m_RCONServer; cRCONServer m_RCONServer;

View File

@ -1,242 +1,251 @@
// StringCompression.cpp // 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 "Globals.h"
#include "ByteBuffer.h"
#include "StringCompression.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)); const auto View = GetView();
return { reinterpret_cast<const char *>(View.data()), View.size() };
// 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)
{
return errorcode;
}
a_Compressed.resize(CompressedSize);
return Z_OK;
} }
int UncompressString(const char * a_Data, size_t a_Length, AString & a_Uncompressed, size_t a_UncompressedSize) ContiguousByteBufferView Compression::Result::GetView() const
{ {
// HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer! // Get a generic std::byte * to what the variant is currently storing:
// It saves us one allocation and one memcpy of the entire compressed data return
// 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)
{ {
return errorcode; std::visit([](const auto & Buffer) -> const std::byte *
}
a_Uncompressed.resize(UncompressedSize);
return Z_OK;
}
int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Compressed)
{
// Compress a_Data into a_Compressed using GZIP; return Z_XXX error constants same as zlib's compress2()
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)
{
LOG("%s: compression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
return res;
}
for (;;)
{
res = deflate(&strm, Z_FINISH);
switch (res)
{ {
case Z_OK: using Variant = std::decay_t<decltype(Buffer)>;
{
// 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;
}
case Z_STREAM_END: if constexpr (std::is_same_v<Variant, Compression::Result::Static>)
{ {
// Finished compressing. Consume the rest of the buffer and return return Buffer.data();
a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
deflateEnd(&strm);
return Z_OK;
} }
else
default:
{ {
// An error has occurred, log it and return the error value return Buffer.get();
LOG("%s: compression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
deflateEnd(&strm);
return res;
} }
} // switch (res) }, Storage), Size
} // while (true) };
} }
extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed) Compression::Compressor::Compressor(int CompressionFactor)
{ {
// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib m_Handle = libdeflate_alloc_compressor(CompressionFactor);
a_Uncompressed.reserve(a_Length); if (m_Handle == nullptr)
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)
{ {
LOG("%s: uncompression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); throw std::bad_alloc();
return res; }
}
Compression::Compressor::~Compressor()
{
libdeflate_free_compressor(m_Handle);
}
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:
{
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 = inflate(&strm, Z_NO_FLUSH); auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity);
switch (res) const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Dynamic.get(), DynamicCapacity);
if (BytesWrittenOut != 0)
{ {
case Z_OK: return { std::move(Dynamic), BytesWrittenOut };
{ }
// 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: DynamicCapacity *= 2;
{ }
// 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)
} }
extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed) Compression::Result Compression::Compressor::CompressGZip(const ContiguousByteBufferView Input)
{ {
a_Uncompressed.reserve(a_Length); return Compress<&libdeflate_gzip_compress>(Input.data(), Input.size());
}
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)
Compression::Result Compression::Compressor::CompressZLib(const ContiguousByteBufferView Input)
{
return Compress<&libdeflate_zlib_compress>(Input.data(), Input.size());
}
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: inflation initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); throw std::bad_alloc();
return res; }
}
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);
}
Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input, size_t UncompressedSize)
{
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:
{
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); size_t BytesWrittenOut;
switch (res) 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 DynamicCapacity *= 2;
a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); continue;
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;
} }
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.");
}

View File

@ -1,27 +1,85 @@
// StringCompression.h // 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() */ class cByteBuffer;
extern int CompressString(const char * a_Data, size_t a_Length, AString & a_Compressed, int a_Factor);
/** Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress() */ struct libdeflate_compressor;
extern int UncompressString(const char * a_Data, size_t a_Length, AString & a_Uncompressed, size_t a_UncompressedSize); 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;
};
}

View File

@ -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>(
return static_cast<short>((Bytes[0] << 8) | Bytes[1]); (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
return (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | Bytes[3]; (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[0] = std::byte(a_Value >> 24);
a_Mem[1] = static_cast<char>((a_Value >> 16) & 0xff); a_Mem[1] = std::byte((a_Value >> 16) & 0xff);
a_Mem[2] = static_cast<char>((a_Value >> 8) & 0xff); a_Mem[2] = std::byte((a_Value >> 8) & 0xff);
a_Mem[3] = static_cast<char>(a_Value & 0xff); a_Mem[3] = std::byte(a_Value & 0xff);
} }

View File

@ -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 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 */ /** 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 */ /** Reads two bytes from the specified memory location and interprets them as BigEndian unsigned short */
extern unsigned short GetBEUShort(const char * a_Mem); extern unsigned short GetBEUShort(const char * a_Mem);
/** Reads four bytes from the specified memory location and interprets them as BigEndian int */ /** 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 */ /** 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. /** 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. a_Output is first cleared and then each separate string is pushed back into a_Output.

View File

@ -137,7 +137,7 @@ std::error_code make_error_code(eNBTParseError a_Err) noexcept
#define NEEDBYTES(N, ERR) \ #define NEEDBYTES(N, ERR) \
do { \ do { \
if (m_Length - m_Pos < static_cast<size_t>(N)) \ if (m_Data.size() - m_Pos < static_cast<size_t>(N)) \
{ \ { \
return ERR; \ 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_Data(a_Data),
m_Length(a_Length),
m_Pos(0) m_Pos(0)
{ {
m_Error = Parse(); m_Error = Parse();
@ -161,12 +160,12 @@ cParsedNBT::cParsedNBT(const char * a_Data, size_t a_Length) :
eNBTParseError cParsedNBT::Parse(void) eNBTParseError cParsedNBT::Parse(void)
{ {
if (m_Length < 3) if (m_Data.size() < 3)
{ {
// Data too short // Data too short
return eNBTParseError::npNeedBytes; 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 // The top-level tag must be a Compound
return eNBTParseError::npNoTopLevelCompound; return eNBTParseError::npNoTopLevelCompound;
@ -190,7 +189,7 @@ eNBTParseError cParsedNBT::ReadString(size_t & a_StringStart, size_t & a_StringL
{ {
NEEDBYTES(2, eNBTParseError::npStringMissingLength); NEEDBYTES(2, eNBTParseError::npStringMissingLength);
a_StringStart = m_Pos + 2; 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); NEEDBYTES(2 + a_StringLen, eNBTParseError::npStringInvalidLength);
m_Pos += 2 + a_StringLen; m_Pos += 2 + a_StringLen;
return eNBTParseError::npSuccess; return eNBTParseError::npSuccess;
@ -210,8 +209,8 @@ eNBTParseError cParsedNBT::ReadCompound(void)
for (;;) for (;;)
{ {
NEEDBYTES(1, eNBTParseError::npCompoundImbalancedTag); NEEDBYTES(1, eNBTParseError::npCompoundImbalancedTag);
const char TagTypeNum = m_Data[m_Pos]; const auto TagTypeNum = m_Data[m_Pos];
if ((TagTypeNum < TAG_Min) || (TagTypeNum > TAG_Max)) if ((TagTypeNum < std::byte(TAG_Min)) || (TagTypeNum > std::byte(TAG_Max)))
{ {
return eNBTParseError::npUnknownTag; return eNBTParseError::npUnknownTag;
} }
@ -248,10 +247,10 @@ eNBTParseError cParsedNBT::ReadList(eTagType a_ChildrenType)
// Read the count: // Read the count:
NEEDBYTES(4, eNBTParseError::npListMissingLength); NEEDBYTES(4, eNBTParseError::npListMissingLength);
int Count = GetBEInt(m_Data + m_Pos); int Count = GetBEInt(m_Data.data() + m_Pos);
m_Pos += 4; m_Pos += 4;
auto MinChildSize = GetMinTagSize(a_ChildrenType); 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; return eNBTParseError::npListInvalidLength;
} }
@ -312,7 +311,7 @@ eNBTParseError cParsedNBT::ReadTag(void)
case TAG_ByteArray: case TAG_ByteArray:
{ {
NEEDBYTES(4, eNBTParseError::npArrayMissingLength); NEEDBYTES(4, eNBTParseError::npArrayMissingLength);
int len = GetBEInt(m_Data + m_Pos); int len = GetBEInt(m_Data.data() + m_Pos);
m_Pos += 4; m_Pos += 4;
if (len < 0) if (len < 0)
{ {
@ -344,7 +343,7 @@ eNBTParseError cParsedNBT::ReadTag(void)
case TAG_IntArray: case TAG_IntArray:
{ {
NEEDBYTES(4, eNBTParseError::npArrayMissingLength); NEEDBYTES(4, eNBTParseError::npArrayMissingLength);
int len = GetBEInt(m_Data + m_Pos); int len = GetBEInt(m_Data.data() + m_Pos);
m_Pos += 4; m_Pos += 4;
if (len < 0) if (len < 0)
{ {
@ -392,7 +391,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen
{ {
if ( if (
(m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) && (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; return Child;
@ -470,9 +469,9 @@ cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) :
m_CurrentStack(0) m_CurrentStack(0)
{ {
m_Stack[0].m_Type = TAG_Compound; m_Stack[0].m_Type = TAG_Compound;
m_Result.reserve(100 * 1024); m_Result.reserve(100 KiB);
m_Result.push_back(TAG_Compound); m_Result.push_back(std::byte(TAG_Compound));
WriteString(a_RootTagName.data(), static_cast<UInt16>(a_RootTagName.size())); WriteString(a_RootTagName);
} }
@ -502,7 +501,7 @@ void cFastNBTWriter::EndCompound(void)
ASSERT(m_CurrentStack > 0); ASSERT(m_CurrentStack > 0);
ASSERT(IsStackTopCompound()); ASSERT(IsStackTopCompound());
m_Result.push_back(TAG_End); m_Result.push_back(std::byte(TAG_End));
--m_CurrentStack; --m_CurrentStack;
} }
@ -520,8 +519,8 @@ void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
TagCommon(a_Name, TAG_List); TagCommon(a_Name, TAG_List);
m_Result.push_back(static_cast<char>(a_ChildrenType)); m_Result.push_back(std::byte(a_ChildrenType));
m_Result.append(4, static_cast<char>(0)); m_Result.append(4, std::byte(0));
++m_CurrentStack; ++m_CurrentStack;
m_Stack[m_CurrentStack].m_Type = TAG_List; 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); ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
// Update the list count: // 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; --m_CurrentStack;
} }
@ -552,7 +551,7 @@ void cFastNBTWriter::EndList(void)
void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
{ {
TagCommon(a_Name, TAG_Byte); 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); TagCommon(a_Name, TAG_Short);
UInt16 Value = htons(static_cast<UInt16>(a_Value)); 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); TagCommon(a_Name, TAG_Int);
UInt32 Value = htonl(static_cast<UInt32>(a_Value)); 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); TagCommon(a_Name, TAG_Long);
UInt64 Value = HostToNetwork8(&a_Value); 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); TagCommon(a_Name, TAG_Float);
UInt32 Value = HostToNetwork4(&a_Value); 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); TagCommon(a_Name, TAG_Double);
UInt64 Value = HostToNetwork8(&a_Value); 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); TagCommon(a_Name, TAG_String);
const UInt16 Length = htons(static_cast<UInt16>(a_Value.size())); const UInt16 Length = htons(static_cast<UInt16>(a_Value.size()));
m_Result.append(reinterpret_cast<const char *>(&Length), sizeof(Length)); m_Result.append(reinterpret_cast<const std::byte *>(&Length), sizeof(Length));
m_Result.append(a_Value); 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); TagCommon(a_Name, TAG_ByteArray);
UInt32 len = htonl(static_cast<UInt32>(a_NumElements)); UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
m_Result.append(reinterpret_cast<const char *>(&len), 4); m_Result.append(reinterpret_cast<const std::byte *>(&len), 4);
m_Result.append(a_Value, a_NumElements); 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.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++) for (size_t i = 0; i < a_NumElements; i++)
{ {
UInt32 Element = htonl(static_cast<UInt32>(a_Value[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) void cFastNBTWriter::Finish(void)
{ {
ASSERT(m_CurrentStack == 0); 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); // TODO check size <= short max
m_Result.append(reinterpret_cast<const char *>(&Len), 2); UInt16 Len = htons(static_cast<unsigned short>(a_Data.size()));
m_Result.append(a_Data, a_Length); 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());
} }

View File

@ -152,7 +152,7 @@ Each primitive tag also stores the length of the contained data, in bytes.
class cParsedNBT class cParsedNBT
{ {
public: public:
cParsedNBT(const char * a_Data, size_t a_Length); cParsedNBT(ContiguousByteBufferView a_Data);
bool IsValid(void) const { return (m_Error == eNBTParseError::npSuccess); } bool IsValid(void) const { return (m_Error == eNBTParseError::npSuccess); }
@ -179,7 +179,7 @@ public:
/** Returns the length of the tag's data, in bytes. /** Returns the length of the tag's data, in bytes.
Not valid for Compound or List tags! */ 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_List);
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound); 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. /** Returns the data stored in this tag.
Not valid for Compound or List tags! */ 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_List);
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound); 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. */ /** 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 inline Int16 GetShort(int a_Tag) const
{ {
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Short); 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. */ /** Returns the value stored in an Int tag. Not valid for any other tag type. */
inline Int32 GetInt(int a_Tag) const inline Int32 GetInt(int a_Tag) const
{ {
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Int); 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. */ /** Returns the value stored in a Long tag. Not valid for any other tag type. */
inline Int64 GetLong(int a_Tag) const inline Int64 GetLong(int a_Tag) const
{ {
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Long); 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. */ /** 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(Check1);
UNUSED_VAR(Check2); 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; float f;
memcpy(&f, &i, sizeof(f)); memcpy(&f, &i, sizeof(f));
return f; return f;
@ -273,29 +273,33 @@ public:
UNUSED_VAR(Check2); UNUSED_VAR(Check2);
ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Double); 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. */ /** Returns the value stored in a String tag. Not valid for any other tag type. */
inline AString GetString(int a_Tag) const 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); ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_String);
AString res; return { reinterpret_cast<const char *>(GetData(a_Tag)), GetDataLength(a_Tag) };
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;
} }
/** Returns the tag's name. For tags that are not named, returns an empty string. */ /** Returns the tag's name. For tags that are not named, returns an empty string. */
inline AString GetName(int a_Tag) const inline AString GetName(int a_Tag) const
{ {
AString res; 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; return res;
} }
protected: protected:
const char * m_Data;
size_t m_Length; ContiguousByteBufferView m_Data;
std::vector<cFastNBTTag> m_Tags; std::vector<cFastNBTTag> m_Tags;
eNBTParseError m_Error; // npSuccess if parsing succeeded eNBTParseError m_Error; // npSuccess if parsing succeeded
@ -343,7 +347,7 @@ public:
AddByteArray(a_Name, a_Value.data(), a_Value.size()); 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); void Finish(void);
@ -363,11 +367,11 @@ protected:
sParent m_Stack[MAX_STACK]; sParent m_Stack[MAX_STACK];
int m_CurrentStack; int m_CurrentStack;
AString m_Result; ContiguousByteBuffer m_Result;
bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); } 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) inline void TagCommon(const AString & a_Name, eTagType a_Type)
{ {
@ -377,8 +381,8 @@ protected:
if (IsStackTopCompound()) if (IsStackTopCompound())
{ {
// Compound: add the type and name: // Compound: add the type and name:
m_Result.push_back(static_cast<char>(a_Type)); m_Result.push_back(std::byte(a_Type));
WriteString(a_Name.c_str(), static_cast<UInt16>(a_Name.length())); WriteString(a_Name);
} }
else else
{ {
@ -387,7 +391,3 @@ protected:
} }
} }
} ; } ;

View File

@ -105,7 +105,7 @@ void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNB
continue; continue;
} }
const char * ColourData = (a_NBT.GetData(explosiontag)); const auto * ColourData = (a_NBT.GetData(explosiontag));
for (size_t i = 0; i < DataLength; i += 4) for (size_t i = 0; i < DataLength; i += 4)
{ {
a_FireworkItem.m_Colours.push_back(GetBEInt(ColourData + i)); a_FireworkItem.m_Colours.push_back(GetBEInt(ColourData + i));
@ -121,7 +121,7 @@ void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNB
continue; continue;
} }
const char * FadeColourData = (a_NBT.GetData(explosiontag)); const auto * FadeColourData = (a_NBT.GetData(explosiontag));
for (size_t i = 0; i < DataLength; i += 4) for (size_t i = 0; i < DataLength; i += 4)
{ {
a_FireworkItem.m_FadeColours.push_back(GetBEInt(FadeColourData + i)); a_FireworkItem.m_FadeColours.push_back(GetBEInt(FadeColourData + i));

View File

@ -4,8 +4,7 @@
#include "Globals.h" #include "Globals.h"
#include "MapSerializer.h" #include "MapSerializer.h"
#include "../StringCompression.h" #include "OSSupport/GZipFile.h"
#include "zlib/zlib.h"
#include "FastNBT.h" #include "FastNBT.h"
#include "../Map.h" #include "../Map.h"
@ -32,22 +31,9 @@ cMapSerializer::cMapSerializer(const AString & a_WorldName, cMap * a_Map):
bool cMapSerializer::Load(void) bool cMapSerializer::Load(void)
{ {
AString Data = cFile::ReadWholeFile(m_Path); const auto Data = GZipFile::ReadRestOfFile(m_Path);
if (Data.empty()) const cParsedNBT NBT(Data.GetView());
{
return false;
}
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()) if (!NBT.IsValid())
{ {
// NBT Parsing failed // NBT Parsing failed
@ -64,32 +50,15 @@ bool cMapSerializer::Load(void)
bool cMapSerializer::Save(void) bool cMapSerializer::Save(void)
{ {
cFastNBTWriter Writer; cFastNBTWriter Writer;
SaveMapToNBT(Writer); SaveMapToNBT(Writer);
Writer.Finish(); Writer.Finish();
#ifdef _DEBUG #ifdef _DEBUG
cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); cParsedNBT TestParse(Writer.GetResult());
ASSERT(TestParse.IsValid()); ASSERT(TestParse.IsValid());
#endif // _DEBUG #endif // _DEBUG
cFile File; GZipFile::Write(m_Path, Writer.GetResult());
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();
return true; return true;
} }
@ -225,7 +194,7 @@ bool cIDCountSerializer::Load(void)
// NOTE: idcounts.dat is not compressed (raw format) // NOTE: idcounts.dat is not compressed (raw format)
// Parse the NBT data: // Parse the NBT data:
cParsedNBT NBT(Data.data(), Data.size()); cParsedNBT NBT({ reinterpret_cast<const std::byte *>(Data.data()), Data.size() });
if (!NBT.IsValid()) if (!NBT.IsValid())
{ {
// NBT Parsing failed // NBT Parsing failed
@ -261,7 +230,7 @@ bool cIDCountSerializer::Save(void)
Writer.Finish(); Writer.Finish();
#ifdef _DEBUG #ifdef _DEBUG
cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); cParsedNBT TestParse(Writer.GetResult());
ASSERT(TestParse.IsValid()); ASSERT(TestParse.IsValid());
#endif // _DEBUG #endif // _DEBUG
@ -278,11 +247,3 @@ bool cIDCountSerializer::Save(void)
return true; return true;
} }

View File

@ -1177,17 +1177,14 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// NBTChunkSerializer: // NBTChunkSerializer:
bool NBTChunkSerializer::serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter) void NBTChunkSerializer::Serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter)
{ {
SerializerCollector serializer(aWriter); SerializerCollector serializer(aWriter);
aWriter.BeginCompound("Level"); aWriter.BeginCompound("Level");
aWriter.AddInt("xPos", aCoords.m_ChunkX); aWriter.AddInt("xPos", aCoords.m_ChunkX);
aWriter.AddInt("zPos", aCoords.m_ChunkZ); aWriter.AddInt("zPos", aCoords.m_ChunkZ);
if (!aWorld.GetChunkData(aCoords, serializer)) [[maybe_unused]] const bool Result = aWorld.GetChunkData(aCoords, serializer); // Chunk must be present in order to save
{ ASSERT(Result);
aWriter.EndCompound(); // "Level"
return false;
}
serializer.Finish(); // Close NBT tags serializer.Finish(); // Close NBT tags
// Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): // 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.AddByte("TerrainPopulated", 1);
aWriter.EndCompound(); // "Level" aWriter.EndCompound(); // "Level"
return true;
} }

View File

@ -21,7 +21,6 @@ class NBTChunkSerializer
{ {
public: public:
/** Serializes the chunk into the specified writer. /** Serializes the chunk into the specified writer. The chunk must be present. */
Returns true on success, false on failure (chunk not present etc.) */ static void Serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter);
static bool serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter);
}; };

View File

@ -7,7 +7,6 @@
#include "FastNBT.h" #include "FastNBT.h"
#include "SchematicFileSerializer.h" #include "SchematicFileSerializer.h"
#include "../StringCompression.h"
#include "../OSSupport/GZipFile.h" #include "../OSSupport/GZipFile.h"
@ -17,118 +16,59 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cSchematicFileSerializer: // 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: const auto Data = GZipFile::ReadRestOfFile(a_FileName);
AString Contents; const cParsedNBT NBT(Data.GetView());
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();
// Parse the NBT:
cParsedNBT NBT(Contents.data(), Contents.size());
if (!NBT.IsValid()) if (!NBT.IsValid())
{ {
LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str()); throw std::runtime_error(fmt::format("Cannot parse the NBT in the schematic file \"{}\".", a_FileName));
return false;
} }
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: const auto Extracted = Compression::Extractor().ExtractGZip(a_SchematicData);
AString UngzippedData; const cParsedNBT NBT(Extracted.GetView());
if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK)
{
LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__);
return false;
}
// Parse the NBT:
cParsedNBT NBT(UngzippedData.data(), UngzippedData.size());
if (!NBT.IsValid()) if (!NBT.IsValid())
{ {
LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__); throw std::runtime_error("Cannot parse the NBT in the schematic data.");
return false;
} }
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: GZipFile::Write(a_FileName, SaveToSchematicNBT(a_BlockArea));
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;
} }
bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out) Compression::Result cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea)
{ {
// Serialize into NBT data: return Compression::Compressor().CompressGZip(SaveToSchematicNBT(a_BlockArea));
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;
} }
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"); int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String)) 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); AString Materials = a_NBT.GetString(TMaterials);
if (Materials.compare("Alpha") != 0) if (Materials.compare("Alpha") != 0)
{ {
LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str()); throw std::runtime_error(fmt::format("Materials tag is present and \"{}\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials));
return false;
} }
} }
int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width"); 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) (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, TSizeY, TSizeZ,
(TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1, (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1,
(TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1, (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1,
(TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1 (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1
); ));
return false;
} }
int SizeX = a_NBT.GetShort(TSizeX); int SizeX = a_NBT.GetShort(TSizeX);
@ -164,16 +103,14 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP
int SizeZ = a_NBT.GetShort(TSizeZ); int SizeZ = a_NBT.GetShort(TSizeZ);
if ((SizeX < 1) || (SizeX > 65535) || (SizeY < 1) || (SizeY > 65535) || (SizeZ < 1) || (SizeZ > 65535)) 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); throw std::runtime_error(fmt::format("Dimensions are invalid in the schematic file: {}, {}, {}", SizeX, SizeY, SizeZ));
return false;
} }
int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks"); int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data"); int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray)) if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
{ {
LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes); throw std::runtime_error(fmt::format("BlockTypes are invalid in the schematic file: {}", TBlockTypes));
return false;
} }
bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray); 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); 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"); cFastNBTWriter Writer("Schematic");
Writer.AddShort("Width", static_cast<Int16>(a_BlockArea.m_Size.x)); 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.EndList();
Writer.Finish(); Writer.Finish();
return Writer.GetResult(); return ContiguousByteBuffer(Writer.GetResult());
} }

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "../BlockArea.h" #include "../BlockArea.h"
#include "../StringCompression.h"
@ -26,29 +27,23 @@ class cSchematicFileSerializer
{ {
public: public:
/** Loads an area from a .schematic file. Returns true if successful. */ /** Loads an area from a .schematic file. */
static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); 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. */ /** Loads an area from a string containing the .schematic file data. */
static bool LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData); static void LoadFromSchematicString(cBlockArea & a_BlockArea, ContiguousByteBufferView a_SchematicData);
/** Saves the area into a .schematic file. Returns true if successful. */ /** Saves the area into a .schematic file. */
static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName); static void SaveToSchematicFile(const cBlockArea & a_BlockArea, const std::string & a_FileName);
/** Saves the area into a string containing the .schematic file data. /** 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 Compression::Result SaveToSchematicString(const cBlockArea & a_BlockArea);
static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out);
private: 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. /** Loads the area from a schematic file uncompressed and parsed into a NBT tree. */
Returns an empty string if failed. */ static void LoadFromSchematicNBT(cBlockArea & a_BlockArea, const cParsedNBT & a_NBT);
static AString SaveToSchematicNBT(const cBlockArea & a_BlockArea);
/** Saves the area into a NBT representation and returns the NBT data as a string. */
static ContiguousByteBuffer SaveToSchematicNBT(const cBlockArea & a_BlockArea);
}; };

View File

@ -4,8 +4,7 @@
#include "Globals.h" #include "Globals.h"
#include "ScoreboardSerializer.h" #include "ScoreboardSerializer.h"
#include "../StringCompression.h" #include "OSSupport/GZipFile.h"
#include "zlib/zlib.h"
#include "FastNBT.h" #include "FastNBT.h"
#include "../Scoreboard.h" #include "../Scoreboard.h"
@ -31,29 +30,24 @@ cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScore
bool cScoreboardSerializer::Load(void) bool cScoreboardSerializer::Load(void)
{ {
AString Data = cFile::ReadWholeFile(m_Path); try
if (Data.empty())
{ {
const auto Data = GZipFile::ReadRestOfFile(m_Path);
const cParsedNBT NBT(Data.GetView());
if (!NBT.IsValid())
{
// NBT Parsing failed
return false;
}
return LoadScoreboardFromNBT(NBT);
}
catch (const std::exception & Oops)
{
LOGWARNING("Failed to load scoreboard from \"%s\": %s", m_Path.c_str(), Oops.what());
return false; return false;
} }
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
return false;
}
return LoadScoreboardFromNBT(NBT);
} }
@ -63,32 +57,15 @@ bool cScoreboardSerializer::Load(void)
bool cScoreboardSerializer::Save(void) bool cScoreboardSerializer::Save(void)
{ {
cFastNBTWriter Writer; cFastNBTWriter Writer;
SaveScoreboardToNBT(Writer); SaveScoreboardToNBT(Writer);
Writer.Finish(); Writer.Finish();
#ifdef _DEBUG #ifdef _DEBUG
cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); cParsedNBT TestParse(Writer.GetResult());
ASSERT(TestParse.IsValid()); ASSERT(TestParse.IsValid());
#endif // _DEBUG #endif // _DEBUG
cFile File; GZipFile::Write(m_Path, Writer.GetResult());
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();
return true; return true;
} }

View File

@ -8,8 +8,8 @@
#include "NBTChunkSerializer.h" #include "NBTChunkSerializer.h"
#include "EnchantmentSerializer.h" #include "EnchantmentSerializer.h"
#include "NamespaceSerializer.h" #include "NamespaceSerializer.h"
#include "zlib/zlib.h"
#include "json/json.h" #include "json/json.h"
#include "OSSupport/GZipFile.h"
#include "../World.h" #include "../World.h"
#include "../Item.h" #include "../Item.h"
#include "../ItemGrid.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) : cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) :
Super(a_World), 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: // Create a level.dat file for mapping tools, if it doesn't already exist:
AString fnam; AString fnam;
@ -117,12 +117,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) :
Writer.EndCompound(); Writer.EndCompound();
Writer.Finish(); Writer.Finish();
gzFile gz = gzopen((fnam).c_str(), "wb"); GZipFile::Write(fnam, Writer.GetResult());
if (gz != nullptr)
{
gzwrite(gz, Writer.GetResult().data(), static_cast<unsigned>(Writer.GetResult().size()));
}
gzclose(gz);
} }
} }
@ -145,7 +140,7 @@ cWSSAnvil::~cWSSAnvil()
bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk)
{ {
AString ChunkData; ContiguousByteBuffer ChunkData;
if (!GetChunkData(a_Chunk, ChunkData)) if (!GetChunkData(a_Chunk, ChunkData))
{ {
// The reason for failure is already printed in GetChunkData() // 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) bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk)
{ {
AString ChunkData; try
if (!SaveChunkToData(a_Chunk, ChunkData))
{ {
LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); if (!SetChunkData(a_Chunk, SaveChunkToData(a_Chunk).GetView()))
return false; {
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; 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: // Construct the filename for offloading:
AString OffloadFileName; AString OffloadFileName;
@ -202,7 +199,7 @@ void cWSSAnvil::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ, const AString & a_Re
// Log the warning to console: // Log the warning to console:
const int RegionX = FAST_FLOOR_DIV(a_ChunkX, 32); const int RegionX = FAST_FLOOR_DIV(a_ChunkX, 32);
const int RegionZ = FAST_FLOOR_DIV(a_ChunkZ, 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() a_ChunkX, a_ChunkZ, m_World->GetName().c_str(), RegionX, RegionZ, a_Reason.c_str(), OffloadFileName.c_str()
); );
LOGWARNING("%s", Info.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); cCSLock Lock(m_CS);
cMCAFile * File = LoadMCAFile(a_Chunk); 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); cCSLock Lock(m_CS);
cMCAFile * File = LoadMCAFile(a_Chunk); 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: try
AString Uncompressed;
int res = InflateString(a_Data.data(), a_Data.size(), Uncompressed);
if (res != Z_OK)
{ {
LOGWARNING("Uncompressing chunk [%d, %d] failed: %d", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, res); const auto Extracted = m_Extractor.ExtractZLib(a_Data);
ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, "InflateString() failed", a_Data); cParsedNBT NBT(Extracted.GetView());
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)
{
ChunkLoadFailed(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Oops.what(), a_Data);
return false; return false;
} }
// Parse the NBT data:
cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
if (!NBT.IsValid())
{
// 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);
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; cFastNBTWriter Writer;
if (!SaveChunkToNBT(a_Chunk, Writer)) NBTChunkSerializer::Serialize(*m_World, a_Chunk, Writer);
{
LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
Writer.Finish(); Writer.Finish();
CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data, m_CompressionFactor); return m_Compressor.CompressZLib(Writer.GetResult());
return true;
} }
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) // The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data)
cChunkDef::BlockTypes BlockTypes; 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) 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)) 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 // The biomes stored don't match in size
return nullptr; 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++) for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
{ {
(*a_BiomeMap)[i] = static_cast<EMCSBiome>(GetBEInt(&BiomeData[i * 4])); (*a_BiomeMap)[i] = static_cast<EMCSBiome>(GetBEInt(&BiomeData[i * 4]));
@ -587,7 +562,7 @@ void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT &
try 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 (...) 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: // All the other blocktypes should have no entities assigned to them. Report an error:
// Get the "id" tag: // Get the "id" tag:
int TagID = a_NBT.FindChildByName(a_Tag, "id"); 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.", FLOGINFO("WorldLoader({0}): Block entity mismatch: block type {1} ({2}), type \"{3}\", at {4}; the entity will be lost.",
m_World->GetName(), m_World->GetName(),
ItemTypeToString(a_BlockType), a_BlockType, TypeName, ItemTypeToString(a_BlockType), a_BlockType, (TagID >= 0) ? a_NBT.GetStringView(TagID) : "unknown",
a_Pos a_Pos
); );
return nullptr; return nullptr;
@ -887,10 +857,16 @@ bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, con
return false; return false;
} }
// Check if the "id" tag is a string:
if (a_NBT.GetType(TagID) != eTagType::TAG_String)
{
return false;
}
// Compare the value: // Compare the value:
for (const auto & et: a_ExpectedTypes) 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; 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.", 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 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_NBT.GetStringView(TagID), a_Pos
a_Pos
); );
return false; return false;
} }
@ -1364,7 +1339,7 @@ OwnedBlockEntity cWSSAnvil::LoadMobSpawnerFromNBT(const cParsedNBT & a_NBT, int
int Type = a_NBT.FindChildByName(a_TagIdx, "EntityId"); int Type = a_NBT.FindChildByName(a_TagIdx, "EntityId");
if ((Type >= 0) && (a_NBT.GetType(Type) == TAG_String)) 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) if (StatInfo.first == NamespaceSerializer::Namespace::Unknown)
{ {
return nullptr; 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 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 static const EntityLoaderMap EntityTypeToFunction
{ {
{ "Boat", &cWSSAnvil::LoadBoatFromNBT }, { "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 // 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()) if (it != EntityTypeToFunction.end())
{ {
(this->*it->second)(a_Entities, a_NBT, a_EntityTagIdx); (this->*it->second)(a_Entities, a_NBT, a_EntityTagIdx);
return; return;
} }
const auto StatInfo = NamespaceSerializer::SplitNamespacedID({ a_IDTag, a_IDTagLength }); const auto StatInfo = NamespaceSerializer::SplitNamespacedID(a_EntityName);
if (StatInfo.first == NamespaceSerializer::Namespace::Unknown) if (StatInfo.first == NamespaceSerializer::Namespace::Unknown)
{ {
return; 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)) if (!OpenFile(true))
{ {
@ -3976,21 +3951,21 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a
UInt32 ChunkSize = 0; UInt32 ChunkSize = 0;
if (m_File.Read(&ChunkSize, 4) != 4) 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; return false;
} }
ChunkSize = ntohl(ChunkSize); ChunkSize = ntohl(ChunkSize);
if (ChunkSize < 1) if (ChunkSize < 1)
{ {
// Chunk size too small // 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; return false;
} }
char CompressionType = 0; char CompressionType = 0;
if (m_File.Read(&CompressionType, 1) != 1) 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; return false;
} }
ChunkSize--; 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)) if (!OpenFile(false))
{ {
@ -4034,7 +4009,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
LocalZ = 32 + LocalZ; LocalZ = 32 + LocalZ;
} }
unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data.size());
// Store the chunk data: // Store the chunk data:
m_File.Seek(static_cast<int>(ChunkSector * 4096)); 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: // See if it fits the current location:
unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]);
unsigned ChunkLen = ChunkLocation & 0xff; 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; return ChunkLocation >> 8;
} }
@ -4126,7 +4101,3 @@ unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const
} // for i - m_Header[] } // for i - m_Header[]
return MaxLocation >> 8; return MaxLocation >> 8;
} }

View File

@ -11,6 +11,7 @@
#include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/BlockEntity.h"
#include "WorldStorage.h" #include "WorldStorage.h"
#include "FastNBT.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); cMCAFile(cWSSAnvil & a_ParentSchema, const AString & a_FileName, int a_RegionX, int a_RegionZ);
bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); bool GetChunkData (const cChunkCoords & a_Chunk, ContiguousByteBuffer & a_Data);
bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); bool SetChunkData (const cChunkCoords & a_Chunk, ContiguousByteBufferView a_Data);
bool EraseChunkData(const cChunkCoords & a_Chunk);
int GetRegionX (void) const {return m_RegionX; } int GetRegionX (void) const {return m_RegionX; }
int GetRegionZ (void) const {return m_RegionZ; } int GetRegionZ (void) const {return m_RegionZ; }
@ -86,8 +86,8 @@ protected:
// Chunk timestamps, following the chunk headers // Chunk timestamps, following the chunk headers
unsigned m_TimeStamps[MCA_MAX_CHUNKS]; 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. */ /** Finds a free location large enough to hold a_Data. Returns the sector number. */
unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); 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) */ /** 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); bool OpenFile(bool a_IsForReading);
@ -97,30 +97,27 @@ protected:
cCriticalSection m_CS; cCriticalSection m_CS;
cMCAFiles m_Files; // a MRU cache of MCA files 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. */ /** 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 */ /** 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 */ /** 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) */ /** 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) */ /** 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). /** Loads the chunk from NBT data (no locking needed).
a_RawChunkData is the raw (compressed) chunk data, used for offloading when chunk loading fails. */ 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); bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT, ContiguousByteBufferView a_RawChunkData);
/** Saves the chunk into NBT data using a_Writer; returns true on success */
bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer);
/** Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, nullptr otherwise */ /** 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); 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 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); 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 LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEnderCrystalFromNBT (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 bool SaveChunk(const cChunkCoords & a_Chunk) override;
virtual const AString GetName(void) const override {return "anvil"; } virtual const AString GetName(void) const override {return "anvil"; }
} ; } ;

View File

@ -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 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));
} }

View File

@ -27,7 +27,7 @@ public:
void Init(const Byte a_Key[16], const Byte a_IV[16]); 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 */ /** 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 */ /** Returns true if the object has been initialized with the Key / IV */
bool IsValid(void) const { return m_IsValid; } bool IsValid(void) const { return m_IsValid; }

View File

@ -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 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));
} }

View File

@ -19,6 +19,7 @@
class cAesCfb128Encryptor class cAesCfb128Encryptor
{ {
public: public:
cAesCfb128Encryptor(void); cAesCfb128Encryptor(void);
~cAesCfb128Encryptor(); ~cAesCfb128Encryptor();
@ -26,7 +27,7 @@ public:
void Init(const Byte a_Key[16], const Byte a_IV[16]); 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 */ /** 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 */ /** Returns true if the object has been initialized with the Key / IV */
bool IsValid(void) const { return m_IsValid; } bool IsValid(void) const { return m_IsValid; }

View File

@ -55,7 +55,7 @@ bool cRsaPrivateKey::Generate(unsigned a_KeySizeBits)
AString cRsaPrivateKey::GetPubKeyDER(void) ContiguousByteBuffer cRsaPrivateKey::GetPubKeyDER(void)
{ {
class cPubKey class cPubKey
{ {
@ -96,21 +96,21 @@ AString cRsaPrivateKey::GetPubKeyDER(void)
int res = mbedtls_pk_write_pubkey_der(PkCtx, buf, sizeof(buf)); int res = mbedtls_pk_write_pubkey_der(PkCtx, buf, sizeof(buf));
if (res < 0) 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", 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!"); ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1; return -1;
@ -126,7 +126,7 @@ int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLeng
size_t DecryptedLength; size_t DecryptedLength;
int res = mbedtls_rsa_pkcs1_decrypt( int res = mbedtls_rsa_pkcs1_decrypt(
&m_Rsa, mbedtls_ctr_drbg_random, m_CtrDrbg.GetInternal(), MBEDTLS_RSA_PRIVATE, &DecryptedLength, &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) if (res != 0)
{ {
@ -134,41 +134,3 @@ int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLeng
} }
return static_cast<int>(DecryptedLength); 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);
}

View File

@ -35,17 +35,12 @@ public:
bool Generate(unsigned a_KeySizeBits = 1024); bool Generate(unsigned a_KeySizeBits = 1024);
/** Returns the public key part encoded in ASN1 DER encoding */ /** Returns the public key part encoded in ASN1 DER encoding */
AString GetPubKeyDER(void); ContiguousByteBuffer GetPubKeyDER(void);
/** Decrypts the data using RSAES-PKCS#1 algorithm. /** Decrypts the data using RSAES-PKCS#1 algorithm.
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
Returns the number of bytes decrypted, or negative number for error. */ 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); int Decrypt(ContiguousByteBufferView a_EncryptedData, 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);
protected: protected:
/** The mbedTLS key context */ /** The mbedTLS key context */

View File

@ -36,7 +36,7 @@ static void TestWrite(void)
buf.WriteVarInt32(5); buf.WriteVarInt32(5);
buf.WriteVarInt32(300); buf.WriteVarInt32(300);
buf.WriteVarInt32(0); buf.WriteVarInt32(0);
AString All; ContiguousByteBuffer All;
buf.ReadAll(All); buf.ReadAll(All);
TEST_EQUAL(All.size(), 4); TEST_EQUAL(All.size(), 4);
TEST_EQUAL(memcmp(All.data(), "\x05\xac\x02\x00", All.size()), 0); TEST_EQUAL(memcmp(All.data(), "\x05\xac\x02\x00", All.size()), 0);

View File

@ -158,7 +158,7 @@ add_library(GeneratorTestingSupport STATIC
${GENERATING_HDRS} ${GENERATING_HDRS}
${STUBS} ${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("Stubs" FILES ${STUBS})
source_group("Generating" FILES ${GENERATING_HDRS} ${GENERATING_SRCS}) source_group("Generating" FILES ${GENERATING_HDRS} ${GENERATING_SRCS})

View File

@ -78,7 +78,7 @@ source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS})
source_group("Sources" FILES ${SRCS}) source_group("Sources" FILES ${SRCS})
source_group("Lua files" FILES Test.lua) source_group("Lua files" FILES Test.lua)
add_executable(LuaThreadStress ${SRCS} ${SHARED_SRCS} ${SHARED_HDRS} 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) add_test(NAME LuaThreadStress-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND LuaThreadStress)

View File

@ -69,7 +69,7 @@ endif()
source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS}) source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS})
source_group("Sources" FILES ${SRCS}) source_group("Sources" FILES ${SRCS})
add_executable(SchematicFileSerializer-exe ${SRCS} ${SHARED_SRCS} ${SHARED_HDRS}) 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) add_test(NAME SchematicFileSerializer-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND SchematicFileSerializer-exe)

View File

@ -16,10 +16,9 @@ static void DoTest(void)
cBlockArea ba; cBlockArea ba;
ba.Create(21, 256, 21); ba.Create(21, 256, 21);
ba.RelLine(0, 0, 0, 9, 8, 7, cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_WOODEN_STAIRS, 1); ba.RelLine(0, 0, 0, 9, 8, 7, cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_WOODEN_STAIRS, 1);
AString Schematic; const auto Schematic = cSchematicFileSerializer::SaveToSchematicString(ba);
TEST_TRUE(cSchematicFileSerializer::SaveToSchematicString(ba, Schematic));
cBlockArea ba2; cBlockArea ba2;
TEST_TRUE(cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic)); cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic.GetView());
} }