| #include "CNetTransceiver.h"
|
| #include "CEngine.h"
|
| #include "U_Log.h"
|
|
|
| #include <algorithm>
|
|
|
| CNetTransceiver::CNetTransceiver(unsigned short port, size_t buffsize) : IoThread([this]() { boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_guard(boost::asio::make_work_guard(IoContext)); IoContext.run(); })
|
| {
|
| State = STATE_IDLE;
|
|
|
| try
|
| {
|
| Socket = std::make_unique<boost::asio::ip::udp::socket>(IoContext, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port));
|
| }
|
| catch (const boost::system::system_error& ex)
|
| {
|
| bool ok = true;
|
| try
|
| {
|
| Socket = std::make_unique<boost::asio::ip::udp::socket>(IoContext, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0U));
|
| }
|
| catch (const boost::system::system_error& ex)
|
| {
|
| ok = false;
|
| }
|
|
|
| if (!ok)
|
| {
|
| Log::ErrInstance() << "Can't init network transceiver\n";
|
| }
|
| else
|
| {
|
| Log::ErrInstance() << "Network port " << port << " was busy, another one was chosen\n";
|
| }
|
| }
|
|
|
| LocalPort = Socket->local_endpoint().port();
|
| LocalIpAddress = Socket->local_endpoint().address();
|
|
|
| Queue.Container.reserve(128); //TODO not hardcoded
|
| Buffer.resize(buffsize);
|
|
|
| Log::Instance() << "Network transceiver is working on " << LocalIpAddress.to_string() << ":" << LocalPort << " with buffer size of " << Buffer.size() << " bytes\n";
|
| }
|
|
|
| CPeerProperty::CPeerProperty(boost::asio::ip::udp::endpoint endpoint, CType in, CType out, bool blockwholeip)
|
| {
|
| EndPoint = endpoint;
|
| TypeIn = in;
|
| TypeOut = out;
|
|
|
| BlockWholeIP = blockwholeip;
|
| }
|
|
|
| void CNetTransceiver::SetNonBlocking(bool enable)
|
| {
|
| Socket->non_blocking(enable);
|
| }
|
|
|
| bool CNetTransceiver::GetNonBlocking()
|
| {
|
| return Socket->non_blocking();
|
| }
|
|
|
| CNetTransceiver::~CNetTransceiver()
|
| {
|
| Stop();
|
| }
|
|
|
| void CPeerProperty::SetTimer(CTimePoint resettime)
|
| {
|
| TimerSet = true;
|
| ResetTime = resettime;
|
| }
|
|
|
| void CPeerProperty::DisableTimer()
|
| {
|
| TimerSet = false;
|
| }
|
|
|
| void CNetTransceiver::StartSync()
|
| {
|
| m_Async = false;
|
| State = STATE_RUNNING;
|
| }
|
|
|
| void CNetTransceiver::StartAsync()
|
| {
|
| m_Async = true;
|
| State = STATE_RUNNING;
|
| ReceiveAsync();
|
| }
|
|
|
| void CNetTransceiver::Stop()
|
| {
|
| if (State == STATE_IDLE) { return; }
|
|
|
| State = STATE_STOP;
|
| //IoThread.detach();
|
|
|
| if (!InReceivingRoutine && !InSendingRoutine)
|
| {
|
| State = STATE_IDLE;
|
| }
|
| else
|
| {
|
| while (State.load() != STATE_IDLE) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); }
|
| }
|
|
|
| IoContext.stop();
|
| IoThread.join();
|
| }
|
|
|
| void CNetTransceiver::ReceiveAsync()
|
| {
|
| if (State == STATE_STOP) { State = STATE_IDLE; return; }
|
|
|
| Receiving = true;
|
|
|
| Socket->async_receive_from(boost::asio::buffer(Buffer), RemoteEndpoint,
|
| [this](boost::system::error_code ec, std::size_t bytes_recvd)
|
| {
|
| Receiving = false;
|
| InReceivingRoutine = true;
|
|
|
| m_networkError = ec;
|
|
|
| UpdateSinglePeerTimer(RemoteEndpoint);
|
| if (CanReceiveFromPeer(RemoteEndpoint))
|
| {
|
| if (bytes_recvd && ec.value() == 0)
|
| {
|
| CTimePoint recvd_at = CEngine::GetInstance()->Time.GetCurrent();
|
|
|
| if(Queue.Enabled)
|
| {
|
| CPacket pkt;
|
| pkt.From = RemoteEndpoint;
|
| pkt.Data.assign(Buffer.begin(), Buffer.begin() + bytes_recvd);
|
| pkt.ReceivedAt = recvd_at;
|
|
|
| {
|
| std::lock_guard<std::mutex> _lock(Queue.Mutex);
|
| Queue.Container.push_back(std::move(pkt));
|
| }
|
| }
|
|
|
| if(ProcessPacket)
|
| {
|
| ProcessPacket(RemoteEndpoint, Buffer, bytes_recvd, recvd_at);
|
| }
|
| }
|
| }
|
|
|
| InReceivingRoutine = false;
|
| if (State == STATE_STOP) { State = STATE_IDLE; return; }
|
| boost::asio::post(Socket->get_executor(), [this]() { ReceiveAsync(); });
|
| });
|
| }
|
|
|
| void CNetTransceiver::SendAsync(boost::asio::ip::udp::endpoint point, const std::vector<std::uint8_t>& msg, std::function<void(boost::system::error_code, std::size_t, CTimePoint)> callback)
|
| {
|
| UpdateSinglePeerTimer(point);
|
| if (!CanSendToPeer(point)) { return; }
|
| Sending = InSendingRoutine = true;
|
| Socket->async_send_to(boost::asio::buffer(msg), point, [this, callback](boost::system::error_code ec, std::size_t bytes_sent)
|
| {
|
| m_networkError = ec;
|
| Sending = false;
|
| InSendingRoutine = false;
|
|
|
| LastSend = CEngine::GetInstance()->Time.GetCurrent();
|
| if(callback)
|
| {
|
| callback(ec, bytes_sent, LastSend);
|
| }
|
| });
|
| }
|
|
|
| void CNetTransceiver::ReceiveSync()
|
| {
|
| Receiving = InReceivingRoutine = true;
|
| size_t bytes_recvd = Socket->receive_from(boost::asio::buffer(Buffer), RemoteEndpoint, 0, m_networkError);
|
| Receiving = false;
|
|
|
| UpdateSinglePeerTimer(RemoteEndpoint);
|
| if (CanReceiveFromPeer(RemoteEndpoint))
|
| {
|
| if (bytes_recvd && m_networkError.value() == 0)
|
| {
|
| CTimePoint recvd_at = CEngine::GetInstance()->Time.GetCurrent();
|
| if(Queue.Enabled)
|
| {
|
| CPacket pkt;
|
| pkt.From = RemoteEndpoint;
|
| pkt.Data.assign(Buffer.begin(), Buffer.begin() + bytes_recvd);
|
| pkt.ReceivedAt = recvd_at;
|
|
|
| {
|
| std::lock_guard<std::mutex> _lock(Queue.Mutex);
|
| Queue.Container.push_back(std::move(pkt));
|
| }
|
| }
|
|
|
| if(ProcessPacket)
|
| {
|
| ProcessPacket(RemoteEndpoint, Buffer, bytes_recvd, recvd_at);
|
| }
|
| }
|
| }
|
| InReceivingRoutine = false;
|
| }
|
|
|
| CTimePoint CNetTransceiver::GetLastSendTime() const
|
| {
|
| return LastSend;
|
| }
|
|
|
| boost::system::error_code CNetTransceiver::GetLastError() const
|
| {
|
| return m_networkError;
|
| }
|
|
|
| void CNetTransceiver::SendSync(boost::asio::ip::udp::endpoint point, const std::vector<std::uint8_t>& msg)
|
| {
|
| UpdateSinglePeerTimer(point);
|
| if (!CanSendToPeer(point)) { return; }
|
|
|
| Sending = InSendingRoutine = true;
|
| Socket->send_to(boost::asio::buffer(msg), point, 0, m_networkError);
|
|
|
| LastSend = CEngine::GetInstance()->Time.GetCurrent();
|
| Sending = InSendingRoutine = false;
|
| }
|
|
|
| bool CNetTransceiver::IsAsync() { return m_Async; }
|
|
|
| bool CPeerProperty::IsValid()
|
| {
|
| return TypeIn != CType::None || CPeerProperty::TypeOut != CType::None;
|
| }
|
|
|
| CPeerProperty CPeerProperty::GetInvalid()
|
| {
|
| return { {}, CPeerProperty::CType::None, CPeerProperty::CType::None };
|
| }
|
|
|
| void CPeerProperty::SetTypeIn(CPeerProperty::CType typ)
|
| {
|
| if (TypeIn == CType::Protect && typ == CType::Block) { return; }
|
| TypeIn = typ;
|
| }
|
|
|
| void CPeerProperty::SetTypeOut(CPeerProperty::CType typ)
|
| {
|
| if (TypeOut == CType::Protect && typ == CType::Block) { return; }
|
| TypeOut = typ;
|
| }
|
|
|
| void CPeerProperty::SetTypes(CType typin, CType typout)
|
| {
|
| SetTypeIn(typin);
|
| SetTypeOut(typout);
|
| }
|
|
|
| std::vector<CPeerProperty>::iterator CNetTransceiver::getPeerIterator(boost::asio::ip::udp::endpoint point)
|
| {
|
| return std::find_if(Filter.begin(), Filter.end(), [point](const CPeerProperty& prop) -> bool
|
| {
|
| if (prop.BlockWholeIP) { return prop.EndPoint.address() == point.address(); }
|
| return prop.EndPoint.address() == point.address() && prop.EndPoint.port() == point.port();
|
| });
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetPeerProperty(boost::asio::ip::udp::endpoint point, CPeerProperty::CType typein, CPeerProperty::CType typeout)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it == Filter.end()) { Filter.push_back({ point, typein, typeout }); return Filter.back(); }
|
| (*it).SetTypes(typein, typeout);
|
| return *it;
|
| }
|
|
|
| void CNetTransceiver::ResetPeerProperty(boost::asio::ip::udp::endpoint point)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it != Filter.end()) { Filter.erase(it); }
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetPeerPropertyTypeIn(boost::asio::ip::udp::endpoint point, CPeerProperty::CType typein)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it == Filter.end()) { Filter.push_back({ point, typein, CPeerProperty::CType::None }); return Filter.back(); }
|
| (*it).SetTypeIn(typein);
|
| return *it;
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetPeerPropertyTypeOut(boost::asio::ip::udp::endpoint point, CPeerProperty::CType typeout)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it == Filter.end()) { Filter.push_back({ point, CPeerProperty::CType::None, typeout }); return Filter.back(); }
|
| (*it).SetTypeOut(typeout);
|
| return *it;
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetPeerPropertyTypeBoth(boost::asio::ip::udp::endpoint point, CPeerProperty::CType typeboth)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it == Filter.end()) { Filter.push_back({ point, typeboth, typeboth }); return Filter.back(); }
|
| (*it).SetTypes(typeboth, typeboth);
|
| return *it;
|
| }
|
|
|
| bool CNetTransceiver::GetPeerProperty(boost::asio::ip::udp::endpoint point, CPeerProperty& prop)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it != Filter.end()) { prop = *it; return true; }
|
| return false;
|
| }
|
|
|
| CPeerProperty CNetTransceiver::GetPeerProperty(boost::asio::ip::udp::endpoint point)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it != Filter.end()) { return *it; }
|
| return CPeerProperty::GetInvalid();
|
| }
|
|
|
| bool CNetTransceiver::PeerHasProperty(boost::asio::ip::udp::endpoint point)
|
| {
|
| auto it = getPeerIterator(point);
|
| return it != Filter.end();
|
| }
|
|
|
| bool CNetTransceiver::CanSendToPeer(boost::asio::ip::udp::endpoint point)
|
| {
|
| CPeerProperty prop = GetPeerProperty(point);
|
|
|
| if (!m_InverseFilter)
|
| {
|
| if (!prop.IsValid()) { return true; }
|
| if (prop.TypeOut == CPeerProperty::CType::Block)
|
| {
|
| return false;
|
| }
|
| }
|
| else
|
| {
|
| if (prop.TypeOut != CPeerProperty::CType::Allow && prop.TypeOut != CPeerProperty::CType::Protect)
|
| {
|
| return false;
|
| }
|
| }
|
| return true;
|
| }
|
|
|
| size_t CNetTransceiver::CountIpsTypeIn(CPeerProperty::CType typein)
|
| {
|
| size_t ret = 0U;
|
| for (CPeerProperty& prop : Filter) { if (prop.TypeIn == typein) { ret++; } }
|
| return ret;
|
| }
|
|
|
| size_t CNetTransceiver::CountIpsTypeOut(CPeerProperty::CType typeout)
|
| {
|
| size_t ret = 0U;
|
| for (CPeerProperty& prop : Filter) { if (prop.TypeOut == typeout) { ret++; } }
|
| return ret;
|
| }
|
|
|
| size_t CNetTransceiver::CountIpsTypes(CPeerProperty::CType typein, CPeerProperty::CType typeout)
|
| {
|
| size_t ret = 0U;
|
| for (CPeerProperty& prop : Filter) { if (prop.TypeIn == typein && prop.TypeOut == typeout) { ret++; } }
|
| return ret;
|
| }
|
|
|
| size_t CNetTransceiver::CountIpsTypeBoth(CPeerProperty::CType typeboth)
|
| {
|
| size_t ret = 0U;
|
| for (CPeerProperty& prop : Filter) { if (prop.TypeIn == typeboth && prop.TypeOut == typeboth) { ret++; } }
|
| return ret;
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetIpProperty(boost::asio::ip::address addr, CPeerProperty::CType typein, CPeerProperty::CType typeout)
|
| {
|
| Filter.erase(std::remove_if(Filter.begin(), Filter.end(), [addr](const CPeerProperty& prop) -> bool
|
| { return prop.EndPoint.address() == addr; }), Filter.end());
|
|
|
| Filter.push_back({{ addr, 11231 }, typein, typeout, true }); //TODO is 11231 default client port?
|
| return Filter.back();
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetIpPropertyTypeIn(boost::asio::ip::address addr, CPeerProperty::CType typein)
|
| {
|
| return SetIpProperty(addr, typein, CPeerProperty::CType::None);
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetIpPropertyTypeOut(boost::asio::ip::address addr, CPeerProperty::CType typeout)
|
| {
|
| return SetIpProperty(addr, CPeerProperty::CType::None, typeout);
|
| }
|
|
|
| CPeerProperty& CNetTransceiver::SetIpPropertyTypeBoth(boost::asio::ip::address addr, CPeerProperty::CType typeboth)
|
| {
|
| return SetIpProperty(addr, typeboth, typeboth);
|
| }
|
|
|
| bool CNetTransceiver::CanReceiveFromPeer(boost::asio::ip::udp::endpoint point)
|
| {
|
| CPeerProperty prop = GetPeerProperty(point);
|
|
|
| if (!m_InverseFilter)
|
| {
|
| if (!prop.IsValid()) { return true; }
|
| if (prop.TypeIn == CPeerProperty::CType::Block)
|
| {
|
| return false;
|
| }
|
| }
|
| else
|
| {
|
| if (prop.TypeIn != CPeerProperty::CType::Allow && prop.TypeIn != CPeerProperty::CType::Protect)
|
| {
|
| return false;
|
| }
|
| }
|
| return true;
|
| }
|
|
|
| void CNetTransceiver::UpdatePeerTimers()
|
| {
|
| Filter.erase(std::remove_if(Filter.begin(), Filter.end(), [](const auto& p) { return p.TimerSet && CEngine::GetInstance()->Time.GetCurrent() >= p.ResetTime; }), Filter.end());
|
| }
|
|
|
| void CNetTransceiver::UpdateSinglePeerTimer(boost::asio::ip::udp::endpoint point)
|
| {
|
| auto it = getPeerIterator(point);
|
| if (it != Filter.end())
|
| {
|
| if ((*it).TimerSet && CEngine::GetInstance()->Time.GetCurrent() >= (*it).ResetTime)
|
| {
|
| Filter.erase(it);
|
| }
|
| }
|
| }
|
|
|
| void CNetTransceiver::SetInverseFilter(bool inversefilter)
|
| {
|
| m_InverseFilter = inversefilter;
|
| }
|
|
|
| void CNetTransceiver::ToggleInverseFilter()
|
| {
|
| m_InverseFilter = !m_InverseFilter;
|
| }
|
|
|
| bool CNetTransceiver::IsFilterInverse()
|
| {
|
| return m_InverseFilter;
|
| }
|
|
|
| char CNetTransceiver::STATE_IDLE = 0;
|
| char CNetTransceiver::STATE_RUNNING = 1;
|
| char CNetTransceiver::STATE_STOP = 2; |