2012-05-29 13:31:11 +00:00
|
|
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program 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 Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "stdnet.h"
|
|
|
|
|
|
|
|
#include "nel/misc/config_file.h"
|
|
|
|
|
|
|
|
#include "nel/net/udp_sock.h"
|
|
|
|
#include "nel/net/udp_sim_sock.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace NLMISC;
|
|
|
|
|
|
|
|
namespace NLNET {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Class
|
|
|
|
//
|
|
|
|
|
|
|
|
struct CBufferizedOutPacket
|
|
|
|
{
|
|
|
|
CBufferizedOutPacket (CUdpSock *client, const uint8 *packet, uint32 packetSize, uint32 delay, const CInetAddress *addr):
|
|
|
|
Client(client), PacketSize(packetSize), Time(CTime::getLocalTime()+delay)
|
|
|
|
{
|
|
|
|
nlassert (packetSize > 0);
|
|
|
|
nlassert (packet != NULL);
|
|
|
|
nlassert (client != NULL);
|
|
|
|
|
|
|
|
Packet = new uint8[packetSize];
|
|
|
|
memcpy (Packet, packet, packetSize);
|
|
|
|
|
|
|
|
if (addr != NULL)
|
|
|
|
{
|
|
|
|
Addr = new CInetAddress;
|
|
|
|
*Addr = *addr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Addr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~CBufferizedOutPacket ()
|
|
|
|
{
|
|
|
|
nlassert (Packet != NULL);
|
|
|
|
delete [] Packet;
|
|
|
|
Packet = NULL;
|
|
|
|
Client = NULL;
|
|
|
|
PacketSize = 0;
|
|
|
|
Time = 0;
|
|
|
|
if (Addr != NULL)
|
|
|
|
delete Addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CUdpSock *Client;
|
|
|
|
uint8 *Packet;
|
|
|
|
uint32 PacketSize;
|
|
|
|
TTime Time;
|
|
|
|
CInetAddress *Addr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Variables
|
|
|
|
//
|
|
|
|
|
|
|
|
//queue<CBufferizedOutPacket*> CUdpSimSock::BufferizedOutPackets;
|
|
|
|
//queue<CBufferizedOutPacket*> CUdpSimSock::BufferizedInPackets;
|
|
|
|
|
|
|
|
uint32 CUdpSimSock::_InLag = 0;
|
|
|
|
uint8 CUdpSimSock::_InPacketLoss = 0;
|
|
|
|
|
|
|
|
uint32 CUdpSimSock::_OutLag = 0;
|
|
|
|
uint8 CUdpSimSock::_OutPacketLoss = 0;
|
|
|
|
uint8 CUdpSimSock::_OutPacketDuplication = 0;
|
|
|
|
uint8 CUdpSimSock::_OutPacketDisordering = 0;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Functions
|
|
|
|
//
|
|
|
|
|
|
|
|
void CUdpSimSock::sendUDPNow (const uint8 *buffer, uint32 len, const CInetAddress *addr)
|
|
|
|
{
|
|
|
|
if (addr == NULL)
|
|
|
|
UdpSock.send (buffer, len);
|
|
|
|
else
|
|
|
|
UdpSock.sendTo (buffer, len, *addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CUdpSimSock::sendUDP (const uint8 *buffer, uint32& len, const CInetAddress *addr)
|
|
|
|
{
|
|
|
|
nlassert (buffer != NULL);
|
|
|
|
nlassert (len > 0);
|
|
|
|
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f >= _OutPacketLoss)
|
|
|
|
{
|
|
|
|
sint32 lag = _OutLag /*+ (rand()%40) - 20*/;// void disordering
|
|
|
|
|
|
|
|
if (lag > 100)
|
|
|
|
{
|
|
|
|
// send the packet later
|
|
|
|
|
|
|
|
CBufferizedOutPacket *bp = new CBufferizedOutPacket (&UdpSock, buffer, len, lag, addr);
|
|
|
|
|
|
|
|
// duplicate the packet
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f < _OutPacketDisordering && _BufferizedOutPackets.size() > 0)
|
|
|
|
{
|
|
|
|
CBufferizedOutPacket *bp2 = _BufferizedOutPackets.back();
|
|
|
|
|
|
|
|
// exange the time
|
|
|
|
TTime t = bp->Time;
|
|
|
|
bp->Time = bp2->Time;
|
|
|
|
bp2->Time = t;
|
|
|
|
|
|
|
|
// exange packet in the buffer
|
|
|
|
_BufferizedOutPackets.back() = bp;
|
|
|
|
bp = bp2;
|
|
|
|
}
|
|
|
|
|
|
|
|
_BufferizedOutPackets.push (bp);
|
|
|
|
|
|
|
|
// duplicate the packet
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f < _OutPacketDuplication)
|
|
|
|
{
|
|
|
|
CBufferizedOutPacket *bp = new CBufferizedOutPacket (&UdpSock, buffer, len, lag, addr);
|
|
|
|
_BufferizedOutPackets.push (bp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// send the packet NOW
|
|
|
|
|
|
|
|
sendUDPNow (buffer, len, addr);
|
|
|
|
|
|
|
|
// duplicate the packet
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f < _OutPacketDuplication)
|
|
|
|
{
|
|
|
|
sendUDPNow (buffer, len, addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CUdpSimSock::updateBufferizedPackets ()
|
|
|
|
{
|
|
|
|
TTime ct = CTime::getLocalTime ();
|
|
|
|
while (!_BufferizedOutPackets.empty())
|
|
|
|
{
|
|
|
|
CBufferizedOutPacket *bp = _BufferizedOutPackets.front ();
|
|
|
|
if (bp->Time <= ct)
|
|
|
|
{
|
|
|
|
// time to send the message
|
|
|
|
sendUDPNow (bp->Packet, bp->PacketSize, bp->Addr);
|
|
|
|
delete bp;
|
|
|
|
_BufferizedOutPackets.pop ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cbSimVar (CConfigFile::CVar &var)
|
|
|
|
{
|
|
|
|
if (var.Name == "SimInLag") CUdpSimSock::_InLag = var.asInt ();
|
|
|
|
else if (var.Name == "SimInPacketLost") CUdpSimSock::_InPacketLoss = uint8(var.asInt ());
|
|
|
|
else if (var.Name == "SimOutLag") CUdpSimSock::_OutLag = var.asInt ();
|
|
|
|
else if (var.Name == "SimOutPacketLost") CUdpSimSock::_OutPacketLoss = uint8(var.asInt ());
|
|
|
|
else if (var.Name == "SimOutPacketDuplication") CUdpSimSock::_OutPacketDuplication = uint8(var.asInt ());
|
|
|
|
else if (var.Name == "SimOutPacketDisordering") CUdpSimSock::_OutPacketDisordering = uint8(var.asInt ());
|
|
|
|
else nlstop;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CUdpSimSock::setSimValues (NLMISC::CConfigFile &cf)
|
|
|
|
{
|
|
|
|
cf.setCallback ("SimInLag", cbSimVar);
|
|
|
|
cf.setCallback ("SimInPacketLost", cbSimVar);
|
|
|
|
cf.setCallback ("SimOutLag", cbSimVar);
|
|
|
|
cf.setCallback ("SimOutPacketLost", cbSimVar);
|
|
|
|
cf.setCallback ("SimOutPacketDuplication", cbSimVar);
|
|
|
|
cf.setCallback ("SimOutPacketDisordering", cbSimVar);
|
|
|
|
|
|
|
|
CConfigFile::CVar *pv;
|
|
|
|
pv = cf.getVarPtr("SimInLag");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
pv = cf.getVarPtr("SimInPacketLost");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
pv = cf.getVarPtr("SimOutLag");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
pv = cf.getVarPtr("SimOutPacketLost");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
pv = cf.getVarPtr("SimOutPacketDuplication");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
pv = cf.getVarPtr("SimOutPacketDisordering");
|
|
|
|
if( pv )
|
|
|
|
cbSimVar( *pv );
|
|
|
|
}
|
|
|
|
|
|
|
|
void CUdpSimSock::connect( const CInetAddress& addr )
|
|
|
|
{
|
|
|
|
UdpSock.connect (addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CUdpSimSock::close()
|
|
|
|
{
|
|
|
|
UdpSock.close ();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8 buffer [10000];
|
|
|
|
|
|
|
|
bool CUdpSimSock::dataAvailable ()
|
|
|
|
{
|
|
|
|
updateBufferizedPackets ();
|
|
|
|
|
|
|
|
if (_InLag > 0)
|
|
|
|
{
|
|
|
|
while (UdpSock.dataAvailable ())
|
|
|
|
{
|
|
|
|
CInetAddress addr;
|
|
|
|
uint len = 10000;
|
|
|
|
UdpSock.receivedFrom (buffer, len, addr);
|
|
|
|
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f >= _InPacketLoss)
|
|
|
|
{
|
|
|
|
CBufferizedOutPacket *bp = new CBufferizedOutPacket (&UdpSock, buffer, len, _InLag, &addr);
|
|
|
|
_BufferizedInPackets.push (bp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TTime ct = CTime::getLocalTime ();
|
|
|
|
if (!_BufferizedInPackets.empty() && _BufferizedInPackets.front ()->Time <= ct)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((float)rand()/(float)(RAND_MAX)*100.0f >= _InPacketLoss)
|
|
|
|
{
|
|
|
|
return UdpSock.dataAvailable ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// consume data
|
|
|
|
if (UdpSock.dataAvailable ())
|
|
|
|
{
|
|
|
|
CInetAddress addr;
|
|
|
|
uint len = 10000;
|
|
|
|
UdpSock.receivedFrom (buffer, len, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// packet lost
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CUdpSimSock::receive (uint8 *buffer, uint32& len, bool throw_exception)
|
|
|
|
{
|
|
|
|
if (_InLag> 0)
|
|
|
|
{
|
|
|
|
if (_BufferizedInPackets.empty())
|
|
|
|
{
|
|
|
|
if (throw_exception)
|
|
|
|
throw Exception ("no data available");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CBufferizedOutPacket *bp = _BufferizedInPackets.front ();
|
|
|
|
uint32 s = min (len, bp->PacketSize);
|
|
|
|
memcpy (buffer, bp->Packet, s);
|
|
|
|
len = s;
|
|
|
|
|
|
|
|
delete bp;
|
|
|
|
_BufferizedInPackets.pop ();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return UdpSock.receive(buffer, len, throw_exception);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CSock::TSockResult CUdpSimSock::send (const uint8 *buffer, uint32& len, bool /* throw_exception */)
|
|
|
|
{
|
|
|
|
sendUDP (buffer, len);
|
|
|
|
return CSock::Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CUdpSimSock::sendTo (const uint8 *buffer, uint32& len, const CInetAddress& addr)
|
|
|
|
{
|
|
|
|
sendUDP (buffer, len, &addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CUdpSimSock::connected()
|
|
|
|
{
|
|
|
|
return UdpSock.connected ();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // NLNET
|