Konsolenausgabe beschleunigen?



  • Hallo,

    ich übe im Moment verschiedene Spielereien in der Konsole, musste aber feststellen, das die Ausgabe selbst etwas langsam abläuft, ca 6 Frames in der Sekunde.

    Gibt es solche Möglichkeiten? Bin beim ersten Suchen über ein Screenbuffering der Konsole gestolpert, ohne mich wirklich damit zu beschäftigen.

    Ich würde eigentlich vorher gerne wissen, ob solche Sachen wie eine Beschleunigung überhaupt möglich sind?



  • Also ein paar hundert bzw. vermutlich sogar ein paar tausend Frames/Sekunde solltet du in der Konsole schon hinbekommen. Sogar ohne spezielle "Optimierungen" wie direkt über die WinAPI Konsolen-Funktionen zuzugreifen. Also ganz normal mit puts/printf/cout .

    Ist natürlich von der Hardware und diversen Einstellungen abhängig.
    Aber auf nem halbwegs aktuellen PC mit halbwegs normalen Einstellungen (=normal grosser Konsolenfont etc.) - siehe oben.

    => Vermutlich machst du etwas anderes falsch, und das ist der Grund warum du bloss 6 FPS schaffst.



  • Hmm ... ich hab das gemessen, und bei 10 Durchgängen die Konsole komplett je mit einem Zeichen zu füllen, hat writeConsole() ca 1,6 Sekunden benötigt.

    Oder meine dortige Funktion ist verkehrt, was ich nicht abweisen möchte. Für eventuelle Hilfen wäre ich dann dankbar.

    void output_writeConsole(HANDLE set, std::vector <int> &palette, char output)
    {
        int p_number = palette.size();
        DWORD ch_number = 1;
    
        for (size_t y=0; y<Height; y++)
        {
            for (int x=0; x<int(Width/p_number)*p_number; x+=p_number)
            {
                for (int i=0; i<p_number; i++)
                {
                    point(set, x+i, y, palette[i]);
                    WriteConsole(set, &output, 1, &ch_number, NULL);
                }
            }
        }
    }
    

    Und in anderen Funktionen kann ich dem Bildaufbau direkt zusehen.

    void showColors_1_cycle_(HANDLE set, std::vector <Char> &palette)
    {
        int plasma[Width][Height], buffer[Width][Height];
        int cha_number = palette.size();
    
        //set colors
        for (size_t x=0; x<Width; x++)
        {
            for (size_t y=0; y<Height; y++)
            {
                plasma[x][y] = x;
            }
        }
        //cycle
        while(!kbhit())
        {
            for (size_t x=0; x<Width; x++)
            {
                for (size_t y=0; y<Height; y++)
                {
                    buffer[x][y] = (plasma[x][y] + 1) % cha_number;
                    setChar_(set, x, y, palette[buffer[x][y]].col, palette[buffer[x][y]].form);
                    plasma[x][y] = buffer[x][y];
                }
            }
        }
    }
    


  • Achaje, ich habe jetzt nochmal ohne die Ausgabe eines Char gemessen und komme bei 10 Durchgängen noch auf gut 1 Sekunde.

    Dann sind ja beinahe schon die Vorgänge in der Funktion langsamer? Als die Ausgabe selbst?



  • Wenn du ein komplettes, kompilierbares Beispiel postest das den Effekt reproduziert, findet sich vermutlich jemand der es sich ansieht.
    So kann man nur raten.



  • Kann ich gerne machen. Am besten den Code, mit dem ich die Zeiten gemessen habe, denke ich mal? Der ist relativ kurz. Bei den anderen Funktionen müsste ich erst ein passendes, kurzes Beispiel zusammenschneiden, aber irgendwie zweifele ich im Moment an mir, das ich erst mal lieber Pause mache. Sorry.

    #include <iostream>
    #include <cmath>
    #include <ctime>
    #include <vector>
    #include <conio.h>
    #include <cstdio>
    #include <windows.h>
    
    const unsigned int dGrey = 176, mGrey = 177, lGrey = 178, lBlock = 254, gBlock = 219;
    const unsigned int Width = 80, Height = 24;
    
    void point(HANDLE set, int x, int y, int col)
    {
        SetConsoleTextAttribute(set, col);
        COORD pos;
        pos.X = x;
        pos.Y = y;
        SetConsoleCursorPosition(set, pos);
    }
    
    void output_cout(HANDLE set, std::vector <int> &palette)
    {
        int p_number = palette.size();
    
        for (size_t y=0; y<Height; y++)
        {
            for (int x=0; x<int(Width/p_number)*p_number; x+=p_number)
            {
                for (int i=0; i<p_number; i++)
                {
                    point(set, x+i, y, palette[i]);
                    std::cout << char(lBlock);
                }
            }
        }
    }
    
    void output_writeConsole(HANDLE set, std::vector <int> &palette, char output)
    {
        int p_number = palette.size();
        DWORD ch_number = 1;
    
        for (size_t y=0; y<Height; y++)
        {
            for (int x=0; x<int(Width/p_number)*p_number; x+=p_number)
            {
                for (int i=0; i<p_number; i++)
                {
                    point(set, x+i, y, palette[i]);
                    WriteConsole(set, &output, 1, &ch_number, NULL);
                }
            }
        }
    }
    
    void output_putchar(HANDLE set, std::vector <int> &palette)
    {
        int p_number = palette.size();
    
        for (size_t y=0; y<Height; y++)
        {
            for (int x=0; x<int(Width/p_number)*p_number; x+=p_number)
            {
                for (int i=0; i<p_number; i++)
                {
                    point(set, x+i, y, palette[i]);
                    putchar(lBlock);
                }
            }
        }
    }
    
    void output_putch(HANDLE set, std::vector <int> &palette)
    {
        int p_number = palette.size();
    
        for (size_t y=0; y<Height; y++)
        {
            for (int x=0; x<int(Width/p_number)*p_number; x+=p_number)
            {
                for (int i=0; i<p_number; i++)
                {
                    point(set, x+i, y, palette[i]);
                    putch(35);
                }
            }
        }
    }
    
    int main()
    {
    
        HANDLE set = GetStdHandle(STD_OUTPUT_HANDLE);
    
        int cycles = 10;
        std::vector <int> palette = {4,6,14,10,2,11,3,9,1,5,13,12};
    
        LONGLONG frequency, curentCount, lastCount;  //Variablen für Zeitmessung
        QueryPerformanceFrequency((LARGE_INTEGER*) & frequency);  //Performance Counter holen
        double time_sec;  //Zeit in Sekunden
    
        /*std::cout*/
        QueryPerformanceCounter((LARGE_INTEGER*) & curentCount);  //1. Messung
        for (int i=0; i<cycles; i++)
        {
            output_cout(set, palette);
        }
    
        QueryPerformanceCounter((LARGE_INTEGER*) & lastCount);  //2. Messung
        time_sec = (((double)(lastCount - curentCount)) / ((double)frequency));  //Zeit in Sekunden ist Differenz von Messung 2 und Messung 1
        double time_cout =  time_sec;
    
        /*writeConsole()*/
        QueryPerformanceCounter((LARGE_INTEGER*) & curentCount);  //1. Messung
        for (int i=0; i<cycles; i++)
        {
            output_writeConsole(set, palette, lBlock);
        }
    
        QueryPerformanceCounter((LARGE_INTEGER*) & lastCount);  //2. Messung
        time_sec = (((double)(lastCount - curentCount)) / ((double)frequency));  //Zeit in Sekunden ist Differenz von Messung 2 und Messung 1
        double time_writeC =  time_sec;
    
        /*putchar()*/
        QueryPerformanceCounter((LARGE_INTEGER*) & curentCount);  //1. Messung
        for (int i=0; i<cycles; i++)
        {
            output_putchar(set, palette);
        }
    
        QueryPerformanceCounter((LARGE_INTEGER*) & lastCount);  //2. Messung
        time_sec = (((double)(lastCount - curentCount)) / ((double)frequency));  //Zeit in Sekunden ist Differenz von Messung 2 und Messung 1
        double time_putchar =  time_sec;
    
        /*putch()*/
        QueryPerformanceCounter((LARGE_INTEGER*) & curentCount);  //1. Messung
        for (int i=0; i<cycles; i++)
        {
            output_putch(set, palette);
        }
    
        QueryPerformanceCounter((LARGE_INTEGER*) & lastCount);  //2. Messung
        time_sec = (((double)(lastCount - curentCount)) / ((double)frequency));  //Zeit in Sekunden ist Differenz von Messung 2 und Messung 1
        double time_putch =  time_sec;
    
        SetConsoleTextAttribute(set, 15);
        std::cout << std::endl << std::endl;
        std::cout << "Cycles: " << cycles << std::endl << std::endl;
        std::cout << "Time in seconds       std::cout: " << time_cout << std::endl;
        std::cout << "Time in seconds  writeConsole(): " << time_writeC << std::endl;
        std::cout << "Time in seconds       putchar(): " << time_putchar << std::endl;
        std::cout << "Time in seconds         putch(): " << time_putch << std::endl;
    
        std::cin.sync();
        std::cin.get();
    
    }
    


  • Ich habe jetzt ein besseres Beispiel:

    Betreffend ist Funktion beispiel_. Wenn dort die markierte Ausgabe-Funktion auskommentiert wird, läuft der dortige Zähler sehr deutlich schneller.

    #include <iostream>
    #include <vector>
    #include <windows.h>
    #include <cmath>
    #include <ctime>
    #include <conio.h>
    
    const unsigned int Block = 219, lGrey = 176, mGrey = 177, dGrey = 178, lBlock = 254;
    const unsigned int Width = 80, Height = 24;
    
    struct Char
    {
        int col;
        int form;
    };
    
    void point(HANDLE set, int x, int y, int col)
    {
        SetConsoleTextAttribute(set, col);
        COORD pos;
        pos.X = x;
        pos.Y = y;
        SetConsoleCursorPosition(set, pos);
    }
    
    void setChar_(HANDLE set, int x, int y, int col, int cha)
    {
        COORD pos;
        pos.X = x;
        pos.Y = y;
        SetConsoleCursorPosition(set, pos);
        SetConsoleTextAttribute(set, col);
        DWORD char_number = 1;
        char output = char(cha);
        WriteConsole(set, &output, 1, &char_number, NULL);
    }
    
    void makePalette(std::vector <Char> &palette, std::vector <int> &char_form, std::vector <int> &char_col)
    {
        palette.clear();
        char_form = {lGrey, mGrey, dGrey, Block, dGrey, mGrey, lGrey};
        char_col = {4,6,14,10,2,11,3,9,1,5,13,12};
    
        Char ch;
    
        for (size_t j=0; j<char_col.size(); j++)
        {
            for (size_t i=0; i<char_form.size(); i++)
            {
                ch.form = char_form.at(i);
                ch.col = char_col.at(j);
                palette.push_back(ch);
            }
        }
    }
    
    //Beispielfunktion
    void beispiel_(HANDLE set, std::vector <Char> &palette)
    {
        int counter = 0;
        int plasma [Width][Height], buffer[Width][Height];
        int cha_number = palette.size();
        int color;
        /*generate colors*/
        for(size_t x=0; x<Width; x++)
        {
            for(size_t y=0; y<Height; y++)
            {
                color = int
                        (
                        cha_number/2 + (cha_number/2 * sin(x / 10.0))
                        + cha_number/2 + (cha_number/2 * sin(y / 16.0))
                        + cha_number/2 + (cha_number/2 * sin(sqrt(double((x - Width / 2.0)
                        * (x - Width / 2.0) + (y - Height / 2.0) * (y - Height / 2.0))) / 6.0))
                        + cha_number/2 + (cha_number/2 * sin(sqrt(double(x * x + y * y)) / 6.0))
                        ) / 4;
                plasma[x][y] = color;
            }
        }
        /*set colors*/
        int paletteShift;
        while(!kbhit())
        {
            paletteShift = int(clock()/100);
            for(size_t x=0; x<Width; x++)
            {
                for(size_t y=0; y<Height; y++)
                {
                    buffer[x][y] = (plasma[x][y] + paletteShift) % cha_number;
                    //hier untere Funktion auskommentieren, um Unterschied zu erkennen
                    setChar_(set, x, y, palette[buffer[x][y]].col, palette[buffer[x][y]].form);
                }
            }
            point(set, 0, 24, 15);
            std::cout << "Frames: " << counter;
            counter ++;
        }
    }
    
    int main()
    {
        HANDLE set = GetStdHandle(STD_OUTPUT_HANDLE);
    
        std::vector <int> char_form;
        std::vector <int> char_col;
        std::vector <Char> palette;
        makePalette(palette, char_form, char_col);
    
        beispiel_(set, palette); //Beispielfunktion fürs Forum
    
        SetConsoleTextAttribute(set, 15);
        std::cin.sync();
        std::cin.get();
    }
    


  • Ich bin schon ganz gespannt, wie der Schwätzer von oben mit puts usw. mehrere 100 FPS hinbekommen will. Dein zweites Beispiel habe ich mir aber vorab trotzdem mal angesehen. Ich habe hier erst einmal die Datentypen angepasst. So konnte ich das Original nämlich nicht übersetzen, und da ich nicht so viele Casts streuen wollte...

    Dann fällt auf, dass Du für jedes Zeichen gleich 3 API Calls ausführst. Wenn Du das auf einen Call reduzierst, bist Du schon mal 'ne ganze Ecke schneller unterwegs:

    #include <iostream>
    #include <vector>
    #include <windows.h>
    #include <cmath>
    #include <ctime>
    #include <conio.h>
    
    const WCHAR Block = 219, lGrey = 176, mGrey = 177, dGrey = 178, lBlock = 254;
    const unsigned int Width = 80, Height = 24;
    
    void point(HANDLE set, SHORT x, SHORT y, int col)
    {
        SetConsoleTextAttribute(set, (WORD)col);
        COORD pos;
        pos.X = x;
        pos.Y = y;
        SetConsoleCursorPosition(set, pos);
    }
    
    void setChar_(HANDLE set, SHORT x, SHORT y, CHAR_INFO cha)
    {
        static CHAR_INFO outBuffer[Width * Height];
        SMALL_RECT rc = { x, y, x, y };
        outBuffer[(Width * y) + x] = cha;
        WriteConsoleOutput(set, outBuffer, COORD{ Width, Height }, COORD{ x, y }, &rc);
    }
    
    void makePalette(std::vector <CHAR_INFO> &palette, std::vector <WCHAR> &char_form, std::vector <WORD> &char_col)
    {
        palette.clear();
        char_form = { lGrey, mGrey, dGrey, Block, dGrey, mGrey, lGrey };
        char_col = { 4,6,14,10,2,11,3,9,1,5,13,12 };
    
        CHAR_INFO ch;
    
        for(size_t j = 0; j<char_col.size(); j++)
        {
            for(size_t i = 0; i<char_form.size(); i++)
            {
                ch.Char.UnicodeChar = char_form.at(i);
                ch.Attributes = char_col.at(j);
                palette.push_back(ch);
            }
        }
    }
    
    //Beispielfunktion 
    void beispiel_(HANDLE set, std::vector <CHAR_INFO> &palette)
    {
        int counter = 0;
        int plasma[Width][Height], buffer[Width][Height];
        intptr_t cha_number = (intptr_t)palette.size();
        int color;
        /*generate colors*/
        for(size_t x = 0; x<Width; x++)
        {
            for(size_t y = 0; y<Height; y++)
            {
                color = int
                    (
                        cha_number / 2 + (cha_number / 2 * sin(x / 10.0))
                        + cha_number / 2 + (cha_number / 2 * sin(y / 16.0))
                        + cha_number / 2 + (cha_number / 2 * sin(sqrt(double((x - Width / 2.0)
                                                                             * (x - Width / 2.0) + (y - Height / 2.0) * (y - Height / 2.0))) / 6.0))
                        + cha_number / 2 + (cha_number / 2 * sin(sqrt(double(x * x + y * y)) / 6.0))
                        ) / 4;
                plasma[x][y] = color;
            }
        }
        /*set colors*/
        int paletteShift;
        while(!_kbhit())
        {
            paletteShift = int(clock() / 100);
            for(size_t x = 0; x<Width; x++)
            {
                for(size_t y = 0; y<Height; y++)
                {
                    buffer[x][y] = (plasma[x][y] + paletteShift) % cha_number;
                    //hier untere Funktion auskommentieren, um Unterschied zu erkennen 
                    setChar_(set, (SHORT)x, (SHORT)y, palette[(size_t)buffer[x][y]]);
                }
            }
            point(set, 0, 24, 15);
            std::cout << "Frames: " << counter;
            counter++;
        }
    }
    
    int main()
    {
        HANDLE set = GetStdHandle(STD_OUTPUT_HANDLE);
    
        std::vector <WCHAR> char_form;
        std::vector <WORD> char_col;
        std::vector <CHAR_INFO> palette;
        makePalette(palette, char_form, char_col);
    
        beispiel_(set, palette); //Beispielfunktion fürs Forum 
    
        SetConsoleTextAttribute(set, 15);
        std::cin.sync();
        std::cin.get();
    }
    

    Und wenn Du jetzt nicht mehr jedes Zeichen einzeln ausgibst, sondern alles zusammen, wird es nochmals flotter:

    #include <iostream>
    #include <vector>
    #include <windows.h>
    #include <cmath>
    #include <ctime>
    #include <conio.h>
    
    const WCHAR Block = 219, lGrey = 176, mGrey = 177, dGrey = 178, lBlock = 254;
    const unsigned int Width = 80, Height = 24;
    
    void point(HANDLE set, SHORT x, SHORT y, int col)
    {
        SetConsoleTextAttribute(set, (WORD)col);
        COORD pos;
        pos.X = x;
        pos.Y = y;
        SetConsoleCursorPosition(set, pos);
    }
    
    void makePalette(std::vector <CHAR_INFO> &palette, std::vector <WCHAR> &char_form, std::vector <WORD> &char_col)
    {
        palette.clear();
        char_form = { lGrey, mGrey, dGrey, Block, dGrey, mGrey, lGrey };
        char_col = { 4,6,14,10,2,11,3,9,1,5,13,12 };
    
        CHAR_INFO ch;
    
        for(size_t j = 0; j<char_col.size(); j++)
        {
            for(size_t i = 0; i<char_form.size(); i++)
            {
                ch.Char.UnicodeChar = char_form.at(i);
                ch.Attributes = char_col.at(j);
                palette.push_back(ch);
            }
        }
    }
    
    //Beispielfunktion 
    void beispiel_(HANDLE set, std::vector <CHAR_INFO> &palette)
    {
        // static, um den Stack nicht zu sehr zu belasten
        static CHAR_INFO outBuffer[Width * Height];
        int counter = 0;
        int plasma[Width][Height], buffer[Width][Height];
        intptr_t cha_number = (intptr_t)palette.size();
        int color;
        /*generate colors*/
        for(size_t x = 0; x<Width; x++)
        {
            for(size_t y = 0; y<Height; y++)
            {
                color = int
                    (
                        cha_number / 2 + (cha_number / 2 * sin(x / 10.0))
                        + cha_number / 2 + (cha_number / 2 * sin(y / 16.0))
                        + cha_number / 2 + (cha_number / 2 * sin(sqrt(double((x - Width / 2.0)
                                                                             * (x - Width / 2.0) + (y - Height / 2.0) * (y - Height / 2.0))) / 6.0))
                        + cha_number / 2 + (cha_number / 2 * sin(sqrt(double(x * x + y * y)) / 6.0))
                        ) / 4;
                plasma[x][y] = color;
            }
        }
        /*set colors*/
        int paletteShift;
        while(!_kbhit())
        {
            paletteShift = int(clock() / 100);
            for(size_t x = 0; x<Width; x++)
            {
                for(size_t y = 0; y<Height; y++)
                {
                    buffer[x][y] = (plasma[x][y] + paletteShift) % cha_number;
                    //hier untere Funktion auskommentieren, um Unterschied zu erkennen 
                    outBuffer[(Width * y) + x] = palette[(size_t)buffer[x][y]];
                }
            }
    
            SMALL_RECT rc = { 0, 0, Width - 1, Height - 1 };
            WriteConsoleOutput(set, outBuffer, COORD{ Width, Height }, COORD{ 0, 0 }, &rc);
            point(set, 0, 24, 15);
            std::cout << "Frames: " << counter;
            counter++;
        }
    }
    
    int main()
    {
        HANDLE set = GetStdHandle(STD_OUTPUT_HANDLE);
    
        std::vector <WCHAR> char_form;
        std::vector <WORD> char_col;
        std::vector <CHAR_INFO> palette;
        makePalette(palette, char_form, char_col);
    
        beispiel_(set, palette); //Beispielfunktion fürs Forum 
    
        SetConsoleTextAttribute(set, 15);
        std::cin.sync();
        std::cin.get();
    }
    

    Natürlich brechen beide Versionen keine Rekorde, Konsolen-Ausgabe ist halt langsam...



  • Ok, danke schon mal vorab 😉 Ich musss mir das noch anschauen. Kann evtl dauern. Aber trotzdem schon danke für die Erweiterungen.

    Bye und so



  • Wow! Also vielen Dank. Die Änderungen beschleunigen ja enorm. Vieles davon war mir bis dahin unbekannt, war mir also über die Möglichkeiten gar nicht bewusst.

    Deine Idee die Zeichen in einem Vorgang zu setzen, erinnert mich an diese Funktion:

    void clearScreen(HANDLE set)
    {
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        COORD pos;
        DWORD written;
        pos.X = 0;
        pos.Y = 0;
        GetConsoleScreenBufferInfo(set, &csbi);
        FillConsoleOutputCharacter(set, ' ', csbi.dwSize.X * csbi.dwSize.Y, pos, &written);
    }
    

    Wo ich mich schon gefragt habe, ob man diese Art von Ausgabe irgendwie anpassen könnte. Aber ich bin jetzt schon ein großes Stück weiter.



  • Dann warte mal auf hustbaer, der per puts/printf/cout noch erheblich viel schneller kann. Ich bin gespannt!



  • Im Moment war sogar paletteShift noch eine Bremse. Die kann man gut noch um eine Zehnergröße verringern 😋



  • Mox schrieb:

    Ich bin schon ganz gespannt, wie der Schwätzer von oben mit puts usw. mehrere 100 FPS hinbekommen will.

    Mox schrieb:

    Dann warte mal auf hustbaer, der per puts/printf/cout noch erheblich viel schneller kann. Ich bin gespannt!

    Bist du irgendwie doof oder sowas?

    Dein Beispiel läuft bei mir mit etwa 200~300 FPS (im Debug, mit Debugger dran hängen).
    Umgebaut auf puts -Ausgabe läuft es, oh Wunder, ca. genau gleich schnell. Bzw. im Release und ohne Debugger dran hängen sogar weit jenseits der 500 FPS. Natürlich ohne Farben - das geht unter Windows halt nicht wirklich mit puts.

    Würde mich echt interessieren was manche Leute für ein Problem haben...

    ps: Vielleicht liegt es auch daran dass ich das ganze auf einem PC teste, und nicht auf einem C64 mit Windows Emulator oder der Steuereinheit eines Kaugummiautomaten?



  • hustbaer schrieb:

    ps: Vielleicht liegt es auch daran dass ich das ganze auf einem PC teste, und nicht auf einem C64 mit Windows Emulator oder der Steuereinheit eines Kaugummiautomaten?

    Wenn Du sonst nichts sinnvolles beizutragen hast, so hast Du doch wenigstens den Längsten. Glückwunsch!



  • Nun streitet Euch bitte wegen meiner Anfrage nicht 😉

    Diese wurde doch in meinen Augen hervorragend gelöst und man sieht die Beteiligten, mehr zählt doch nicht ...



  • Wir streiten uns nicht wegen deiner Anfrage.
    Wir streiten uns weil Mox ein Idiot ist.



  • Hallo,

    ich hätte hier in dem Zusammenhang noch eine Frage. Es geht um die WriteConsoleOutput() Funktion.

    static CHAR_INFO outBuffer[Width * Height];
    
            for (unsigned int x = 0; x<Width; x++)
            {
                for (unsigned int y = 0; y<Height; y++)
                {
                    buffer[x][y] = (plasma[x][y] + paletteShift) % cha_number;
                    outBuffer[(Width * y) + x] = palette.at(buffer[x][y]);
                }
            }
            SMALL_RECT rc = { 0, 0, Width - 1, Height - 1 };
            WriteConsoleOutput(set, outBuffer, COORD { Width, Height }, COORD { 0, 0 }, &rc);
    

    Gibt es eine Möglichkeit, das fertige CHAR_INFO outBuffer[] (also vor dem eigentlichen WriteConsoleOutput()) per Referenz an eine Funktion zu übergeben, um nachträglich noch einige Werte/Inhalte zu verändern?



  • Falls in der Richtung nichts möglich sein kann, ich habe und verändere die Werte jetzt erst mal in einem std::array und übertrage die dann zum Schluss in den outBuffer.


Anmelden zum Antworten