Support packet fragmentation
This commit is contained in:
parent
0205bea826
commit
4bffc7920d
@ -4,6 +4,7 @@
|
|||||||
#include "auth.hpp"
|
#include "auth.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "network.hpp"
|
#include "network.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
#include "profile_infos.hpp"
|
#include "profile_infos.hpp"
|
||||||
|
|
||||||
#include <game/game.hpp>
|
#include <game/game.hpp>
|
||||||
@ -17,6 +18,8 @@
|
|||||||
#include <utils/info_string.hpp>
|
#include <utils/info_string.hpp>
|
||||||
#include <utils/cryptography.hpp>
|
#include <utils/cryptography.hpp>
|
||||||
|
|
||||||
|
#include <game/fragment_handler.hpp>
|
||||||
|
|
||||||
namespace auth
|
namespace auth
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@ -100,10 +103,7 @@ namespace auth
|
|||||||
std::string serialize_connect_data(const char* data, const int length)
|
std::string serialize_connect_data(const char* data, const int length)
|
||||||
{
|
{
|
||||||
utils::byte_buffer buffer{};
|
utils::byte_buffer buffer{};
|
||||||
profile_infos::profile_info info{};
|
profile_infos::get_profile_info().value_or(profile_infos::profile_info{}).serialize(buffer);
|
||||||
info.version = 4; // invalid
|
|
||||||
info.serialize(buffer);
|
|
||||||
//profile_infos::get_profile_info().value_or(profile_infos::profile_info{}).serialize(buffer);
|
|
||||||
|
|
||||||
buffer.write_string(data, static_cast<size_t>(length));
|
buffer.write_string(data, static_cast<size_t>(length));
|
||||||
|
|
||||||
@ -112,24 +112,38 @@ namespace auth
|
|||||||
return buffer.move_buffer();
|
return buffer.move_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, int len)
|
void send_fragmented_connect_packet(const game::netsrc_t sock, game::netadr_t* adr, const char* data,
|
||||||
|
const int length)
|
||||||
|
{
|
||||||
|
const auto connect_data = serialize_connect_data(data, length);
|
||||||
|
game::fragment_handler::fragment_data //
|
||||||
|
(connect_data.data(), connect_data.size(), [&](const utils::byte_buffer& buffer)
|
||||||
|
{
|
||||||
|
utils::byte_buffer packet_buffer{};
|
||||||
|
packet_buffer.write("connect");
|
||||||
|
packet_buffer.write(" ");
|
||||||
|
packet_buffer.write(buffer);
|
||||||
|
|
||||||
|
const auto& fragment_packet = packet_buffer.get_buffer();
|
||||||
|
|
||||||
|
game::NET_OutOfBandData(
|
||||||
|
sock, adr, fragment_packet.data(),
|
||||||
|
static_cast<int>(fragment_packet.size()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, const int len)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string buffer{};
|
|
||||||
|
|
||||||
const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
||||||
if (is_connect_sequence)
|
if (!is_connect_sequence)
|
||||||
{
|
{
|
||||||
buffer.append("connect");
|
return game::NET_OutOfBandData(sock, adr, data, len);
|
||||||
buffer.push_back(' ');
|
|
||||||
buffer.append(serialize_connect_data(data, len));
|
|
||||||
|
|
||||||
data = buffer.data();
|
|
||||||
len = static_cast<int>(buffer.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reinterpret_cast<decltype(&send_connect_data_stub)>(0x142173600_g)(sock, adr, data, len);
|
send_fragmented_connect_packet(sock, adr, data, len);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -139,16 +153,8 @@ namespace auth
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_connect_packet(const game::netadr_t& target, const network::data_view& data)
|
void dispatch_connect_packet(const game::netadr_t& target, const std::string& data)
|
||||||
{
|
{
|
||||||
if (!game::is_server_running())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
printf("Deserialized with size: %llX\n", data.size());
|
|
||||||
|
|
||||||
utils::byte_buffer buffer(data);
|
utils::byte_buffer buffer(data);
|
||||||
const profile_infos::profile_info info(buffer);
|
const profile_infos::profile_info info(buffer);
|
||||||
|
|
||||||
@ -177,6 +183,22 @@ namespace auth
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_connect_packet_fragment(const game::netadr_t& target, const network::data_view& data)
|
||||||
|
{
|
||||||
|
if (!game::is_server_running())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::byte_buffer buffer(data);
|
||||||
|
|
||||||
|
std::string final_packet{};
|
||||||
|
if (game::fragment_handler::handle(target, buffer, final_packet))
|
||||||
|
{
|
||||||
|
dispatch_connect_packet(target, final_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_guid()
|
uint64_t get_guid()
|
||||||
@ -200,7 +222,7 @@ namespace auth
|
|||||||
{
|
{
|
||||||
// Skip connect handler
|
// Skip connect handler
|
||||||
utils::hook::set<uint8_t>(game::select(0x142253EFA, 0x14053714A), 0xEB);
|
utils::hook::set<uint8_t>(game::select(0x142253EFA, 0x14053714A), 0xEB);
|
||||||
network::on("connect", handle_connect_packet);
|
network::on("connect", handle_connect_packet_fragment);
|
||||||
|
|
||||||
// Patch steam id bit check
|
// Patch steam id bit check
|
||||||
std::vector<std::pair<size_t, size_t>> patches{};
|
std::vector<std::pair<size_t, size_t>> patches{};
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "network.hpp"
|
#include "network.hpp"
|
||||||
|
|
||||||
|
#include "game/fragment_handler.hpp"
|
||||||
|
|
||||||
namespace network
|
namespace network
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@ -292,6 +294,8 @@ namespace network
|
|||||||
{
|
{
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
|
scheduler::loop(game::fragment_handler::clean, scheduler::async, 5s);
|
||||||
|
|
||||||
utils::hook::nop(game::select(0x1423322B6, 0x140596DF6), 4);
|
utils::hook::nop(game::select(0x1423322B6, 0x140596DF6), 4);
|
||||||
// don't increment data pointer to optionally skip socket byte
|
// don't increment data pointer to optionally skip socket byte
|
||||||
utils::hook::call(game::select(0x142332283, 0x140596DC3), read_socket_byte_stub);
|
utils::hook::call(game::select(0x142332283, 0x140596DC3), read_socket_byte_stub);
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include "party.hpp"
|
#include "party.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
#include <utils/properties.hpp>
|
#include <utils/properties.hpp>
|
||||||
#include <utils/concurrency.hpp>
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
@ -14,6 +13,7 @@
|
|||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
#include "game/utils.hpp"
|
#include "game/utils.hpp"
|
||||||
|
#include "game/fragment_handler.hpp"
|
||||||
|
|
||||||
namespace profile_infos
|
namespace profile_infos
|
||||||
{
|
{
|
||||||
@ -44,9 +44,12 @@ namespace profile_infos
|
|||||||
return {std::move(info)};
|
return {std::move(info)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_profile_info(const game::netadr_t& address, const std::string& buffer)
|
void send_profile_info(const game::netadr_t& address, const std::string& data)
|
||||||
{
|
{
|
||||||
network::send(address, "profileInfo", buffer);
|
game::fragment_handler::fragment_data(data.data(), data.size(), [&address](const utils::byte_buffer& buffer)
|
||||||
|
{
|
||||||
|
network::send(address, "profileInfo", buffer.get_buffer());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void distribute_profile_info(const uint64_t user_id, const profile_info& info)
|
void distribute_profile_info(const uint64_t user_id, const profile_info& info)
|
||||||
@ -253,10 +256,16 @@ namespace profile_infos
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::byte_buffer buffer(data);
|
utils::byte_buffer buffer(data);
|
||||||
|
|
||||||
|
std::string final_packet{};
|
||||||
|
if (game::fragment_handler::handle(server, buffer, final_packet))
|
||||||
|
{
|
||||||
|
buffer = utils::byte_buffer(final_packet);
|
||||||
const auto user_id = buffer.read<uint64_t>();
|
const auto user_id = buffer.read<uint64_t>();
|
||||||
const profile_info info(buffer);
|
const profile_info info(buffer);
|
||||||
|
|
||||||
add_profile_info(user_id, info);
|
add_profile_info(user_id, info);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
155
src/client/game/fragment_handler.cpp
Normal file
155
src/client/game/fragment_handler.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "fragment_handler.hpp"
|
||||||
|
|
||||||
|
namespace game::fragment_handler
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr size_t MAX_FRAGMENTS = 100;
|
||||||
|
|
||||||
|
using fragments = std::unordered_map<size_t, std::string>;
|
||||||
|
|
||||||
|
struct fragmented_packet
|
||||||
|
{
|
||||||
|
size_t fragment_count{0};
|
||||||
|
fragments fragments{};
|
||||||
|
std::chrono::high_resolution_clock::time_point insertion_time = std::chrono::high_resolution_clock::now();
|
||||||
|
};
|
||||||
|
|
||||||
|
using id_fragment_map = std::unordered_map<uint64_t, fragmented_packet>;
|
||||||
|
using address_fragment_map = std::unordered_map<netadr_t, id_fragment_map>;
|
||||||
|
|
||||||
|
utils::concurrency::container<address_fragment_map> global_map{};
|
||||||
|
|
||||||
|
std::vector<std::string> construct_fragments(const void* data, const size_t length)
|
||||||
|
{
|
||||||
|
std::vector<std::string> fragments{};
|
||||||
|
|
||||||
|
constexpr size_t max_fragment_size = 0x400;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; i += max_fragment_size)
|
||||||
|
{
|
||||||
|
const auto current_fragment_size = std::min(length - i, max_fragment_size);
|
||||||
|
|
||||||
|
std::string fragment(static_cast<const char*>(data) + i, current_fragment_size);
|
||||||
|
fragments.push_back(std::move(fragment));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(const netadr_t& target, utils::byte_buffer& buffer, std::string& final_packet)
|
||||||
|
{
|
||||||
|
const auto fragment_id = buffer.read<uint64_t>();
|
||||||
|
const size_t fragment_count = buffer.read<uint32_t>();
|
||||||
|
const size_t fragment_index = buffer.read<uint32_t>();
|
||||||
|
|
||||||
|
auto fragment_data = buffer.get_remaining_data();
|
||||||
|
|
||||||
|
if (fragment_index > fragment_count || !fragment_count || fragment_count > MAX_FRAGMENTS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return global_map.access<bool>([&](address_fragment_map& map)
|
||||||
|
{
|
||||||
|
auto& user_map = map[target];
|
||||||
|
if (!user_map.contains(fragment_id) && user_map.size() > MAX_FRAGMENTS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& packet_queue = user_map[fragment_id];
|
||||||
|
|
||||||
|
if (packet_queue.fragment_count == 0)
|
||||||
|
{
|
||||||
|
packet_queue.fragment_count = fragment_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet_queue.fragment_count != fragment_count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet_queue.fragments.size() + 1 < fragment_count)
|
||||||
|
{
|
||||||
|
packet_queue.fragments[fragment_index] = std::move(fragment_data);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final_packet.clear();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fragment_count; ++i)
|
||||||
|
{
|
||||||
|
if (i == fragment_index)
|
||||||
|
{
|
||||||
|
final_packet.append(fragment_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final_packet.append(packet_queue.fragments.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean()
|
||||||
|
{
|
||||||
|
global_map.access([](address_fragment_map& map)
|
||||||
|
{
|
||||||
|
for (auto i = map.begin(); i != map.end();)
|
||||||
|
{
|
||||||
|
auto& user_map = i->second;
|
||||||
|
|
||||||
|
for (auto j = user_map.begin(); j != user_map.end();)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - j->second.insertion_time;
|
||||||
|
|
||||||
|
if (diff > 5s)
|
||||||
|
{
|
||||||
|
j = user_map.erase(j);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_map.empty())
|
||||||
|
{
|
||||||
|
i = map.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment_handler::fragment_data(const void* data, const size_t size,
|
||||||
|
const std::function<void(const utils::byte_buffer& buffer)>& callback)
|
||||||
|
{
|
||||||
|
static std::atomic_uint64_t current_id{0};
|
||||||
|
const auto id = current_id++;
|
||||||
|
|
||||||
|
const auto fragments = construct_fragments(data, size);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fragments.size(); ++i)
|
||||||
|
{
|
||||||
|
utils::byte_buffer buffer{};
|
||||||
|
buffer.write(id);
|
||||||
|
buffer.write(static_cast<uint32_t>(fragments.size()));
|
||||||
|
buffer.write(static_cast<uint32_t>(i));
|
||||||
|
|
||||||
|
auto& fragment = fragments.at(i);
|
||||||
|
buffer.write(fragment.data(), fragment.size());
|
||||||
|
|
||||||
|
callback(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/client/game/fragment_handler.hpp
Normal file
17
src/client/game/fragment_handler.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/byte_buffer.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
#include "../component/network.hpp"
|
||||||
|
|
||||||
|
namespace game::fragment_handler
|
||||||
|
{
|
||||||
|
bool handle(const netadr_t& target, utils::byte_buffer& buffer,
|
||||||
|
std::string& final_packet);
|
||||||
|
|
||||||
|
void clean();
|
||||||
|
|
||||||
|
void fragment_data(const void* data, const size_t size,
|
||||||
|
const std::function<void(const utils::byte_buffer& buffer)>& callback);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PROTOCOL 2
|
#define PROTOCOL 3
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
namespace game
|
namespace game
|
||||||
|
@ -31,7 +31,9 @@ namespace game
|
|||||||
WEAK symbol<void(const char* gametype, bool loadDefaultSettings)> Com_GametypeSettings_SetGametype{
|
WEAK symbol<void(const char* gametype, bool loadDefaultSettings)> Com_GametypeSettings_SetGametype{
|
||||||
0x1420F5980
|
0x1420F5980
|
||||||
};
|
};
|
||||||
WEAK symbol<unsigned int(const char* settingName, bool getDefault)> Com_GametypeSettings_GetUInt{0x1420F4E00, 0x1404FE5C0};
|
WEAK symbol<unsigned int(const char* settingName, bool getDefault)> Com_GametypeSettings_GetUInt{
|
||||||
|
0x1420F4E00, 0x1404FE5C0
|
||||||
|
};
|
||||||
WEAK symbol<bool()> Com_IsRunningUILevel{0x142148350};
|
WEAK symbol<bool()> Com_IsRunningUILevel{0x142148350};
|
||||||
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
||||||
0x14214A4D0
|
0x14214A4D0
|
||||||
@ -77,12 +79,13 @@ namespace game
|
|||||||
WEAK symbol<bool(uint64_t, int*, bool)> Live_GetConnectivityInformation{0x141E0C380};
|
WEAK symbol<bool(uint64_t, int*, bool)> Live_GetConnectivityInformation{0x141E0C380};
|
||||||
|
|
||||||
// Info
|
// Info
|
||||||
WEAK symbol<const char* (const char*, const char* key)> Info_ValueForKey{ 0x1422E87B0 };
|
WEAK symbol<const char*(const char*, const char* key)> Info_ValueForKey{0x1422E87B0};
|
||||||
|
|
||||||
// MSG
|
// MSG
|
||||||
WEAK symbol<uint8_t(msg_t* msg)> MSG_ReadByte{0x142155450, 0x14050D1B0};
|
WEAK symbol<uint8_t(msg_t* msg)> MSG_ReadByte{0x142155450, 0x14050D1B0};
|
||||||
|
|
||||||
// NET
|
// NET
|
||||||
|
WEAK symbol<bool(netsrc_t sock, netadr_t* adr, const void* data, int len)> NET_OutOfBandData{0x142173600};
|
||||||
WEAK symbol<bool(netsrc_t sock, int length, const void* data, const netadr_t* to)> NET_SendPacket{
|
WEAK symbol<bool(netsrc_t sock, int length, const void* data, const netadr_t* to)> NET_SendPacket{
|
||||||
0x1423323B0, 0x140596E40
|
0x1423323B0, 0x140596E40
|
||||||
};
|
};
|
||||||
@ -147,7 +150,7 @@ namespace game
|
|||||||
WEAK symbol<void(hks::lua_State*, const char*)> Lua_CoD_LoadLuaFile{0x141F11A20, 0x0};
|
WEAK symbol<void(hks::lua_State*, const char*)> Lua_CoD_LoadLuaFile{0x141F11A20, 0x0};
|
||||||
WEAK symbol<void(int localClientNum)> CG_LUIHUDRestart{0x140F7E970};
|
WEAK symbol<void(int localClientNum)> CG_LUIHUDRestart{0x140F7E970};
|
||||||
WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0};
|
WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0};
|
||||||
WEAK symbol<void(const char* key, int value, hks::lua_State* luaVM)> Lua_SetTableInt{ 0x141F066E0 };
|
WEAK symbol<void(const char* key, int value, hks::lua_State* luaVM)> Lua_SetTableInt{0x141F066E0};
|
||||||
|
|
||||||
// Scr
|
// Scr
|
||||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x1412E9870, 0x14016F160};
|
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x1412E9870, 0x14016F160};
|
||||||
|
@ -41,9 +41,9 @@ namespace utils
|
|||||||
this->offset_ += length;
|
this->offset_ += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> byte_buffer::read_data(const size_t length)
|
std::string byte_buffer::read_data(const size_t length)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> result{};
|
std::string result{};
|
||||||
result.resize(length);
|
result.resize(length);
|
||||||
|
|
||||||
this->read(result.data(), result.size());
|
this->read(result.data(), result.size());
|
||||||
|
@ -20,6 +20,11 @@ namespace utils
|
|||||||
|
|
||||||
void write(const void* buffer, size_t length);
|
void write(const void* buffer, size_t length);
|
||||||
|
|
||||||
|
void write(const char* text)
|
||||||
|
{
|
||||||
|
this->write(text, strlen(text));
|
||||||
|
}
|
||||||
|
|
||||||
void write_string(const char* str, const size_t length)
|
void write_string(const char* str, const size_t length)
|
||||||
{
|
{
|
||||||
this->write<uint32_t>(static_cast<uint32_t>(length));
|
this->write<uint32_t>(static_cast<uint32_t>(length));
|
||||||
@ -42,6 +47,13 @@ namespace utils
|
|||||||
this->write(&object, sizeof(object));
|
this->write(&object, sizeof(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void write<byte_buffer>(const byte_buffer& object)
|
||||||
|
{
|
||||||
|
const auto& buffer = object.get_buffer();
|
||||||
|
this->write(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write(const std::vector<T>& vec)
|
void write(const std::vector<T>& vec)
|
||||||
{
|
{
|
||||||
@ -109,7 +121,17 @@ namespace utils
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> read_data(size_t length);
|
size_t get_remaining_size() const
|
||||||
|
{
|
||||||
|
return this->buffer_.size() - offset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remaining_data()
|
||||||
|
{
|
||||||
|
return this->read_data(this->get_remaining_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read_data(size_t length);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool writing_{false};
|
bool writing_{false};
|
||||||
|
Loading…
Reference in New Issue
Block a user