#include "MinigameServer.h"
#include <iostream>
#include <Klient.h>
#include <Globals.h>

// Inhalt der LoginServer Klasse aus LoginServer.h
// Konstruktor 
MinigameServer::MinigameServer( InitDatei *zIni )
    : Thread()
{
    Network::Start( 100 );
    std::cout << "MS: Verbindung mit Datenbank wird hergestellt...\n";
    db = new MSDatenbank( zIni );
    klientAnzahl = 0;
    klients = new RCArray< MSKlient >();
    empfangen = 0;
    gesendet = 0;
    fehler = new Text();
    ini = zIni->getThis();
    id = *zIni->zWert( "ServerId" );
    server = new Server();
    aServer = new Server();
    std::cout << "MS: Starten des Admin Servers...\n";
    if( !aServer->verbinde( (unsigned short)TextZuInt( ini->zWert( "AdminServerPort" )->getText(), 10 ), 10 ) )
    {
        std::cout << "MS: Der Admin Server konnte nicht gestartet werden. Das Programm wird beendet.\n";
        exit( 1 );
    }
    db->setServerStatus( id, 2 );
    end = 0;
    nichtPausiert = 0;
    InitializeCriticalSection( &cs );
    ref = 1;
    if( zIni->zWert( "Aktiv" )->istGleich( "TRUE" ) )
    {
        serverStarten();
        serverFortsetzen();
    }
}

// Destruktor 
MinigameServer::~MinigameServer()
{
    fehler->release();
    server->trenne();
    server->release();
    aServer->trenne();
    aServer->release();
    if( klients )
        klients->release();
    ini->release();
    db->release();
    DeleteCriticalSection( &cs );
}

// nicht constant 
void MinigameServer::runn()
{
    while( !end )
    {
        SKlient *klient;
        klient = aServer->getKlient();
        if( end && klient )
        {
            klient->trenne();
            klient = klient->release();
            Sleep( 1000 );
            return;
        }
        if( !klient )
            return;
        MSAKlient *clHandle = new MSAKlient( klient, getThis() );
        clHandle->start();
    }
}

void MinigameServer::thread()
{
    while( 1 )
    {
        SKlient *klient;
        klient = server->getKlient();
        if( !klient )
            break;
        Framework::getThreadRegister()->cleanUpClosedThreads();
        MSKlient *clHandle = new MSKlient( klient, getThis() );
        EnterCriticalSection( &cs );
        klients->set( clHandle, klientAnzahl );
        klientAnzahl++;
        LeaveCriticalSection( &cs );
        clHandle->start();
    }
}

void MinigameServer::close()
{
    db->setServerStatus( id, 1 );
    server->trenne();
#ifdef WIN32
    warteAufThread( 1000 );
#endif
    EnterCriticalSection( &cs );
    for( int i = 0; i < klientAnzahl; i++ )
        klients->z( i )->absturz();
    klients = klients->release();
    klientAnzahl = 0;
    LeaveCriticalSection( &cs );
    ende();
    run = 0;
    end = 1;
    Klient *klient = new Klient();
    klient->verbinde( aServer->getPort(), "127.0.0.1" );
    Sleep( 500 );
    aServer->trenne();
    klient->release();
}

bool MinigameServer::serverStarten()
{
    if( nichtPausiert )
    {
        fehler->setText( "Der Server konnte nicht gestartet werden: Der Server läuft bereits." );
        return 0;
    }
    if( server )
        server->release();
    server = new Server();
    if( server->verbinde( (unsigned short)TextZuInt( ini->zWert( "ServerPort" )->getText(), 10 ), 10 ) )
    {
        nichtPausiert = 1;
        start();
        return 1;
    }
    else
    {
        serverBeenden();
        fehler->setText( "Der Server konnte nicht gestartet werden: Eventuell ist der Port in benutzung." );
        return 0;
    }
}

