void GameClient::ExecuteGameFrame_Replay()
{
    randcheckinfo.rand = RANDOM.GetCurrentRandomValue();
    AsyncChecksum checksum(randcheckinfo.rand);

    RTTR_Assert(replayinfo.next_gf >= framesinfo.gf_nr || framesinfo.gf_nr > replayinfo.replay.lastGF_);

    // Commands alle aus der Datei lesen
    while(replayinfo.next_gf == framesinfo.gf_nr) // Schon an der Zeit?
    {
        // RC-Type auslesen
        Replay::ReplayCommand rc = replayinfo.replay.ReadRCType();

        // Chat Command?
        if(rc == Replay::RC_CHAT)
        {
            unsigned char player, dest;
            std::string message;
            replayinfo.replay.ReadChatCommand(&player, &dest, message);

            // Nächsten Zeitpunkt lesen
            replayinfo.replay.ReadGF(&replayinfo.next_gf);

            if(ci)
                ci->CI_Chat(player, ChatDestination(dest), message);
        }
        // Game Command?
        else if(rc == Replay::RC_GAME)
        {
            std::vector<unsigned char> gcData = replayinfo.replay.ReadGameCommand();
            Serializer ser(&gcData.front(), gcData.size());
            GameMessage_GameCommand msg;
            msg.Deserialize(ser);
            // Nächsten Zeitpunkt lesen
            replayinfo.replay.ReadGF(&replayinfo.next_gf);

            // NCs ausführen (4 Bytes Checksumme und 1 Byte Player-ID überspringen)
            ExecuteAllGCs(msg);

            // Replay ist NSYNC äh ASYNC!
            if(msg.checksum.randState != 0 &&  msg.checksum != checksum)
            {
                if(replayinfo.async == 0)
                {
                    // Meldung mit GF erzeugen
                    char text[256];
                    sprintf(text, _("Warning: The played replay is not in sync with the original match. (GF: %u)"), framesinfo.gf_nr);

                    // Messenger im Game (prints to console too)
                    if(ci)
                        ci->CI_ReplayAsync(text);

                    LOG.lprintf("Async at GF %u: Checksum %i:%i ObjCt %u:%u ObjIdCt %u:%u\n", framesinfo.gf_nr,
                        msg.checksum.randState, checksum.randState,
                        msg.checksum.objCt, checksum.objCt,
                        msg.checksum.objIdCt, checksum.objIdCt);

                    // pausieren
                    framesinfo.isPaused = true;
                }

                replayinfo.async++;
            }

            // resync random generator, so replay "can't" be async.
            // (of course it can, since we resynchronize only after each command, the command itself could be use multiple rand values)
            //RANDOM.ReplaySet(msg.checksum);
        }
    }

    // Frame ausführen
    NextGF();

    // Replay zu Ende?
    if(framesinfo.gf_nr == replayinfo.replay.lastGF_)
    {
        // Replay zu Ende

        // Meldung erzeugen
        char text[256];
        sprintf(text, _("Notice: The played replay has ended. (GF: %u, %dh %dmin %ds, TF: %u, AVG_FPS: %u)"), framesinfo.gf_nr, GAMEMANAGER.GetRuntime() / 3600, ((GAMEMANAGER.GetRuntime()) % 3600) / 60, (GameManager::inst().GetRuntime()) % 3600 % 60, GameManager::inst().GetFrameCount(), GameManager::inst().GetAverageFPS());

        // Messenger im Game
        if(ci)
            ci->CI_ReplayEndReached(text);

        if(replayinfo.async != 0)
        {
            char text[256];
            sprintf(text, _("Notice: Overall asynchronous frame count: %u"), replayinfo.async);
            // Messenger im Game
            if(ci)
                ci->CI_ReplayEndReached(text);
        }

        replayinfo.end = true;

        // pausieren
        framesinfo.isPaused = true;
    }
}
void GameClient::ExecuteGameFrame_Replay()
{
    randcheckinfo.rand = RANDOM.GetCurrentRandomValue();
    AsyncChecksum checksum(randcheckinfo.rand);

    const unsigned curGF = GetGFNumber();
    RTTR_Assert(replayinfo.next_gf >= curGF || curGF > replayinfo.replay.lastGF_);

    // Execute all commands from the replay for the current GF
    while(replayinfo.next_gf == curGF)
    {
        // What type of command follows?
        Replay::ReplayCommand rc = replayinfo.replay.ReadRCType();

        if(rc == Replay::RC_CHAT)
        {
            unsigned char player, dest;
            std::string message;
            replayinfo.replay.ReadChatCommand(&player, &dest, message);

            if(ci)
                ci->CI_Chat(player, ChatDestination(dest), message);
        } else if(rc == Replay::RC_GAME)
        {
            std::vector<unsigned char> gcData = replayinfo.replay.ReadGameCommand();
            Serializer ser(&gcData.front(), gcData.size());
            GameMessage_GameCommand msg;
            msg.Deserialize(ser);

            // Execute them
            ExecuteAllGCs(msg);

            // Check for async if checksum data is valid
            if(msg.checksum.randState != 0 &&  msg.checksum != checksum)
            {
                // Show message if this is the first async GF
                if(replayinfo.async == 0)
                {
                    char text[256];
                    sprintf(text, _("Warning: The played replay is not in sync with the original match. (GF: %u)"), curGF);

                    if(ci)
                        ci->CI_ReplayAsync(text);

                    LOG.write("Async at GF %u: Checksum %i:%i ObjCt %u:%u ObjIdCt %u:%u\n") % curGF
                        % msg.checksum.randState % checksum.randState
                        % msg.checksum.objCt % checksum.objCt
                        % msg.checksum.objIdCt % checksum.objIdCt;

                    // and pause the game for further investigation
                    framesinfo.isPaused = true;
                }

                replayinfo.async++;
            }
        }
        // Read GF of next command
        replayinfo.replay.ReadGF(&replayinfo.next_gf);
    }

    // Run game simulation
    NextGF();

    // Check for game end
    if(curGF == replayinfo.replay.lastGF_)
    {
        char text[256];
        sprintf(text, _("Notice: The played replay has ended. (GF: %u, %dh %dmin %ds, TF: %u, AVG_FPS: %u)"), curGF, GAMEMANAGER.GetRuntime() / 3600, ((GAMEMANAGER.GetRuntime()) % 3600) / 60, (GameManager::inst().GetRuntime()) % 3600 % 60, GameManager::inst().GetFrameCount(), GameManager::inst().GetAverageFPS());

        if(ci)
            ci->CI_ReplayEndReached(text);

        if(replayinfo.async != 0)
        {
            char text[256];
            sprintf(text, _("Notice: Overall asynchronous frame count: %u"), replayinfo.async);
            // Messenger im Game
            if(ci)
                ci->CI_ReplayEndReached(text);
        }

        replayinfo.end = true;
        framesinfo.isPaused = true;
    }
}