Добавлена задержка перед выгрузкой чанков
This commit is contained in:
@@ -1635,6 +1635,8 @@ VulkanRenderSession::VulkanRenderSession(Vulkan *vkInst, IServerSession *serverS
|
|||||||
}
|
}
|
||||||
|
|
||||||
VulkanRenderSession::~VulkanRenderSession() {
|
VulkanRenderSession::~VulkanRenderSession() {
|
||||||
|
|
||||||
|
|
||||||
if(VoxelOpaquePipeline)
|
if(VoxelOpaquePipeline)
|
||||||
vkDestroyPipeline(VkInst->Graphics.Device, VoxelOpaquePipeline, nullptr);
|
vkDestroyPipeline(VkInst->Graphics.Device, VoxelOpaquePipeline, nullptr);
|
||||||
if(VoxelTransparentPipeline)
|
if(VoxelTransparentPipeline)
|
||||||
|
|||||||
@@ -1223,10 +1223,10 @@ class VulkanRenderSession : public IRenderSession {
|
|||||||
glm::vec3 X64Offset_f, X64Delta; // Смещение мира относительно игрока в матрице вида (0 -> 64)
|
glm::vec3 X64Offset_f, X64Delta; // Смещение мира относительно игрока в матрице вида (0 -> 64)
|
||||||
glm::quat Quat;
|
glm::quat Quat;
|
||||||
|
|
||||||
ChunkPreparator CP;
|
|
||||||
ModelProvider MP;
|
|
||||||
std::unique_ptr<TextureProvider> TP;
|
std::unique_ptr<TextureProvider> TP;
|
||||||
|
ModelProvider MP;
|
||||||
std::unique_ptr<NodestateProvider> NSP;
|
std::unique_ptr<NodestateProvider> NSP;
|
||||||
|
ChunkPreparator CP;
|
||||||
|
|
||||||
AtlasImage LightDummy;
|
AtlasImage LightDummy;
|
||||||
Buffer TestQuad;
|
Buffer TestQuad;
|
||||||
|
|||||||
@@ -65,12 +65,14 @@ AssetsPreloader::Out_checkAndPrepareResourcesUpdate AssetsPreloader::checkAndPre
|
|||||||
) {
|
) {
|
||||||
assert(idResolver);
|
assert(idResolver);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
assert(_Reloading.compare_exchange_strong(expected, true) && "Двойной вызов reloadResources");
|
assert(_Reloading.compare_exchange_strong(expected, true) && "Двойной вызов reloadResources");
|
||||||
struct ReloadGuard {
|
struct ReloadGuard {
|
||||||
std::atomic<bool>& Flag;
|
std::atomic<bool>& Flag;
|
||||||
~ReloadGuard() { Flag.exchange(false); }
|
~ReloadGuard() { Flag.exchange(false); }
|
||||||
} guard{_Reloading};
|
} guard{_Reloading};
|
||||||
|
#endif
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ReloadStatus secondStatus;
|
ReloadStatus secondStatus;
|
||||||
|
|||||||
@@ -925,9 +925,9 @@ void GameServer::BackingAsyncLua_t::run(int id) {
|
|||||||
constexpr int kTestGlobalY = 64;
|
constexpr int kTestGlobalY = 64;
|
||||||
if(regionBase.y <= kTestGlobalY && (regionBase.y + 63) >= kTestGlobalY) {
|
if(regionBase.y <= kTestGlobalY && (regionBase.y + 63) >= kTestGlobalY) {
|
||||||
int localY = kTestGlobalY - regionBase.y;
|
int localY = kTestGlobalY - regionBase.y;
|
||||||
setNode(2, localY, 2, kNodeLava, 0, false);
|
setNode(7, localY, 2, kNodeLava, 0, false);
|
||||||
setNode(4, localY, 2, kNodeWater, 0, false);
|
setNode(8, localY, 2, kNodeWater, 0, false);
|
||||||
setNode(6, localY, 2, kNodeFire, 0, false);
|
setNode(9, localY, 2, kNodeFire, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1432,6 +1432,7 @@ void GameServer::run() {
|
|||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
((uint32_t&) Game.AfterStartTime) += (uint32_t) (CurrentTickDuration*256);
|
((uint32_t&) Game.AfterStartTime) += (uint32_t) (CurrentTickDuration*256);
|
||||||
|
Game.Tick++;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point atTickStart = std::chrono::steady_clock::now();
|
std::chrono::steady_clock::time_point atTickStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
@@ -1725,73 +1726,202 @@ void GameServer::reloadMods() {
|
|||||||
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
||||||
IWorldSaveBackend::TickSyncInfo_In toDB;
|
IWorldSaveBackend::TickSyncInfo_In toDB;
|
||||||
|
|
||||||
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
|
constexpr uint32_t kRegionUnloadDelayTicks = 300;
|
||||||
assert(remoteClient);
|
constexpr uint8_t kUnloadHysteresisExtraRegions = 1;
|
||||||
// Пересчитать зоны наблюдения
|
const uint32_t nowTick = Game.Tick;
|
||||||
if(remoteClient->CrossedRegion) {
|
|
||||||
remoteClient->CrossedRegion = false;
|
|
||||||
|
|
||||||
// Пересчёт зон наблюдения
|
|
||||||
std::vector<ContentViewCircle> newCVCs;
|
|
||||||
|
|
||||||
{
|
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
|
||||||
std::vector<std::tuple<WorldId_t, Pos::Object, uint8_t>> points = remoteClient->getViewPoints();
|
assert(remoteClient);
|
||||||
for(auto& [wId, pos, radius] : points) {
|
|
||||||
assert(radius < 5);
|
// 1) Если игрок пересёк границу региона — пересчитываем области наблюдения.
|
||||||
|
// Вводим гистерезис: загрузка по "внутренней" границе, выгрузка по "внешней" (+1 регион).
|
||||||
|
if(remoteClient->CrossedRegion) {
|
||||||
|
remoteClient->CrossedRegion = false;
|
||||||
|
|
||||||
|
std::vector<ContentViewCircle> innerCVCs;
|
||||||
|
std::vector<ContentViewCircle> outerCVCs;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<std::tuple<WorldId_t, Pos::Object, uint8_t>> points = remoteClient->getViewPoints();
|
||||||
|
for(auto& [wId, pos, radius] : points) {
|
||||||
|
assert(radius < 5);
|
||||||
|
|
||||||
|
// Внутренняя область (на загрузку)
|
||||||
|
{
|
||||||
ContentViewCircle cvc;
|
ContentViewCircle cvc;
|
||||||
cvc.WorldId = wId;
|
cvc.WorldId = wId;
|
||||||
cvc.Pos = Pos::Object_t::asRegionsPos(pos);
|
cvc.Pos = Pos::Object_t::asRegionsPos(pos);
|
||||||
cvc.Range = radius*radius;
|
cvc.Range = int16_t(radius * radius);
|
||||||
|
|
||||||
std::vector<ContentViewCircle> list = Expanse.accumulateContentViewCircles(cvc);
|
std::vector<ContentViewCircle> list = Expanse.accumulateContentViewCircles(cvc);
|
||||||
newCVCs.insert(newCVCs.end(), list.begin(), list.end());
|
innerCVCs.insert(innerCVCs.end(), list.begin(), list.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Внешняя область (на удержание/выгрузку) = внутренняя + 1 регион
|
||||||
|
{
|
||||||
|
uint8_t outerRadius = radius + kUnloadHysteresisExtraRegions;
|
||||||
|
if(outerRadius > 5) outerRadius = 5;
|
||||||
|
|
||||||
|
ContentViewCircle cvc;
|
||||||
|
cvc.WorldId = wId;
|
||||||
|
cvc.Pos = Pos::Object_t::asRegionsPos(pos);
|
||||||
|
cvc.Range = int16_t(outerRadius * outerRadius);
|
||||||
|
|
||||||
|
std::vector<ContentViewCircle> list = Expanse.accumulateContentViewCircles(cvc);
|
||||||
|
outerCVCs.insert(outerCVCs.end(), list.begin(), list.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentViewInfo newCbg = Expanse_t::makeContentViewInfo(newCVCs);
|
ContentViewInfo viewInner = Expanse_t::makeContentViewInfo(innerCVCs);
|
||||||
|
ContentViewInfo viewOuter = Expanse_t::makeContentViewInfo(outerCVCs);
|
||||||
|
|
||||||
ContentViewInfo_Diff diff = newCbg.diffWith(remoteClient->ContentViewState);
|
// Отменяем отложенную выгрузку для регионов, которые снова попали во внешнюю область
|
||||||
if(!diff.WorldsNew.empty()) {
|
for(const auto& [worldId, regions] : viewOuter.Regions) {
|
||||||
// Сообщить о новых мирах
|
auto itWorld = remoteClient->PendingRegionUnload.find(worldId);
|
||||||
for(const WorldId_t id : diff.WorldsNew) {
|
if(itWorld == remoteClient->PendingRegionUnload.end())
|
||||||
auto iter = Expanse.Worlds.find(id);
|
continue;
|
||||||
assert(iter != Expanse.Worlds.end());
|
|
||||||
|
|
||||||
remoteClient->prepareWorldUpdate(id, iter->second.get());
|
for(const Pos::GlobalRegion& pos : regions) {
|
||||||
}
|
itWorld->second.erase(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteClient->ContentViewState = newCbg;
|
if(itWorld->second.empty())
|
||||||
// Вычистка не наблюдаемых регионов
|
remoteClient->PendingRegionUnload.erase(itWorld);
|
||||||
for(const auto& [worldId, regions] : diff.RegionsLost)
|
}
|
||||||
remoteClient->prepareRegionsRemove(worldId, regions);
|
|
||||||
// и миров
|
|
||||||
for(const WorldId_t worldId : diff.WorldsLost)
|
|
||||||
remoteClient->prepareWorldRemove(worldId);
|
|
||||||
|
|
||||||
// Подписываем игрока на наблюдение за регионами
|
// Загрузка: только по внутренней границе
|
||||||
for(const auto& [worldId, regions] : diff.RegionsNew) {
|
ContentViewInfo_Diff diffInner = viewInner.diffWith(remoteClient->ContentViewState);
|
||||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
|
||||||
assert(iterWorld != Expanse.Worlds.end());
|
|
||||||
|
|
||||||
std::vector<Pos::GlobalRegion> notLoaded = iterWorld->second->onRemoteClient_RegionsEnter(worldId, remoteClient, regions);
|
if(!diffInner.WorldsNew.empty()) {
|
||||||
if(!notLoaded.empty()) {
|
// Сообщить о новых мирах
|
||||||
// Добавляем к списку на загрузку
|
for(const WorldId_t id : diffInner.WorldsNew) {
|
||||||
std::vector<Pos::GlobalRegion> &tl = toDB.Load[worldId];
|
auto iter = Expanse.Worlds.find(id);
|
||||||
tl.insert(tl.end(), notLoaded.begin(), notLoaded.end());
|
assert(iter != Expanse.Worlds.end());
|
||||||
}
|
|
||||||
|
remoteClient->prepareWorldUpdate(id, iter->second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подписываем игрока на наблюдение за регионами (внутренняя область)
|
||||||
|
for(const auto& [worldId, regions] : diffInner.RegionsNew) {
|
||||||
|
// Добавляем в состояние клиента (слиянием, т.к. там могут быть регионы на удержании)
|
||||||
|
{
|
||||||
|
auto& cur = remoteClient->ContentViewState.Regions[worldId];
|
||||||
|
std::vector<Pos::GlobalRegion> merged;
|
||||||
|
merged.reserve(cur.size() + regions.size());
|
||||||
|
std::merge(cur.begin(), cur.end(), regions.begin(), regions.end(), std::back_inserter(merged));
|
||||||
|
merged.erase(std::unique(merged.begin(), merged.end()), merged.end());
|
||||||
|
cur = std::move(merged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отписываем то, что игрок больше не наблюдает
|
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||||
for(const auto& [worldId, regions] : diff.RegionsLost) {
|
assert(iterWorld != Expanse.Worlds.end());
|
||||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
|
||||||
assert(iterWorld != Expanse.Worlds.end());
|
|
||||||
|
|
||||||
iterWorld->second->onRemoteClient_RegionsLost(worldId, remoteClient, regions);
|
std::vector<Pos::GlobalRegion> notLoaded = iterWorld->second->onRemoteClient_RegionsEnter(worldId, remoteClient, regions);
|
||||||
|
if(!notLoaded.empty()) {
|
||||||
|
// Добавляем к списку на загрузку
|
||||||
|
std::vector<Pos::GlobalRegion> &tl = toDB.Load[worldId];
|
||||||
|
tl.insert(tl.end(), notLoaded.begin(), notLoaded.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кандидаты на выгрузку: то, что есть у клиента, но не попадает во внешнюю область (гистерезис)
|
||||||
|
for(const auto& [worldId, curRegions] : remoteClient->ContentViewState.Regions) {
|
||||||
|
std::vector<Pos::GlobalRegion> outer;
|
||||||
|
auto itOuter = viewOuter.Regions.find(worldId);
|
||||||
|
if(itOuter != viewOuter.Regions.end())
|
||||||
|
outer = itOuter->second;
|
||||||
|
|
||||||
|
std::vector<Pos::GlobalRegion> toDelay;
|
||||||
|
toDelay.reserve(curRegions.size());
|
||||||
|
|
||||||
|
if(outer.empty()) {
|
||||||
|
toDelay = curRegions;
|
||||||
|
} else {
|
||||||
|
std::set_difference(
|
||||||
|
curRegions.begin(), curRegions.end(),
|
||||||
|
outer.begin(), outer.end(),
|
||||||
|
std::back_inserter(toDelay)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!toDelay.empty()) {
|
||||||
|
auto& pending = remoteClient->PendingRegionUnload[worldId];
|
||||||
|
for(const Pos::GlobalRegion& pos : toDelay) {
|
||||||
|
// если уже ждёт выгрузки — не трогаем
|
||||||
|
if(pending.find(pos) == pending.end())
|
||||||
|
pending[pos] = nowTick + kRegionUnloadDelayTicks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2) Отложенная выгрузка: если истекла задержка — реально удаляем регион из зоны видимости
|
||||||
|
if(!remoteClient->PendingRegionUnload.empty()) {
|
||||||
|
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> expiredByWorld;
|
||||||
|
|
||||||
|
for(auto itWorld = remoteClient->PendingRegionUnload.begin(); itWorld != remoteClient->PendingRegionUnload.end(); ) {
|
||||||
|
const WorldId_t worldId = itWorld->first;
|
||||||
|
auto& regMap = itWorld->second;
|
||||||
|
|
||||||
|
std::vector<Pos::GlobalRegion> expired;
|
||||||
|
for(auto itReg = regMap.begin(); itReg != regMap.end(); ) {
|
||||||
|
if(itReg->second <= nowTick) {
|
||||||
|
expired.push_back(itReg->first);
|
||||||
|
itReg = regMap.erase(itReg);
|
||||||
|
} else {
|
||||||
|
++itReg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!expired.empty()) {
|
||||||
|
std::sort(expired.begin(), expired.end());
|
||||||
|
expiredByWorld[worldId] = std::move(expired);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(regMap.empty())
|
||||||
|
itWorld = remoteClient->PendingRegionUnload.erase(itWorld);
|
||||||
|
else
|
||||||
|
++itWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Применяем выгрузку: отписка + сообщение клиенту + актуализация ContentViewState
|
||||||
|
for(auto& [worldId, expired] : expiredByWorld) {
|
||||||
|
// Удаляем регионы из состояния клиента
|
||||||
|
auto itCur = remoteClient->ContentViewState.Regions.find(worldId);
|
||||||
|
if(itCur != remoteClient->ContentViewState.Regions.end()) {
|
||||||
|
std::vector<Pos::GlobalRegion> kept;
|
||||||
|
kept.reserve(itCur->second.size());
|
||||||
|
|
||||||
|
std::set_difference(
|
||||||
|
itCur->second.begin(), itCur->second.end(),
|
||||||
|
expired.begin(), expired.end(),
|
||||||
|
std::back_inserter(kept)
|
||||||
|
);
|
||||||
|
|
||||||
|
itCur->second = std::move(kept);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сообщаем клиенту и мирам
|
||||||
|
remoteClient->prepareRegionsRemove(worldId, expired);
|
||||||
|
|
||||||
|
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||||
|
if(iterWorld != Expanse.Worlds.end()) {
|
||||||
|
iterWorld->second->onRemoteClient_RegionsLost(worldId, remoteClient, expired);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если в мире больше нет наблюдаемых регионов — удалить мир у клиента
|
||||||
|
auto itStateWorld = remoteClient->ContentViewState.Regions.find(worldId);
|
||||||
|
if(itStateWorld != remoteClient->ContentViewState.Regions.end() && itStateWorld->second.empty()) {
|
||||||
|
remoteClient->ContentViewState.Regions.erase(itStateWorld);
|
||||||
|
remoteClient->prepareWorldRemove(worldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for(auto& [worldId, regions] : toDB.Load) {
|
for(auto& [worldId, regions] : toDB.Load) {
|
||||||
std::sort(regions.begin(), regions.end());
|
std::sort(regions.begin(), regions.end());
|
||||||
auto eraseIter = std::unique(regions.begin(), regions.end());
|
auto eraseIter = std::unique(regions.begin(), regions.end());
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ class GameServer : public AsyncObject {
|
|||||||
struct {
|
struct {
|
||||||
std::vector<std::shared_ptr<RemoteClient>> RemoteClients;
|
std::vector<std::shared_ptr<RemoteClient>> RemoteClients;
|
||||||
ServerTime AfterStartTime = {0, 0};
|
ServerTime AfterStartTime = {0, 0};
|
||||||
|
// Счётчик тактов (увеличивается на 1 каждый тик в GameServer::run)
|
||||||
|
uint32_t Tick = 0;
|
||||||
|
|
||||||
} Game;
|
} Game;
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,10 @@ public:
|
|||||||
ContentViewInfo ContentViewState;
|
ContentViewInfo ContentViewState;
|
||||||
// Если игрок пересекал границы региона (для перерасчёта ContentViewState)
|
// Если игрок пересекал границы региона (для перерасчёта ContentViewState)
|
||||||
bool CrossedRegion = true;
|
bool CrossedRegion = true;
|
||||||
|
|
||||||
|
// Отложенная выгрузка регионов (гистерезис + задержка)
|
||||||
|
// worldId -> (regionPos -> tick_deadline)
|
||||||
|
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, uint32_t>> PendingRegionUnload;
|
||||||
std::queue<Pos::GlobalNode> Build, Break;
|
std::queue<Pos::GlobalNode> Build, Break;
|
||||||
std::optional<ServerEntityId_t> PlayerEntity;
|
std::optional<ServerEntityId_t> PlayerEntity;
|
||||||
|
|
||||||
|
|||||||
@@ -338,9 +338,10 @@ class ByteBuffer : public std::vector<uint8_t> {
|
|||||||
if(Index + sizeof(T) > Obj->size())
|
if(Index + sizeof(T) > Obj->size())
|
||||||
throw std::runtime_error("Вышли за пределы буфера");
|
throw std::runtime_error("Вышли за пределы буфера");
|
||||||
|
|
||||||
const uint8_t *ptr = Obj->data()+Index;
|
T value{};
|
||||||
Index += sizeof(T);
|
std::memcpy(&value, Obj->data() + Index, sizeof(T));
|
||||||
return swapEndian(*(const T*) ptr);
|
Index += sizeof(T);
|
||||||
|
return swapEndian(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -362,8 +363,8 @@ class ByteBuffer : public std::vector<uint8_t> {
|
|||||||
inline Reader& operator>>(int64_t &value) { value = readOffset<int64_t>(); return *this; }
|
inline Reader& operator>>(int64_t &value) { value = readOffset<int64_t>(); return *this; }
|
||||||
inline Reader& operator>>(uint64_t &value) { value = readOffset<uint64_t>(); return *this; }
|
inline Reader& operator>>(uint64_t &value) { value = readOffset<uint64_t>(); return *this; }
|
||||||
inline Reader& operator>>(bool &value) { value = readOffset<uint8_t>(); return *this; }
|
inline Reader& operator>>(bool &value) { value = readOffset<uint8_t>(); return *this; }
|
||||||
inline Reader& operator>>(float &value) { return operator>>(*(uint32_t*) &value); }
|
inline Reader& operator>>(float &value) { uint32_t raw = readOffset<uint32_t>(); std::memcpy(&value, &raw, sizeof(raw)); return *this; }
|
||||||
inline Reader& operator>>(double &value) { return operator>>(*(uint64_t*) &value); }
|
inline Reader& operator>>(double &value) { uint64_t raw = readOffset<uint64_t>(); std::memcpy(&value, &raw, sizeof(raw)); return *this; }
|
||||||
|
|
||||||
inline int8_t readInt8() { int8_t value; this->operator>>(value); return value; }
|
inline int8_t readInt8() { int8_t value; this->operator>>(value); return value; }
|
||||||
inline uint8_t readUInt8() { uint8_t value; this->operator>>(value); return value; }
|
inline uint8_t readUInt8() { uint8_t value; this->operator>>(value); return value; }
|
||||||
@@ -449,6 +450,17 @@ class ByteBuffer : public std::vector<uint8_t> {
|
|||||||
size_t Index = 0;
|
size_t Index = 0;
|
||||||
uint16_t BlockSize = 256;
|
uint16_t BlockSize = 256;
|
||||||
|
|
||||||
|
template<typename T> inline void writeRaw(const T &value)
|
||||||
|
{
|
||||||
|
uint8_t *ptr = checkBorder(sizeof(T));
|
||||||
|
std::memcpy(ptr, &value, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> inline void writeSwapped(const T &value)
|
||||||
|
{
|
||||||
|
T temp = swapEndian(value);
|
||||||
|
writeRaw(temp);
|
||||||
|
}
|
||||||
|
|
||||||
inline uint8_t* checkBorder(size_t count)
|
inline uint8_t* checkBorder(size_t count)
|
||||||
{
|
{
|
||||||
@@ -469,17 +481,17 @@ class ByteBuffer : public std::vector<uint8_t> {
|
|||||||
Writer& operator=(const Writer&) = default;
|
Writer& operator=(const Writer&) = default;
|
||||||
Writer& operator=(Writer&&) = default;
|
Writer& operator=(Writer&&) = default;
|
||||||
|
|
||||||
inline Writer& operator<<(const int8_t &value) { *(int8_t*) checkBorder(sizeof(value)) = value; return *this; }
|
inline Writer& operator<<(const int8_t &value) { writeRaw(value); return *this; }
|
||||||
inline Writer& operator<<(const uint8_t &value) { *(uint8_t*) checkBorder(sizeof(value)) = value; return *this; }
|
inline Writer& operator<<(const uint8_t &value) { writeRaw(value); return *this; }
|
||||||
inline Writer& operator<<(const int16_t &value) { *(int16_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const int16_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const uint16_t &value) { *(uint16_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const uint16_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const int32_t &value) { *(int32_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const int32_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const uint32_t &value) { *(uint32_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const uint32_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const int64_t &value) { *(int64_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const int64_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const uint64_t &value) { *(uint64_t*) checkBorder(sizeof(value)) = swapEndian(value); return *this; }
|
inline Writer& operator<<(const uint64_t &value) { writeSwapped(value); return *this; }
|
||||||
inline Writer& operator<<(const bool &value) { *(uint8_t*) checkBorder(sizeof(value)) = uint8_t(value ? 1 : 0); return *this; }
|
inline Writer& operator<<(const bool &value) { uint8_t temp = value ? 1 : 0; writeRaw(temp); return *this; }
|
||||||
inline Writer& operator<<(const float &value) { *(uint32_t*) checkBorder(sizeof(value)) = swapEndian(*(uint32_t*) &value); return *this; }
|
inline Writer& operator<<(const float &value) { uint32_t raw; std::memcpy(&raw, &value, sizeof(raw)); writeSwapped(raw); return *this; }
|
||||||
inline Writer& operator<<(const double &value) { *(uint64_t*) checkBorder(sizeof(value)) = swapEndian(*(uint64_t*) &value); return *this; }
|
inline Writer& operator<<(const double &value) { uint64_t raw; std::memcpy(&raw, &value, sizeof(raw)); writeSwapped(raw); return *this; }
|
||||||
|
|
||||||
inline void writeInt8(const int8_t &value) { this->operator<<(value); }
|
inline void writeInt8(const int8_t &value) { this->operator<<(value); }
|
||||||
inline void writeUInt8(const uint8_t &value) { this->operator<<(value); }
|
inline void writeUInt8(const uint8_t &value) { this->operator<<(value); }
|
||||||
|
|||||||
Reference in New Issue
Block a user