#pragma once #include #include namespace sp { template class IOInterface { public: DataBuffer Read(std::size_t a_Amount); void Write(const DataBuffer& a_Data); }; template class IOStream { protected: MessageDispatcher m_Dispatcher; IOInterface m_Interface; using MessageBase = typename MessageDispatcher::MessageBaseType; using MsgIdType = typename MessageBase::MsgIdType; public: IOStream() {} IOStream(IOInterface&& a_Interface) : m_Interface(std::move(a_Interface)) {} IOStream(IOStream&& a_Stream) : m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {} void RecieveMessages(); void SendMessage(const MessageBase& a_Message); MessageDispatcher& GetDispatcher() { return m_Dispatcher; } }; template void IOStream::SendMessage(const MessageBase& a_Message) { // TODO: process compress + encryption DataBuffer data = a_Message.Write(); DataBuffer dataSize; m_Interface.Write(dataSize << sp::VarInt{data.GetSize()} << data); } template void IOStream::RecieveMessages() { // TODO: process compress + encryption 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)); // if non-blocking call 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"); } DataBuffer buffer; buffer = m_Interface.Read(lenghtValue); // TODO: process compress + encryption MsgIdType packetType; buffer >> packetType; static const MessageFactory messageFactory; std::unique_ptr message = messageFactory.CreateMessage(packetType); assert(message != nullptr); message->Read(buffer); GetDispatcher().Dispatch(*message); } } } // namespace sp