/* 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 .
*/
#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
#include
#include
std::vector 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 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 data)
{
m_SendBuffer.push_back(std::move(data));
}
void Stream::PushMessage(const CNetMessage* message)
{
m_SendBuffer.push_back(CNetHost::CreatePacket(message));
}
std::optional> Stream::PeekData()
{
const std::size_t startOffset{m_SentOffset - m_AckedOffset};
std::size_t offset{0};
for (std::vector& 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& head{m_SendBuffer.front()};
if (m_AckedOffset + head.size() > offset)
break;
m_AckedOffset += head.size();
m_SendBuffer.pop_front();
}
}
std::optional> Stream::Receive(const std::span 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(0), std::plus<>{}, std::mem_fn(&std::vector::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 messageCopy;
while (messageCopy.size() < messageSize)
{
messageCopy.insert(messageCopy.end(), m_ReceiveBuffer.front().begin(),
m_ReceiveBuffer.front().end());
m_ReceiveBuffer.pop_front();
}
return messageCopy;
}