bool MinigameServer::serverPause()
{
    if( !nichtPausiert )
    {
        fehler->setText( "Der Server konnte nicht pausiert werden: Der Server läuft nicht." );
        return 0;
    }
    if( !db->setServerStatus( id, 2 ) )
    {
        fehler->setText( "Der Server konnte nicht pausiert werden: " );
        fehler->append( db->getLetzterFehler() );
        return 0;
    }
    return 1;
}

bool MinigameServer::serverFortsetzen()
{
    if( !nichtPausiert )
    {
        fehler->setText( "Der Server konnte nicht fortgesetzt werden: Der Server läuft nicht." );
        return 0;
    }
    if( !db->setServerStatus( id, 3 ) )
    {
        fehler->setText( "Der Server konnte nicht fortgesetzt werden: " );
        fehler->append( db->getLetzterFehler() );
        return 0;
    }
    return 1;
}

bool MinigameServer::serverBeenden()
{
    if( !nichtPausiert )
    {
        fehler->setText( "Der Server konnte nicht beendet werden: Der Server läuft nicht." );
        return 0;
    }
    if( db->serverIstNichtPausiert( id ) )
    {
        fehler->setText( "Der Server konnte nicht beendet werden: Der Server muss erst pausiert werden." );
        return 0;
    }
    nichtPausiert = 0;
    ende();
    if( server )
        server->trenne();
    return 1;
}

bool MinigameServer::setMaxKlients( int mc )
{
    if( !db->setMaxClients( id, mc ) )
    {
        fehler->setText( "Die maximale Anzahl der Clients konnte nicht gesetzt werden:\n" );
        fehler->append( db->getLetzterFehler() );
        return 0;
    }
    ini->setWert( "MaxClients", Text() += mc );
    return 1;
}

bool MinigameServer::absturzKlient( int klientId )
{
    bool gefunden = 0;
    EnterCriticalSection( &cs );
    for( int i = 0; i < klientAnzahl; i++ )
    {
        if( klients->z( i ) && klients->z( i )->getKlientNummer() == klientId )
        {
            klients->z( i )->absturz();
            klients->remove( i );
            klientAnzahl--;
            gefunden = 1;
            break;
        }
    }
    LeaveCriticalSection( &cs );
    return gefunden;
}

bool MinigameServer::removeKlient( MSKlient *zKlient )
{
    bool gefunden = 0;
    EnterCriticalSection( &cs );
    for( int i = 0; i < klientAnzahl; i++ )
    {
        if( klients->z( i ) == zKlient )
        {
            klients->remove( i );
            klientAnzahl--;
            gefunden = 1;
            break;
        }
    }
    LeaveCriticalSection( &cs );
    return gefunden;
}

void MinigameServer::addGesendet( int bytes )
{
    gesendet += bytes;
}

void MinigameServer::addEmpfangen( int bytes )
{
    empfangen += bytes;
}

// constant
bool MinigameServer::istAn() const
{
    return db->serverIstNichtPausiert( id );
}

Server *MinigameServer::zServer() const
{
    return server;
}

MSDatenbank *MinigameServer::zDB() const
{
    return db;
}

bool MinigameServer::hatClients() const
{
    return klientAnzahl > 0;
}

int MinigameServer::getId() const
{
    return id;
}

char *MinigameServer::getLetzterFehler() const
{
    return fehler->getText();
}

InitDatei *MinigameServer::zInit() const
{
    return ini;
}

// Reference Counting
MinigameServer *MinigameServer::getThis()
{
    ref++;
    return this;
}

MinigameServer *MinigameServer::release()
{
    ref--;
    if( !ref )
        delete this;
    return 0;
}


// Inhalt der LSAKlient Klasse aus LoginServer.h
// Konstruktor 
MSAKlient::MSAKlient( SKlient *klient, MinigameServer *ls )
    : Thread()
{
    this->klient = klient;
    unsigned char key[ 20 ] = { 168, 13, 57, 219, 1, 54, 176, 97, 163, 80, 63, 101, 137, 132, 45, 103, 204, 225, 53, 197 };
    klient->setSendeKey( (char*)key, 20 );
    klient->setEmpfangKey( (char*)key, 20 );
    name = new Text( "" );
    passwort = new Text( "" );
    adminId = 0;
    version = 0;
    this->ms = ls;
}

