/* 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; }