// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>

#if defined(WIN32)
    #include <conio.h>
    #include <sys\stat.h>
    #include <sys\types.h>

    #if defined(_DEBUG)
        #include <crtdbg.h>
    #endif
#else
    int memicmp(const void *p1, const void *p2, int len)
    {
        const unsigned char *c1 = (const unsigned char *)p1;
        const unsigned char *c2 = (const unsigned char *)p2;
        while (len--)
        {
            int res = toupper(*c1) - toupper(*c2);
            if (res)
                return(res);
            c1++;
            c2++;
        }
        return(0);
    }
#endif

#include "UdpLibrary.h"
#include "UdpDriverRecord.h"

using namespace UdpLibrary;


enum { cPacketNone
        , cPacketRequestList    // no data
        , cPacketData           // raw data
        , cPacketAddress        // string address
     };


class Player : public UdpConnectionHandler
{
    public:
        Player(UdpConnection *con, bool requestList);
        virtual ~Player();
        virtual void OnRoutePacket(UdpConnection *con, const udp_uchar *data, int dataLen);
        virtual void OnTerminated(UdpConnection *con);
        virtual void OnConnectComplete(UdpConnection *con);
        virtual void OnCrcReject(UdpConnection *con, const udp_uchar *data, int dataLen);
        virtual void OnPacketCorrupt(UdpConnection *con, const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
    public:
        UdpLinkedListMember<Player> mLink;
        UdpConnection *mConnection;
        bool mRequestList;
        int mId;
};

class PlayerManager : public UdpManagerHandler
{
    public:
        PlayerManager();
        virtual ~PlayerManager();

        void AddPlayer(/*gift*/ Player *player);
        void GiveTime();
        void DumpStats(FILE *file);
        void SendPacketToAll(UdpChannel channel, void *data, int dataLen);
        void SendAllPlayers(Player *toPlayer);
        int GetPlayersCount() { return(mPlayers.Count()); };
        void DisconnectAll();

        virtual void OnConnectRequest(UdpConnection *con);
        virtual int OnUserSuppliedEncrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
        virtual int OnUserSuppliedDecrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);

    private:
        UdpLinkedList<Player> mPlayers;
        int mNextId;
};


UdpManager *g_udpManager = NULL;
PlayerManager *g_playerManager = NULL;
bool g_noListen = false;
bool g_connectToCluster = true;

void EstablishConnections(char *address)
{
    UdpConnection *con = g_udpManager->EstablishConnection(address, 0);
    if (con == NULL)
    {
        printf("Error, could not create connection, invalid IP/Port may have been specified\n");
        return;
    }
    g_playerManager->AddPlayer(new Player(con, g_connectToCluster));
    con->Release();
}

