// 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/>. // // Includes // #include <string> #include <queue> #include <nel/misc/types_nl.h> #include <nel/misc/debug.h> #include <nel/net/udp_sock.h> // // Using // using namespace std; using namespace NLMISC; using namespace NLNET; // // Variables // static uint32 Lag = 0; static uint8 PacketLoss = 0; static uint8 PacketDuplication = 0; static uint8 PacketDisordering = 0; // // Class // struct CBufferizedPacket { CBufferizedPacket (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; } } ~CBufferizedPacket () { 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 // static queue<CBufferizedPacket*> BufferizedPackets; // // Prototypes // void sendUDPNow (CUdpSock *client, const uint8 *packet, uint32 packetSize, const CInetAddress *addr); // // Functions // void updateBufferizedPackets () { TTime ct = CTime::getLocalTime (); while (!BufferizedPackets.empty()) { CBufferizedPacket *bp = BufferizedPackets.front (); if (bp->Time <= ct) { // time to send the message sendUDPNow (bp->Client, bp->Packet, bp->PacketSize, bp->Addr); delete bp; BufferizedPackets.pop (); } else { break; } } } void setSimlagValues (sint32 lag, sint8 packetLoss, sint8 packetDuplication, sint8 packetDisordering) { if (lag != -1) Lag = lag; if (packetLoss != -1) PacketLoss = packetLoss; if (packetDuplication != -1) PacketDuplication = packetDuplication; if (packetDisordering != -1) PacketDisordering = packetDisordering; } void sendUDPNow (CUdpSock *client, const uint8 *packet, uint32 packetSize, const CInetAddress *addr) { if (addr == NULL) client->send (packet, packetSize); else client->sendTo (packet, packetSize, *addr); uint32 packetNumber = *(uint32 *)packet; // nlinfo ("time %"NL_I64"u sending now packet %5u", CTime::getLocalTime (), packetNumber); } void sendUDP (CUdpSock *client, const uint8 *packet, uint32 packetSize, const CInetAddress *addr) { nlassert (client != NULL); nlassert (packet != NULL); nlassert (packetSize > 0); if ((float)rand()/(float)(RAND_MAX)*100.0f >= PacketLoss) { sint32 lag = Lag /*+ (rand()%40) - 20*/;// void disordering if (lag > 100) { // send the packet later CBufferizedPacket *bp = new CBufferizedPacket (client, packet, packetSize, lag, addr); // duplicate the packet if ((float)rand()/(float)(RAND_MAX)*100.0f < PacketDisordering && BufferizedPackets.size() > 0) { CBufferizedPacket *bp2 = BufferizedPackets.back(); // exange the time TTime t = bp->Time; bp->Time = bp2->Time; bp2->Time = t; // exange packet in the buffer BufferizedPackets.back() = bp; bp = bp2; } BufferizedPackets.push (bp); // duplicate the packet if ((float)rand()/(float)(RAND_MAX)*100.0f < PacketDuplication) { CBufferizedPacket *bp = new CBufferizedPacket (client, packet, packetSize, lag, addr); BufferizedPackets.push (bp); } } else { // send the packet NOW sendUDPNow (client, packet, packetSize, addr); // duplicate the packet if ((float)rand()/(float)(RAND_MAX)*100.0f < PacketDuplication) { sendUDPNow (client, packet, packetSize, addr); } } } }