ddnet/src/engine/shared/network_client.cpp
heinrich5991 4fb050ccb7 Use STUN to determine UDP connectivity and show diagnostics
These diagnostics are supposed to guide the user to problem resolution.
They're displayed if no packet is received from the server within one
second of connecting.

No message if we don't have STUN servers.

"Trying to determine UDP connectivity..." if no answer has been received
from the STUN server yet and it hasn't timed out yet.

"UDP seems to be filtered." if the STUN request has timed out.

"UDP and TCP IP addresses seem to be different. Try disabling VPN,
proxy or network accelerators." if the STUN request has returned an IP
address different from the one obtained via HTTP from info2.ddnet.tw.

"No answer from server yet." otherwise, if the STUN request has returned
no interesting data, indicating that it's likely the game server's
fault.
2022-05-18 09:21:31 +02:00

184 lines
4 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "network.h"
#include <base/system.h>
bool CNetClient::Open(NETADDR BindAddr)
{
// open socket
NETSOCKET Socket;
Socket = net_udp_create(BindAddr);
if(!Socket)
return false;
// clean it
mem_zero(this, sizeof(*this));
// init
m_Socket = Socket;
m_pStun = new CStun(m_Socket);
m_Connection.Init(m_Socket, false);
return true;
}
int CNetClient::Close()
{
if(!m_Socket)
return 0;
if(m_pStun)
{
delete m_pStun;
m_pStun = nullptr;
}
return net_udp_close(m_Socket);
}
int CNetClient::Disconnect(const char *pReason)
{
//dbg_msg("netclient", "disconnected. reason=\"%s\"", pReason);
m_Connection.Disconnect(pReason);
return 0;
}
int CNetClient::Update()
{
m_Connection.Update();
if(m_Connection.State() == NET_CONNSTATE_ERROR)
Disconnect(m_Connection.ErrorString());
m_pStun->Update();
return 0;
}
int CNetClient::Connect(NETADDR *pAddr)
{
m_Connection.Connect(pAddr);
return 0;
}
int CNetClient::ResetErrorString()
{
m_Connection.ResetErrorString();
return 0;
}
int CNetClient::Recv(CNetChunk *pChunk)
{
while(true)
{
// check for a chunk
if(m_RecvUnpacker.FetchChunk(pChunk))
return 1;
// TODO: empty the recvinfo
NETADDR Addr;
unsigned char *pData;
int Bytes = net_udp_recv(m_Socket, &Addr, &pData);
// no more packets for now
if(Bytes <= 0)
break;
if(m_pStun->OnPacket(Addr, pData, Bytes))
{
continue;
}
bool Sixup = false;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup) == 0)
{
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONNLESS)
{
pChunk->m_Flags = NETSENDFLAG_CONNLESS;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_EXTENDED)
{
pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
mem_copy(pChunk->m_aExtraData, m_RecvUnpacker.m_Data.m_aExtraData, sizeof(pChunk->m_aExtraData));
}
return 1;
}
else
{
if(m_Connection.State() != NET_CONNSTATE_OFFLINE && m_Connection.State() != NET_CONNSTATE_ERROR && net_addr_comp(m_Connection.PeerAddress(), &Addr) == 0 && m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
m_RecvUnpacker.Start(&Addr, &m_Connection, 0);
}
}
}
return 0;
}
int CNetClient::Send(CNetChunk *pChunk)
{
if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
{
dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize);
return -1;
}
if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
{
// send connectionless packet
CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize,
pChunk->m_Flags & NETSENDFLAG_EXTENDED, pChunk->m_aExtraData);
}
else
{
int Flags = 0;
dbg_assert(pChunk->m_ClientID == 0, "erroneous client id");
if(pChunk->m_Flags & NETSENDFLAG_VITAL)
Flags = NET_CHUNKFLAG_VITAL;
m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData);
if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
m_Connection.Flush();
}
return 0;
}
int CNetClient::State()
{
if(m_Connection.State() == NET_CONNSTATE_ONLINE)
return NETSTATE_ONLINE;
if(m_Connection.State() == NET_CONNSTATE_OFFLINE)
return NETSTATE_OFFLINE;
return NETSTATE_CONNECTING;
}
int CNetClient::Flush()
{
return m_Connection.Flush();
}
int CNetClient::GotProblems(int64_t MaxLatency) const
{
if(time_get() - m_Connection.LastRecvTime() > MaxLatency)
return 1;
return 0;
}
const char *CNetClient::ErrorString() const
{
return m_Connection.ErrorString();
}
void CNetClient::FeedStunServer(NETADDR StunServer)
{
m_pStun->FeedStunServer(StunServer);
}
void CNetClient::RefreshStun()
{
m_pStun->Refresh();
}
CONNECTIVITY CNetClient::GetConnectivity(int NetType, NETADDR *pGlobalAddr)
{
return m_pStun->GetConnectivity(NetType, pGlobalAddr);
}