int main(int argc, char **argv)
{
#if defined(WIN32)
    // SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);

    #if defined(_DEBUG)
        _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    #endif
#endif
        // set default parameter values
    int sendInterval = 30;
    int sendPacketMinSize = 235;
    int sendPacketMaxSize = sendPacketMinSize;
    int sendPacketCount = 40;
    char connectAddress[256];
    connectAddress[0] = 0;
    int listenPort = 0;
    int rawPacketSize = 1460;
    int clockSyncDelay = 0;
    int keepAliveDelay = 0;
    int runTimeSeconds = 0;
    int icmpIgnoreTime = 2000;
    int dumpSeconds = 0;
    int noDataTimeout = 0;
    int sleepDelay = 10;
    float packetlossPercent = 0.0f;
    int crcBytes = 2;
    int holdTime = 0;
    int latency = 0;
    int queueEventTime = 0;
    int toleranceLossCount = 0;
    int minimumFlowControlWindow = 0;
    bool lfn = false;
    bool reliableSends = true;
    bool threading = false;
    bool reliableCoalesce = true;
    char recordFilename[256];
    recordFilename[0] = 0;
    char playbackFilename[256];
    playbackFilename[0] = 0;

#if defined(__PPU__)   // PS3
    threading = true;
    listenPort = 0;
    sendPacketCount = 1;
    sendInterval = 100;
    strcpy(connectAddress, "64.37.155.1:5000");
#endif
        // read overrides from command line
    for (int i = 0; i < argc; i++)
    {
        if (memicmp(argv[i], "/CONNECT:", 9) == 0)
        {
            strcpy(connectAddress, argv[i] + 9);
        }

        if (memicmp(argv[i], "/RECORD:", 8) == 0)
        {
            strcpy(recordFilename, argv[i] + 8);
        }

        if (memicmp(argv[i], "/PLAYBACK:", 10) == 0)
        {
            strcpy(playbackFilename, argv[i] + 10);
        }

        if (memicmp(argv[i], "/SIZE:", 6) == 0)
        {
            sendPacketMaxSize = sendPacketMinSize = atoi(argv[i] + 6);
            char *cptr = strchr(argv[i], ',');
            if (cptr != NULL)
                sendPacketMaxSize = atoi(cptr + 1);
        }
        if (memicmp(argv[i], "/COUNT:", 7) == 0)
            sendPacketCount = atoi(argv[i] + 7);
        if (memicmp(argv[i], "/INTERVAL:", 10) == 0)
            sendInterval = atoi(argv[i] + 10);
        if (memicmp(argv[i], "/PORT:", 6) == 0)
            listenPort = atoi(argv[i] + 6);
        if (memicmp(argv[i], "/RAWSIZE:", 9) == 0)
            rawPacketSize = atoi(argv[i] + 9);
        if (memicmp(argv[i], "/CLOCKSYNC:", 11) == 0)
            clockSyncDelay = atoi(argv[i] + 11);
        if (memicmp(argv[i], "/HOLDTIME:", 10) == 0)
            holdTime = atoi(argv[i] + 10);
        if (memicmp(argv[i], "/KEEPALIVE:", 11) == 0)
            keepAliveDelay = atoi(argv[i] + 11);
        if (memicmp(argv[i], "/RUNTIME:", 9) == 0)
            runTimeSeconds = atoi(argv[i] + 9);
        if (memicmp(argv[i], "/DUMP:", 6) == 0)
            dumpSeconds = atoi(argv[i] + 6);
        if (memicmp(argv[i], "/LOSS:", 6) == 0)
            packetlossPercent = (float)atof(argv[i] + 6);
        if (memicmp(argv[i], "/LATENCY:", 9) == 0)
            latency = atoi(argv[i] + 9);
        if (memicmp(argv[i], "/SLEEP:", 7) == 0)
            sleepDelay = atoi(argv[i] + 7);
        if (memicmp(argv[i], "/TIMEOUT:", 9) == 0)
            noDataTimeout = atoi(argv[i] + 9);
        if (memicmp(argv[i], "/ICMPIGNORE:", 12) == 0)
            icmpIgnoreTime = atoi(argv[i] + 12);
        if (memicmp(argv[i], "/CLUSTER:", 9) == 0)
            g_connectToCluster = (0 != atoi(argv[i] + 9));
        if (memicmp(argv[i], "/LFN:", 5) == 0)
            lfn = (0 != atoi(argv[i] + 5));
        if (memicmp(argv[i], "/NOLISTEN:", 10) == 0)
            g_noListen = (0 != atoi(argv[i] + 10));
        if (memicmp(argv[i], "/RELIABLE:", 10) == 0)
            reliableSends = (0 != atoi(argv[i] + 10));
        if (memicmp(argv[i], "/THREAD:", 8) == 0)
            threading = (0 != atoi(argv[i] + 8));
        if (memicmp(argv[i], "/COALESCE:", 10) == 0)
            reliableCoalesce = (0 != atoi(argv[i] + 10));
        if (memicmp(argv[i], "/CRC:", 5) == 0)
            crcBytes = atoi(argv[i] + 5);
        if (memicmp(argv[i], "/QUEUE:", 7) == 0)
            queueEventTime = atoi(argv[i] + 7);
        if (memicmp(argv[i], "/TOLERANCE:", 11) == 0)
            toleranceLossCount = atoi(argv[i] + 11);
        if (memicmp(argv[i], "/MINFLOW:", 9) == 0)
            minimumFlowControlWindow = atoi(argv[i] + 9);
    }

        // validate and touchup parameters as need
    if (sendPacketMinSize < 1)
        sendPacketMinSize = 1;
    if (sendPacketMaxSize < sendPacketMinSize)
        sendPacketMaxSize = sendPacketMinSize;

        // display program usage and current settings
    fprintf(stderr, "UdpLibrary Stress Test, press SHIFT-F12 to terminate.\n");
    printf("    /CONNECT:xx.xx.xx.xx:port   (using=%s)\n", connectAddress);
    printf("    /INTERVAL:milliseconds      (using=%d)\n", sendInterval);
    printf("    /SIZE:minBytes[,maxBytes]   (using=%d,%d)\n", sendPacketMinSize, sendPacketMaxSize);
    printf("    /COUNT:packets              (using=%d)\n", sendPacketCount);
    printf("    /PORT:port                  (using=%d)\n", listenPort);
    printf("    /RAWSIZE:bytes              (using=%d)\n", rawPacketSize);
    printf("    /CRC:bytes                  (using=%d)\n", crcBytes);
    printf("    /LOSS:percent               (using=%f)\n", packetlossPercent);
    printf("    /LATENCY:milliseconds       (using=%d)\n", latency);
    printf("    /SLEEP:milliseconds         (using=%d)\n", sleepDelay);
    printf("    /CLOCKSYNC:milliseconds     (using=%d)\n", clockSyncDelay);
    printf("    /HOLDTIME:milliseconds      (using=%d)\n", holdTime);
    printf("    /KEEPALIVE:milliseconds     (using=%d)\n", keepAliveDelay);
    printf("    /TIMEOUT:milliseconds       (using=%d)\n", noDataTimeout);
    printf("    /ICMPIGNORE:milliseconds    (using=%d)\n", icmpIgnoreTime);
    printf("    /RUNTIME:seconds            (using=%d)\n", runTimeSeconds);
    printf("    /DUMP:seconds               (using=%d)\n", dumpSeconds);
    printf("    /CLUSTER:{0|1}              (using=%d)\n", g_connectToCluster);
    printf("    /LFN:{0|1}                  (using=%d)\n", lfn);
    printf("    /NOLISTEN:{0|1}             (using=%d)\n", g_noListen);
    printf("    /RELIABLE:{0|1}             (using=%d)\n", reliableSends);
    printf("    /COALESCE:{0|1}             (using=%d)\n", reliableCoalesce);
    printf("    /THREAD:{0|1}               (using=%d)\n", threading);
    printf("    /RECORD:filename            (using=%s)\n", recordFilename);
    printf("    /PLAYBACK:filename          (using=%s)\n", playbackFilename);
    printf("    /QUEUE:milliseconds         (using=%d)\n", queueEventTime);
    printf("    /TOLERANCE:count            (using=%d)\n", toleranceLossCount);
    printf("    /MINFLOW:bytes              (using=%d)\n", minimumFlowControlWindow);
    printf("\n");

        //////////////////////////////////////////////////
        // initialize everything
        //////////////////////////////////////////////////
    g_playerManager = new PlayerManager();

    UdpManager::Params params;
    params.handler = g_playerManager;
    params.crcBytes = crcBytes;
    params.hashTableSize = 5000;
    if (lfn)
    {
        params.incomingBufferSize = 32 * 1024 * 1024;
        params.outgoingBufferSize = 32 * 1024 * 1024;
        params.reliable[0].maxInstandingPackets = 30000;
        params.reliable[0].maxOutstandingPackets = 30000;
        params.reliable[0].maxOutstandingBytes = 32 * 1024 * 1024;
        params.reliable[0].congestionWindowMinimum = 100000;    // you said it was an lfn :)
    }
    else
    {
        params.incomingBufferSize = 8 * 1024 * 1024;
        params.outgoingBufferSize = 8 * 1024 * 1024;
        params.reliable[0].maxInstandingPackets = 1000;
        params.reliable[0].maxOutstandingPackets = 1000;
        params.reliable[0].maxOutstandingBytes = 1024 * 1024;
    }
    params.clockSyncDelay = clockSyncDelay;
    params.keepAliveDelay = keepAliveDelay;
    params.maxConnections = 1000;
    params.packetHistoryMax = 4;
    params.noDataTimeout = noDataTimeout;
    params.icmpErrorRetryPeriod = icmpIgnoreTime;
    params.port = listenPort;
    params.maxDataHoldTime = holdTime;
    params.maxRawPacketSize = rawPacketSize;
    params.pooledPacketMax = 100000;
    params.threadSleepTime = sleepDelay;
    params.eventQueuing = (queueEventTime > 0) | threading;
    params.incomingLogicalPacketMax = 1024 * 1024 * 1024;
    params.callbackEventPoolMax = 100000;            // set really large in case the library is configured to stall a long time between deliveries
#if 0
    params.userSuppliedEncryptExpansionBytes = 3;    // set to match whatever the callback-encrypt function does
    params.encryptMethod[0] = cEncryptMethodUserSupplied;
#else
    params.encryptMethod[0] = cEncryptMethodNone;
#endif
    params.pooledPacketInitial = 0;
    params.reliable[0].maxOutstandingBytes = params.outgoingBufferSize;
    params.reliable[0].outOfOrder = false;
    params.reliable[0].trickleRate = 0;
    params.reliable[0].trickleSize = 0;
    params.reliable[0].coalesce = reliableCoalesce;
    params.reliable[0].resendDelayAdjust = 1200;
    params.reliable[0].toleranceLossCount = toleranceLossCount;
    if (minimumFlowControlWindow != 0)
    {
        params.reliable[0].congestionWindowMinimum = minimumFlowControlWindow;
    }

    UdpDriver *udpDriver = NULL;
    if (playbackFilename[0] != 0)
    {
        params.udpDriver = udpDriver = new UdpDriverPlayback(playbackFilename);
    }
    else if (recordFilename[0] != 0)
    {
        params.udpDriver = udpDriver = new UdpDriverRecord(recordFilename);
    }

    g_udpManager = new UdpManager(&params);

    UdpSimulationParameters sim;
    sim.simulateIncomingLossPercent = 0;
    sim.simulateOutgoingLossPercent = packetlossPercent;
    sim.simulateOutgoingLatency = latency;
    sim.simulateIncomingLatency = latency;
    g_udpManager->SetSimulation(&sim);

    if (threading)
    {
        g_udpManager->ThreadStart();
    }

    char tbuf[256];
    UdpPlatformAddress test;
    test = UdpMisc::GetSelfAddress(false);
    printf("SelfAddress (routeable): %s\n", test.GetAddress(tbuf, sizeof(tbuf)));
    test = UdpMisc::GetSelfAddress(true);
    printf("SelfAddress (non-routeable): %s\n", test.GetAddress(tbuf, sizeof(tbuf)));

    fprintf(stderr, "Listening on port %d\n", g_udpManager->GetLocalPort());

        // establish connection to other running stress apps
    if (connectAddress[0] != 0)
    {
        EstablishConnections(connectAddress);
    }


        // master loop (processing incoming data/connections)
    UdpClockStamp lastSendTime = 0;
    UdpClockStamp deliverStart = 0;
    UdpManagerStatistics startStats;
    g_udpManager->GetStats(&startStats);
    UdpClockStamp clockStart = g_udpManager->Clock();
    UdpClockStamp statStart = g_udpManager->Clock();
    UdpClockStamp runTimeStart = g_udpManager->Clock();
    int sendIterations = 0;
    char *hold = new char[sendPacketMaxSize];
    memset(hold, cPacketData, sendPacketMaxSize);

    for (;;)
    {
#if defined(WIN32)
            // check for shutdown keys
        if (kbhit())
        {
            int k = getch();
            if ((k == 0 || k == 0xe0) && (getch() == 0x88))
                break;
            if (k == 32)
            {
                fprintf(stderr, "\nListening on port %d\n", g_udpManager->GetLocalPort());
                g_playerManager->DumpStats(stderr);
            }
            if (k == 'x' || k == 'X')
            {
                g_playerManager->DisconnectAll();
            }
        }
#endif

            // display stats
        int e = g_udpManager->ClockElapsed(clockStart);
        if (e >= 200)
        {
            clockStart = g_udpManager->Clock();
            UdpManagerStatistics endStats;
            g_udpManager->GetStats(&endStats);

            int iterations = (int)((endStats.iterations - startStats.iterations) * 1000 / e);
            int incoming = (int)((endStats.packetsReceived - startStats.packetsReceived) * 1000 / e);
            int inBytes = (int)((endStats.bytesReceived - startStats.bytesReceived) * 1000 / e);
            int outgoing = (int)((endStats.packetsSent - startStats.packetsSent) * 1000 / e);
            int outBytes = (int)((endStats.bytesSent - startStats.bytesSent) * 1000 / e);
            
            fprintf( stderr, "p=%d fps=%-3d in=%5d(%8d) out=%5d(%8d) q=%6d(%8d)\r", g_playerManager->GetPlayersCount(), iterations, incoming, inBytes, outgoing, outBytes, endStats.eventListCount, endStats.eventListBytes);

            sendIterations = 0;
            startStats = endStats;
        }

            // dump stats if needed
        if (dumpSeconds != 0 && g_udpManager->ClockElapsed(statStart) >= dumpSeconds * 1000)
        {
            statStart = g_udpManager->Clock();
            g_playerManager->DumpStats(stdout);
        }

            // send data (if needed)
        UdpClockStamp s = g_udpManager->Clock();
        if (sendInterval != 0 && UdpMisc::ClockDiff(lastSendTime, s) >= sendInterval)
        {
            lastSendTime = s;

            sendIterations++;
            for (int i = 0; i < sendPacketCount; i++)
            {
                int range = (sendPacketMaxSize - sendPacketMinSize + 1);
                int sendSize = sendPacketMinSize + (g_udpManager->Random() % range);
                if (sendSize >= 9)
                {
                    int r = (int)g_udpManager->Clock();
                    UdpMisc::PutValue32(hold + 1, r);
                    UdpMisc::PutValue32(hold + sendSize - 4, r);
                }

                g_playerManager->SendPacketToAll(reliableSends ? cUdpChannelReliable1 : cUdpChannelUnreliable, hold, sendSize);
            }
        }

        if (runTimeSeconds != 0)
        {
            if (g_udpManager->ClockElapsed(runTimeStart) > runTimeSeconds * 1000)
            {
                printf("Program runtime exceeded, terminating\n");
                break;
            }
        }

        if (g_noListen && g_playerManager->GetPlayersCount() == 0)
        {
            printf("No connections left and not listening, terminating\n");
            break;
        }

            // give managers time to do their job
        if (!threading)
        {
            g_udpManager->GiveTime();
        }
        g_playerManager->GiveTime();

            // deliver packets one per second to application (if in queuing mode)
        if (g_udpManager->GetEventQueuing() && g_udpManager->ClockElapsed(deliverStart) >= queueEventTime)
        {
            deliverStart = g_udpManager->Clock();
            g_udpManager->DeliverEvents(5000);
        }

        g_udpManager->Sleep(sleepDelay);
    }

    delete[] hold;

        //////////////////////////////////////////////////
        // terminate everything
        //////////////////////////////////////////////////
    g_udpManager->ThreadStop();   // if it's running, stop it
    delete g_playerManager;
    g_udpManager->Release();

    if (udpDriver != NULL)
    {
        delete udpDriver;
        udpDriver = NULL;
    }

    return(0);
}



    ////////////////////////////////////////
    // Player implementation
    ////////////////////////////////////////
