#pragma once #include #include namespace sp { namespace details { template struct MessageEncapsulatorPack {}; template <> struct MessageEncapsulatorPack<> { static DataBuffer Encapsulate(const DataBuffer& a_Data) { return a_Data; } static DataBuffer Decapsulate(DataBuffer& a_Data) { return a_Data; } }; template struct MessageEncapsulatorPack { static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) { DataBuffer data = io::MessageEncapsulator::Encapsulate(a_Data, a_Option); return MessageEncapsulatorPack::Encapsulate(data, a_Options...); } static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) { DataBuffer data = io::MessageEncapsulator::Decapsulate(a_Data, a_Option); return MessageEncapsulatorPack::Decapsulate(data, a_Options...); } }; } // namespace details namespace io { template Stream::Stream(IOInterface&& a_Interface, TOptions&&... a_Options) : m_Interface(std::move(a_Interface)), m_Options(std::make_tuple(std::move(a_Options)...)) {} template Stream::Stream( Stream&& a_Stream) : m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {} template void Stream::SendMessage(const MessageBase& a_Message) { DataBuffer data = a_Message.Write(); DataBuffer encapsulated = std::apply([&data](const auto&... a_Options) { return Encapsulate(data, a_Options...); }, m_Options); DataBuffer finalData; finalData << VarInt{encapsulated.GetSize()} << encapsulated; m_Interface.Write(finalData); } template void Stream::RecieveMessages() { while (true) { // reading the first VarInt part byte by byte std::uint64_t lenghtValue = 0; unsigned int readPos = 0; while (true) { static constexpr int SEGMENT_BITS = (1 << 7) - 1; static constexpr int CONTINUE_BIT = 1 << 7; DataBuffer buffer = m_Interface.Read(sizeof(std::uint8_t)); // eof if (buffer.GetSize() == 0) return; std::uint8_t part; buffer >> part; lenghtValue |= static_cast(part & SEGMENT_BITS) << readPos; if ((part & CONTINUE_BIT) == 0) break; readPos += 7; if (readPos >= 8 * sizeof(lenghtValue)) throw std::runtime_error("VarInt is too big"); } // nothing to read if (lenghtValue == 0) return; DataBuffer buffer; buffer = m_Interface.Read(lenghtValue); buffer = std::apply([&buffer, lenghtValue](const auto&... a_Options) { return Decapsulate(buffer, a_Options...); }, m_Options); VarInt packetType; buffer >> packetType; static const MessageFactory messageFactory; std::unique_ptr message = messageFactory.CreateMessage(packetType.GetValue()); assert(message != nullptr); message->Read(buffer); GetDispatcher().Dispatch(*message); } } template DataBuffer Stream::Encapsulate( const DataBuffer& a_Data, const TOptions&... a_Options) { return details::MessageEncapsulatorPack::Encapsulate(a_Data, a_Options...); } template DataBuffer Stream::Decapsulate( DataBuffer& a_Data, const TOptions&... a_Options) { return details::MessageEncapsulatorPack::Decapsulate(a_Data, a_Options...); } } // namespace io } // namespace sp