// Destruktor 
MSAKlient::~MSAKlient()
{
    klient->trenne();
    klient->release();
    ms->release();
    name->release();
    passwort->release();
}

// nicht constant 
void MSAKlient::thread()
{
    while( 1 )
    {
        char c = 0;
        if( !klient->getNachrichtEncrypted( &c, 1 ) )
            break;
        else
        {
            bool br = 0;
            switch( c )
            {
            case 1: // Login
                if( 1 )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    char nLen = 0;
                    klient->getNachrichtEncrypted( &nLen, 1 );
                    char *n = new char[ nLen + 1 ];
                    n[ (int)nLen ] = 0;
                    if( nLen )
                        klient->getNachrichtEncrypted( n, nLen );
                    char pLen = 0;
                    klient->getNachrichtEncrypted( &pLen, 1 );
                    char *p = new char[ pLen + 1 ];
                    p[ (int)pLen ] = 0;
                    if( pLen )
                        klient->getNachrichtEncrypted( p, pLen );
                    int adminId = ms->zDB()->istAdministrator( n, p );
                    if( adminId )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        name->setText( n );
                        passwort->setText( p );
                        this->adminId = adminId;
                    }
                    else
                        errorZuKlient( "Falsche Kombination aus Name und Passwort." );
                    delete[] n;
                    delete[] p;
                }
                break;
            case 2: // Logout
                adminId = 0;
                name->setText( "" );
                passwort->setText( "" );
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 3: // Trennen
                br = 1;
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 4: // Server starten
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSStarten ) )
                    {
                        if( !ms->serverStarten() )
                        {
                            Text *err = new Text();
                            err->append( ms->getLetzterFehler() );
                            errorZuKlient( err->getText() );
                            err->release();
                        }
                        else
                            klient->sendeEncrypted( "\1", 1 );
                    }
                    else
                        errorZuKlient( "Du bist nicht berechtigt den Server zu starten." );
                }
                break;
            case 5: // Server beenden
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSBeenden ) )
                    {
                        if( ms->serverBeenden() )
                            klient->sendeEncrypted( "\1", 1 );
                        else
                        {
                            Text *err = new Text();
                            err->append( ms->getLetzterFehler() );
                            errorZuKlient( err->getText() );
                            err->release();
                        }
                    }
                    else
                        errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
                }
                break;
            case 6: // Programm Schließen
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    bool ok = 0;
                    if( ms->isRunning() )
                    {
                        if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSBeenden ) )
                        {
                            if( ms->serverBeenden() )
                                ok = 1;
                            else
                            {
                                Text *err = new Text();
                                err->append( ms->getLetzterFehler() );
                                errorZuKlient( err->getText() );
                                err->release();
                            }
                        }
                        else
                            errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
                    }
                    else
                        ok = 1;
                    if( ok && ms->hatClients() )
                    {
                        errorZuKlient( "Es sind noch Klients Online. Bitte versuche es später erneut." );
                        break;
                    }
                    if( ok )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        std::cout << "MS: Der Server wird von Benutzer " << adminId << " heruntergefahren.\n";
                        ms->close();
                        br = 1;
                    }
                }
                break;
            case 7: // Progtamm abstürzen
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    bool ok = 0;
                    if( ms->isRunning() )
                    {
                        if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSBeenden ) )
                        {
                            ms->serverBeenden();
                            ok = 1;
                        }
                        else
                            errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
                    }
                    else
                        ok = 1;
                    if( ok )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        std::cout << "MS: Der Server wurde von Benutzer " << adminId << " terminiert.\n";
                        ms->close();
                        br = 1;
                    }
                }
                break;
            case 8: // Status Frage
                if( 1 )
                {
                    char status = 0;
                    if( ms->isRunning() )
                    {
                        status = 1;
                        if( ms->istAn() )
                            status = 2;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                    klient->sendeEncrypted( &status, 1 );
                }
                break;
            case 9: // Server pausieren
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    klient->sendeEncrypted( "\1", 1 );
                    char pause = 0;
                    klient->getNachrichtEncrypted( &pause, 1 );
                    if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSPausieren ) )
                    {
                        bool ok = 0;
                        if( pause )
                            ok = ms->serverPause();
                        else
                            ok = ms->serverFortsetzen();
                        if( ok )
                            klient->sendeEncrypted( "\1", 1 );
                        else
                        {
                            Text *err = new Text();
                            err->append( ms->getLetzterFehler() );
                            errorZuKlient( err->getText() );
                            err->release();
                        }
                    }
                    else
                    {
                        if( pause )
                            errorZuKlient( "Du bist nicht berechtigt den Server zu pausieren." );
                        else
                            errorZuKlient( "Du bist nicht berechtigt den Server fortzusetzen." );
                    }
                }
                break;
            case 0xA: // maximale Anzahl der Clients setzen
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int maxC = 0;
                    klient->getNachrichtEncrypted( (char*)&maxC, 4 );
                    if( ms->zDB()->adminHatRecht( adminId, Admin_Recht::MSMCChange ) )
                    {
                        if( ms->setMaxKlients( maxC ) )
                            klient->sendeEncrypted( "\1", 1 );
                        else
                        {
                            Text *err = new Text();
                            err->append( ms->getLetzterFehler() );
                            errorZuKlient( err->getText() );
                            err->release();
                        }
                    }
                    else
                        errorZuKlient( "Du bist nicht berechtigt die maximale Anzahl der Clients zu verändern." );
                }
                break;
            case 0xC: // klient absturtz
                if( 1 )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int klientId = 0;
                    klient->getNachrichtEncrypted( (char*)&klientId, 4 );
                    if( klientId && ms->absturzKlient( klientId ) )
                        klient->sendeEncrypted( "\1", 1 );
                    else
                        klient->sendeEncrypted( "\0", 1 );
                }
                break;
            default:
                errorZuKlient( "Unbekannte Nachricht!" );
                break;
            }
            if( br )
                break;
            ms->addEmpfangen( klient->getDownloadBytes( 1 ) );
            ms->addGesendet( klient->getUploadBytes( 1 ) );
        }
    }
    ms->addEmpfangen( klient->getDownloadBytes( 1 ) );
    ms->addGesendet( klient->getUploadBytes( 1 ) );
    delete this;
}