Player::Player(UdpConnection *con, bool requestList)
{
    mRequestList = requestList;
    mConnection = con;
    mConnection->AddRef();
    mConnection->SetHandler(this);
}

Player::~Player()
{
    if (mConnection != NULL)
    {
        mConnection->SetHandler(NULL);
        mConnection->Disconnect();    // not really needed, the connection will disconnect automatically when the last reference is released
        mConnection->Release();
        mConnection = NULL;
    }
}

void Player::OnRoutePacket(UdpConnection * /*con*/, const udp_uchar *data, int dataLen)
{
    switch(data[0])
    {
        case cPacketRequestList:
        {
            g_playerManager->SendAllPlayers(this);
            break;
        }
        case cPacketAddress:
        {
            if (g_connectToCluster)
            {
                const char *address = (const char *)(data + 1);
                UdpConnection *con = g_udpManager->EstablishConnection(address, 0, 30000);
                g_playerManager->AddPlayer(new Player(con, false));
                con->Release();
            }
            break;
        }
        case cPacketData:
        {
            if (dataLen >= 9)
            {
                udp_uint seq = UdpMisc::GetValue32(data + 1);
                udp_uint seq2 = UdpMisc::GetValue32(data + dataLen - 4);
                if (seq != seq2)
                    printf("ERROR: End of packet does not match start\n");
            }
            break;
        }
        default:
        {
            printf("ERROR: Unknown data type received\n");
            break;
        }
    }
}

