0ad/source/network/NetHost.cpp
2026-05-21 18:42:04 +02:00

134 lines
3.3 KiB
C++

/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "NetHost.h"
#include "lib/debug.h"
#include "lib/external_libraries/enet.h"
#include "network/NetMessage.h"
#include "ps/CLogger.h"
#include <cstddef>
#include <numeric>
#include <vector>
std::vector<std::uint8_t> CNetHost::CreatePacket(const CNetMessage* message)
{
size_t size = message->GetSerializedLength();
ENSURE(size); // else we'll fail when accessing the 0th element
// Adjust buffer for message
std::vector<u8> buffer;
buffer.resize(size);
// Save message to internal buffer
message->Serialize(&buffer[0]);
return buffer;
}
void CNetHost::Initialize()
{
int ret = enet_initialize();
ENSURE(ret == 0);
}
void CNetHost::Deinitialize()
{
enet_deinitialize();
}
Stream::Stream(const std::int64_t streamId):
m_Id{streamId}
{}
void Stream::PushData(std::vector<std::uint8_t> data)
{
m_SendBuffer.push_back(std::move(data));
}
void Stream::PushMessage(const CNetMessage* message)
{
m_SendBuffer.push_back(CNetHost::CreatePacket(message));
}
std::optional<std::span<const std::uint8_t>> Stream::PeekData()
{
const std::size_t startOffset{m_SentOffset - m_AckedOffset};
std::size_t offset{0};
for (std::vector<std::uint8_t>& bytes : m_SendBuffer)
{
if (startOffset - offset < bytes.size())
{
const std::size_t temp{startOffset - offset};
return std::span{bytes.data() + temp, bytes.size() - temp};
}
offset += bytes.size();
}
return std::nullopt;
}
void Stream::MarkSent(const std::size_t offset)
{
m_SentOffset += offset;
}
void Stream::MarkAcknowledged(const std::size_t offset)
{
while (!m_SendBuffer.empty())
{
std::vector<uint8_t>& head{m_SendBuffer.front()};
if (m_AckedOffset + head.size() > offset)
break;
m_AckedOffset += head.size();
m_SendBuffer.pop_front();
}
}
std::optional<std::vector<std::uint8_t>> Stream::Receive(const std::span<const std::uint8_t> data)
{
m_ReceiveBuffer.emplace_back(data.begin(), data.end());
const std::size_t bufferSize{std::transform_reduce(m_ReceiveBuffer.begin(), m_ReceiveBuffer.end(),
static_cast<std::size_t>(0), std::plus<>{}, std::mem_fn(&std::vector<std::uint8_t>::size))};
if (bufferSize < 3)
return std::nullopt;
const auto& message = m_ReceiveBuffer.front();
auto bufferIter = message.begin();
std::size_t messageSize;
Deserialize_int_1(bufferIter, std::ignore);
Deserialize_int_2(bufferIter, messageSize);
if (messageSize > bufferSize)
return std::nullopt;
std::vector<std::uint8_t> messageCopy;
while (messageCopy.size() < messageSize)
{
messageCopy.insert(messageCopy.end(), m_ReceiveBuffer.front().begin(),
m_ReceiveBuffer.front().end());
m_ReceiveBuffer.pop_front();
}
return messageCopy;
}