void MSAKlient::errorZuKlient( const char *nachricht ) const // sendet eine Fehlernachricht zum Klient
{
    klient->sendeEncrypted( "\3", 1 );
    char len = (char)textLength( nachricht );
    klient->sendeEncrypted( &len, 1 );
    klient->sendeEncrypted( nachricht, len );
}


// Inhalt der LSKlient aus LoginServer.h
// Konstruktor
MSKlient::MSKlient( SKlient *klient, MinigameServer *ls )
    : Thread()
{
    this->klient = klient;
    unsigned char key[ 20 ] = { 88, 103, 192, 232, 69, 54, 57, 3, 239, 138, 234, 172, 126, 72, 81, 55, 205, 97, 59, 255 };
    klient->setSendeKey( (char*)key, 20 );
    klient->setEmpfangKey( (char*)key, 20 );
    klientNummer = 0;
    this->ms = ls;
    ref = 1;
}

// Destruktor 
MSKlient::~MSKlient()
{
    klient->release();
    ms->release();
}

// nicht constant 
void MSKlient::absturz()
{
    ende();
    klient->trenne();
    ms->zDB()->unregisterKlient( klientNummer, ms->getId() );
}

void MSKlient::thread()
{
    while( 1 )
    {
        char c = 0;
        if( !klient->getNachrichtEncrypted( &c, 1 ) )
            break;
        else
        {
            bool br = 0;
            switch( c )
            {
            case 1: // Klient identifikation
                klient->getNachrichtEncrypted( (char*)&klientNummer, 4 );
                if( !ms->zDB()->proveKlient( klientNummer, ms->getId() ) )
                {
                    klientNummer = 0;
                    errorZuKlient( "Du bist nicht für diesen Server eingetragen" );
                }
                else
                {
                    Text *key = ms->zDB()->getKlientKey( klientNummer );
                    if( !key )
                        errorZuKlient( "Es konnte kein Schlüssel ermittelt werden." );
                    else
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        klient->setEmpfangKey( *key, key->getLength() );
                        klient->setSendeKey( *key, key->getLength() );
                        key->release();
                    }
                }
                break;
            case 2: // Main / Erhaltung Server message
                if( 1 )
                {
                    char befehl = 0;
                    klient->getNachrichtEncrypted( &befehl, 1 );
                    switch( befehl )
                    {
                    case 2: // klient absturtz
                        if( 1 )
                        {
                            int klientId = 0;
                            klient->getNachrichtEncrypted( (char*)&klientId, 4 );
                            if( klientId && ms->absturzKlient( klientId ) )
                                klient->sendeEncrypted( "\1", 1 );
                            else
                                klient->sendeEncrypted( "\0", 1 );
                        }
                        break;
                    default:
                        errorZuKlient( "Befehl nicht bekannt!" );
                        break;
                    }
                }
                break;
            case 3: // Verbindungsende
                br = 1;
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 4: // unregister Klient
                if( !klientNummer )
                {
                    errorZuKlient( "Du bist nicht Identifiziert." );
                    break;
                }
                ms->zDB()->unregisterKlient( klientNummer, ms->getId() );
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 0x5: // ping
                if( !klientNummer )
                {
                    errorZuKlient( "Du bist nicht Identifiziert." );
                    break;
                }
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 0x6: // Get Minigame Option List
            {
                klient->sendeEncrypted( "\1", 1 );
                char l = 0;
                klient->getNachrichtEncrypted( &l, 1 );
                char *mName = new char[ l ];
                mName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( mName, l );
                RCArray< Text > oList;
                int anz = ms->zDB()->getMinigameOptionList( mName, &oList );
                delete[] mName;
                klient->sendeEncrypted( (char*)&anz, 4 );
                for( auto t = oList.getArray(); t.set && t.var; t++ )
                {
                    l = (char)t.var->getLength();
                    klient->sendeEncrypted( &l, 1 );
                    klient->sendeEncrypted( t.var->getText(), l );
                }
                break;
            }
            case 0x7: // Get Minigame Bestscore List
            {
                klient->sendeEncrypted( "\1", 1 );
                char l = 0;
                klient->getNachrichtEncrypted( &l, 1 );
                char *mName = new char[ l ];
                mName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( mName, l );
                Array< int > sList;
                RCArray< Text > pList;
                RCArray< Text > oList;
                int anz = ms->zDB()->getMinigameBestscore( mName, &sList, &pList, &oList );
                delete[] mName;
                klient->sendeEncrypted( (char*)&anz, 4 );
                for( int i = 0; i < anz; i++ )
                {
                    int s = sList.get( i );
                    klient->sendeEncrypted( (char*)&s, 4 );
                    l = (char)pList.z( i )->getLength();
                    klient->sendeEncrypted( &l, 1 );
                    klient->sendeEncrypted( pList.z( i )->getText(), l );
                    l = (char)oList.z( i )->getLength();
                    klient->sendeEncrypted( &l, 1 );
                    klient->sendeEncrypted( oList.z( i )->getText(), l );
                }
                break;
            }
            case 0x8: // Get Minigame Option Bestscore
            {
                klient->sendeEncrypted( "\1", 1 );
                char l = 0;
                klient->getNachrichtEncrypted( &l, 1 );
                char *mName = new char[ l ];
                mName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( mName, l );
                klient->getNachrichtEncrypted( &l, 1 );
                char *oName = new char[ l ];
                oName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( oName, l );
                Text player;
                int score = ms->zDB()->getMinigameBestscore( oName, mName, &player );
                delete[] mName;
                delete[] oName;
                klient->sendeEncrypted( (char*)&score, 4 );
                if( score )
                {
                    l = (char)player.getLength();
                    klient->sendeEncrypted( &l, 1 );
                    klient->sendeEncrypted( player.getText(), l );
                }
                break;
            }
            case 0x9: // Report End of Game
            {
                if( !klientNummer )
                {
                    errorZuKlient( "Du bist nicht Identifiziert." );
                    break;
                }
                klient->sendeEncrypted( "\1", 1 );
                char l = 0;
                klient->getNachrichtEncrypted( &l, 1 );
                char *mName = new char[ l ];
                mName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( mName, l );
                klient->getNachrichtEncrypted( &l, 1 );
                char *oName = new char[ l ];
                oName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( oName, l );
                int score;
                klient->getNachrichtEncrypted( (char*)&score, 4 );
                Text fName;
                if( ms->zDB()->updateMinigameScore( score, oName, klientNummer, mName, ms->getId(), &fName ) )
                {
                    ms->zDB()->lock();
                    Text path = ms->zInit()->zWert( "MGC_Pfad" )->getText();
                    if( path.getText()[ path.getLength() - 1 ] != '/' )
                        path += "/";
                    path += (char*)fName;
                    Datei d;
                    d.setDatei( path );
                    d.erstellen();
                    if( d.open( Datei::Style::schreiben ) )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        int size;
                        klient->getNachrichtEncrypted( (char*)&size, 4 );
                        char *buffer = new char[ 2048 ];
                        while( size > 0 )
                        {
                            int l = size > 2048 ? 2048 : size;
                            klient->getNachrichtEncrypted( buffer, l );
                            d.schreibe( buffer, l );
                            size -= l;
                        }
                        delete[] buffer;
                        d.close();
                    }
                    else
                        errorZuKlient( "Fehler beim speichern des Spielverlaufs" );
                    ms->zDB()->unlock();
                }
                else
                    klient->sendeEncrypted( "\0", 1 );
                delete[] mName;
                delete[] oName;
                break;
            }
            case 0xA: // Get Minigame Capture
            {
                klient->sendeEncrypted( "\1", 1 );
                char l = 0;
                klient->getNachrichtEncrypted( &l, 1 );
                char *mName = new char[ l ];
                mName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( mName, l );
                klient->getNachrichtEncrypted( &l, 1 );
                char *oName = new char[ l ];
                oName[ (int)l ] = 0;
                klient->getNachrichtEncrypted( oName, l );
                int serverId = ms->zDB()->getMinigameServer( mName, oName );
                if( serverId && serverId == ms->getId() )
                {
                    ms->zDB()->lock();
                    Text *fileName = ms->zDB()->getMinigameCaptureFileName( oName, mName );
                    Text path = ms->zInit()->zWert( "MGC_Pfad" )->getText();
                    if( path.getText()[ path.getLength() - 1 ] != '/' )
                        path += "/";
                    path += fileName->getText();
                    fileName->release();
                    Datei d;
                    d.setDatei( path );
                    if( d.open( Datei::Style::lesen ) )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        int size = (int)d.getSize();
                        klient->sendeEncrypted( (char*)&size, 4 );
                        char *buffer = new char[ 2048 ];
                        while( size > 0 )
                        {
                            int l = size > 2048 ? 2048 : size;
                            d.lese( buffer, l );
                            klient->sendeEncrypted( buffer, l );
                            size -= l;
                        }
                        delete[] buffer;
                        d.close();
                    }
                    errorZuKlient( "Aufzeichnung konnte nicht gelesen werden." );
                    ms->zDB()->unlock();
                }
                else if( serverId )
                {
                    Text ip;
                    Text port;
                    if( ms->zDB()->getMinigameServer( mName, oName, &ip, &port ) )
                    {
                        Klient *k = new Klient();
                        unsigned char key[ 20 ] = { 88, 103, 192, 232, 69, 54, 57, 3, 239, 138, 234, 172, 126, 72, 81, 55, 205, 97, 59, 255 };
                        k->setSendeKey( (char*)key, 20 );
                        k->setEmpfangKey( (char*)key, 20 );
                        if( k->verbinde( (unsigned short)(int)port, (char*)ip ) )
                        {
                            k->sendeEncrypted( "\xA", 1 );
                            char ret;
                            k->getNachrichtEncrypted( &ret, 1 );
                            if( ret == 1 )
                            {
                                l = (char)textLength( mName );
                                k->sendeEncrypted( &l, 1 );
                                k->sendeEncrypted( mName, l );
                                l = (char)textLength( oName );
                                k->sendeEncrypted( &l, 1 );
                                k->sendeEncrypted( oName, l );
                                k->getNachrichtEncrypted( &ret, 1 );
                                if( ret == 1 )
                                {
                                    klient->sendeEncrypted( "\1", 1 );
                                    int size;
                                    k->getNachrichtEncrypted( (char*)&size, 4 );
                                    klient->sendeEncrypted( (char*)&size, 4 );
                                    char *buffer = new char[ 2048 ];
                                    while( size > 0 )
                                    {
                                        int l = size > 2048 ? 2048 : size;
                                        k->getNachrichtEncrypted( buffer, l );
                                        klient->sendeEncrypted( buffer, l );
                                        size -= l;
                                    }
                                    delete[] buffer;
                                }
                            }
                            if( ret == 3 )
                            {
                                k->getNachrichtEncrypted( &l, 1 );
                                char *error = new char[ l + 1 ];
                                error[ (int)l ] = 0;
                                k->getNachrichtEncrypted( error, l );
                                errorZuKlient( error );
                                delete[] error;
                            }
                            else if( ret != 1 )
                                errorZuKlient( "Umbekannter Fehler während der Kommunikation mit dem Aufzeichnungs Server." );
                            k->sendeEncrypted( "\3", 1 );
                            k->getNachrichtEncrypted( &ret, 1 );
                            k->trenne();
                            k->release();
                        }
                        else
                            errorZuKlient( "Aufzeichnungs Server konnte nicht kontaktiert werden." );
                    }
                    else
                        errorZuKlient( "Aufzeichnungs Server wurde nicht gefunden." );
                }
                else
                    errorZuKlient( "Aufzeichnung wurde nicht gefunden." );
                delete[] mName;
                delete[] oName;
                break;
            }
            default:
                errorZuKlient( "Unbekannte Nachricht!" );
                break;
            }
            if( br )
                break;
            ms->addEmpfangen( klient->getDownloadBytes( 1 ) );
            ms->addGesendet( klient->getUploadBytes( 1 ) );
        }
    }
    ms->addEmpfangen( klient->getDownloadBytes( 1 ) );
    ms->addGesendet( klient->getUploadBytes( 1 ) );
    ms->removeKlient( this ); // delete this
}

// constant
void MSKlient::errorZuKlient( const char *nachricht ) const // sendet eine Fehlernachricht zum Klient
{
    klient->sendeEncrypted( "\3", 1 );
    char len = (char)textLength( nachricht );
    klient->sendeEncrypted( &len, 1 );
    klient->sendeEncrypted( nachricht, len );
}

int MSKlient::getKlientNummer() const // gibt die KlientId zurück
{
    return klientNummer;
}

// Reference Counting
MSKlient *MSKlient::getThis()
{
    ref++;
    return this;
}

MSKlient *MSKlient::release()
{
    ref--;
    if( !ref )
        delete this;
    return 0;
}