void Player::OnTerminated(UdpConnection * /*con*/)
{
    char hold[256];
    if (mConnection->GetDisconnectReason() == UdpConnection::cDisconnectReasonOtherSideTerminated)
    {
        printf("Disconnected: %s,%d  Reason=%s  (OtherSideReason=%s)\n", mConnection->GetDestinationIp().GetAddress(hold, sizeof(hold)), mConnection->GetDestinationPort(), UdpConnection::DisconnectReasonText(mConnection->GetDisconnectReason()), UdpConnection::DisconnectReasonText(mConnection->GetOtherSideDisconnectReason()));
    }
    else
    {
        printf("Disconnected: %s,%d  Reason=%s\n", mConnection->GetDestinationIp().GetAddress(hold, sizeof(hold)), mConnection->GetDestinationPort(), UdpConnection::DisconnectReasonText(mConnection->GetDisconnectReason()));
    }

    mConnection->SetHandler(NULL);
    mConnection->Release();
    mConnection = NULL;
}

void Player::OnConnectComplete(UdpConnection *con)
{
    char hold[256];
    printf("Connection established: %s,%d\n", con->GetDestinationIp().GetAddress(hold, sizeof(hold)), con->GetDestinationPort());

    if (mRequestList)
    {
        char request[256];
        request[0] = cPacketRequestList;
        con->Send(cUdpChannelReliable1, request, 1);
    }
}

void Player::OnCrcReject(UdpConnection *con, const udp_uchar *data, int dataLen)
{
    char hold[256];
    printf("CRC Reject: %s,%d (len=%d) ", con->GetDestinationIp().GetAddress(hold, sizeof(hold)), con->GetDestinationPort(), dataLen);
    for (int i = 0; i < dataLen; i++)
    {
        printf("%02x ", data[i]);
    }
    printf("\n");
}

void Player::OnPacketCorrupt(UdpConnection *con, const udp_uchar *data, int dataLen, UdpCorruptionReason reason)
{
    char hold[256];
    printf("Corrupt Packet(reason=%d): %s,%d (len=%d) ", reason, con->GetDestinationIp().GetAddress(hold, sizeof(hold)), con->GetDestinationPort(), dataLen);
    for (int i = 0; i < dataLen; i++)
    {
        printf("%02x ", data[i]);
    }
    printf("\n");
}

    ////////////////////////////////////////
    // PlayerManager implementation
    ////////////////////////////////////////
PlayerManager::PlayerManager() : mPlayers(&Player::mLink)
{
    mNextId = 1;
}

PlayerManager::~PlayerManager()
{
    Player *cur = mPlayers.RemoveHead();
    while (cur != NULL)
    {
        delete cur;
        cur = mPlayers.RemoveHead();
    }
}

int PlayerManager::OnUserSuppliedEncrypt(UdpConnection * /*con*/, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen)
{
    destData[0] = 0xff;
    memcpy(destData + 1, sourceData, sourceLen);
    destData[sourceLen + 1] = 0xff;
    destData[sourceLen + 2] = 0xff;
    return(sourceLen + 3);
}

int PlayerManager::OnUserSuppliedDecrypt(UdpConnection * /*con*/, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen)
{
    memcpy(destData, sourceData + 1, sourceLen - 3);
    return(sourceLen - 3);
}

void PlayerManager::OnConnectRequest(UdpConnection *con)
{
    if (g_noListen)
        return;

    char hold[256];
    printf("Incoming Connection: %s,%d\n", con->GetDestinationIp().GetAddress(hold, sizeof(hold)), con->GetDestinationPort());
    AddPlayer(new Player(con, false));
}

void PlayerManager::AddPlayer(/*gift*/ Player *player)
{
    player->mId = mNextId++;
    mPlayers.InsertTail(player);
}

void PlayerManager::GiveTime()
{
    Player *cur = mPlayers.First();
    while (cur != NULL)
    {
        Player *next = mPlayers.Next(cur);

        if (cur->mConnection == NULL)
        {
            mPlayers.Remove(cur);
            delete cur;
        }

        cur = next;
    }
}

void PlayerManager::SendPacketToAll(UdpChannel channel, void *data, int dataLen)
{
    Player *cur = mPlayers.First();
    while (cur != NULL)
    {
        if (cur->mConnection != NULL && cur->mConnection->GetStatus() == UdpConnection::cStatusConnected)
        {
            if (cur->mConnection->TotalPendingBytes() < 40000000)    // don't let any one connection backlog over 40mb worth of data
            {
                cur->mConnection->Send(channel, data, dataLen);
            }
        }

        cur = mPlayers.Next(cur);
    }
}

void PlayerManager::DumpStats(FILE *file)
{
    Player *cur = mPlayers.First();
    while (cur != NULL)
    {
        if (cur->mConnection != NULL)
        {
            UdpConnectionStatistics stats;
            cur->mConnection->GetStats(&stats);
            int pend = cur->mConnection->TotalPendingBytes();

            UdpConnection::ChannelStatus cs;
            cur->mConnection->GetChannelStatus(cUdpChannelReliable1, &cs);

            char hold[512];
#ifdef WIN32
            fprintf(file, "%s,%d  AckPing=%d  BytesPending=%d  CW,SS=%d,%d  Crc=%I64d\nResentTimeout=%I64d  ResentAccel=%I64d\nClockSync(average=%d high=%d low=%d master=%d age=%d)\n%I64d<<%I64d(in-loss=%.1f%%)  %I64d>>%I64d(out-loss=%.1f%%)\n"
#else
            fprintf(file, "%s,%d  AckPing=%d  BytesPending=%d  CW,SS=%d,%d  Crc=%lld\nResentTimeout=%lld  ResentAccel=%lld\nClockSync(average=%d high=%d low=%d master=%d age=%d)\n%lld<<%lld(in-loss=%.1f%%)  %lld>>%lld(out-loss=%.1f%%)\n"
#endif
                        , cur->mConnection->GetDestinationIp().GetAddress(hold, sizeof(hold)), cur->mConnection->GetDestinationPort()
                        , stats.reliableAveragePing, pend, cs.congestionWindowSize, cs.congestionSlowStartThreshhold
                        , stats.crcRejectedPackets, stats.resentPacketsTimedOut, stats.resentPacketsAccelerated
                        , stats.averagePingTime, stats.highPingTime, stats.lowPingTime, stats.masterPingTime, stats.masterPingAge
                        , stats.syncOurReceived, stats.syncTheirSent, (1.0f - stats.percentReceivedSuccess) * 100.0f
                        , stats.syncOurSent, stats.syncTheirReceived, (1.0f - stats.percentSentSuccess) * 100.0f
                        );
        }

        cur = mPlayers.Next(cur);
    }

    UdpManagerStatistics manStats;
    g_udpManager->GetStats(&manStats);
    fprintf(file, "PoolAvailable=%d  PoolCreated=%d  SendError=%d\n", manStats.poolAvailable, manStats.poolCreated, (int)manStats.socketOverflowErrors);
}

void PlayerManager::SendAllPlayers(Player *toPlayer)
{
    Player *cur = mPlayers.First();
    while (cur != NULL)
    {
            // we only send addresses of connections that were established before ours
            // to prevent the situations where two people connect at the same time, then
            // both of them request the list and they both have each other on the list, resulting
            // in them both trying to establish connections to each other at the same time.
        if (cur != toPlayer && cur->mConnection != NULL && cur->mId < toPlayer->mId)
        {
            char abuf[256];
            cur->mConnection->GetDestinationIp().GetAddress(abuf, sizeof(abuf));
            int aport = cur->mConnection->GetDestinationPort();

            char data[256];
            data[0] = cPacketAddress;
            sprintf(data + 1, "%s:%d", abuf, aport);

            toPlayer->mConnection->Send(cUdpChannelReliable1, data, (int)(strlen(data + 1) + 2));
        }

        cur = mPlayers.Next(cur);
    }
}

void PlayerManager::DisconnectAll()
{
    Player *cur = mPlayers.First();
    while (cur != NULL)
    {
        Player *next = mPlayers.Next(cur);

        if (cur->mConnection != NULL)
        {
            cur->mConnection->Disconnect();
        }

        cur = next;
    }
}







