Code Ausführung ist durcheinader bei Superscalar CPU



  • Hallo Liebe Foren Benutzer und Benutzerinnen.☺

    Ich haben endlich mal seit September vergangen Jahr wider Zeit gefunden an "meiner" Playstation 2 Spiel Engine weiter zu programmieren. ...Meine in "", weil ich reverse Engineere ein Spiel 🙃

    An einer stelle komme ich nicht weiter. Ich habe den code auch noch auf den Raspberry am laufen und auf eine Normalen Pentium PC, da tritt das Problem nicht auf. Es muss also mit der Playstation2, speziell mit der (das vermute ich) Superscalar Funktionalität der CPU zu tun haben.

    Damit ihr das Problem nachvollziehen könnt muss ich Zuerst mal die Objektorientiert erklären.
    Ich habe eine Klasse "Engine", die Klasse kümmert sich um das erzeugen (spawnen) gameobjekten zudem wird hier auch die Kollision mit allem Gameobjekten abgefragt.
    Jedes gameobjekt ist in der Basis ein "LivingObject".
    Daher ist die Vererbung:
    class Charakter : public LivingObject, private Char_States

    In der Abgeleiteten klasse und Basisklasse nutze ich eine Virtuelle Funktion:
    float hit_impl(const Itr *itr, Charakter *thisChar, const Volume *vol) override
    in LinvinObjeckt: virtual float hit_impl(const Itr *itr, Charakter *thisChar, const Volume *vol) = 0;
    Ich habe die vtable zwar mehrfach überprüft, aber ich erwähne es trotzdem noch.
    Auch mit Upcasting also ohne vtable besteht das Problem.

    Die Engine klasse ist eine Singleton klasse und die Gameobjekte werden in eine vector gespeichert:
    private:
    Engine() {} // Privater Konstruktor -> Keine weiteren Instanzen erlaubt
    vector<std::unique_ptr<LivingObject>> gameObjects;
    Charakter* charakterControllerObject[NUM_CHILD_CONTROLLER] = {nullptr};

    Nun zu Problem, dazu fange ich bei der game loop an:
    Die game loop ruft engine.run() auf:

    void Engine::run()
    {
        for (uint8_t i = 0; i<NUM_CHILD_CONTROLLER; i++)
        {
            if(charakterControllerObject[i]){
                charakterControllerObject[i]->childController->fetch();
                charakterControllerObject[i]->combodec->frame();
            }
        }
        TU_trans();
    }
    
    void Engine::TU_trans() //every  30fps
    {
        emit_event(1);
        //TODO proces_task
        emit_event(0);
        bg_TU(renderer);
    }
    
    void Engine::emit_event(uint8_t E)
    {
        for (auto& go : gameObjects)
        {
            if(E == 0)
            {
                go->TU_update();
            }
            else
            {
                go->transit();
            }
        }
    }
    

    Charakter die durch eine Person gesteuert werden landen zusätzlich noch im
    charakterControllerObject array. Via:

    if (!isCom)
        {
            charPtr->addController(cc);
            charakterControllerObject[numChildController] = charPtr.get(); // Wir speichern den rohen Zeiger
            numChildController += 1;
        }
    

    Charakter, die von einer Person gesteuert werden, werden zur Laufzeit nie aus dem Speicher gelöscht. Sollte man das Spiel verlassen indem man ins Menü wechselt, setze ich den Inhalt von charakterControllerObject wider auf nullptr.
    Das nur mal so am Rande erwähnt.
    childController->fetch(); und combodec->frame(); validieren nur die Controller eingaben.

    Die Funktion go->transit(); macht letztendlich die Frame transition, also wechselt das Bild auf dem Display.
    Das eigentliche Problem entsteht erst beim aufrufen der Funktion go->TU_update();
    TU_update durchläuft die komplette state machine, je nach State und frame passieren dinge.
    TU_update ruft aber auch auf:

    void Charakter::post_interaction()
    {
        const uint8_t ITR_Len = thisFrame.D->numOfItr;
        for(uint8_t i=0; i<ITR_Len; i++)
        {
            const Itr *itr = &thisFrame.D->itr[i];
            Volume vol = getVolume(itr, nullptr);
            vol.zwidth = 0;
            std::vector<LivingObject*> ex;
            ex.push_back(this);
            where.tag = "body";
            std::vector<LivingObject*> hit = Engine::getInstance().query(&vol, ex, where);
            switch(itr->kind)
            {
            case 0:
            case 4:
            {
                for(uint16_t t=0; t<hit.size(); t++)
                {
                    bool canhit= true;
                    LivingObject* gameObjectPtr = hit[t];
                    switch(itr->effect)
                    {
                    case 0:
                    case 1:
                        if(gameObjectPtr && gameObjectPtr->where.type == 0 && gameObjectPtr->where.team == this->where.team)
                        {
                            canhit = false;
                        }
                        break;
                        if(gameObjectPtr && !(gameObjectPtr->where.type != 0 && gameObjectPtr->getState() == 3000))
                        {
                            canhit = false;
                        }
                        break;
                    case 21:
                    case 22://bunring
                        if(gameObjectPtr && this->getState() == 18 && gameObjectPtr->where.team == this->where.team) //not bunring teammates
                        {
                            canhit = false;
                        }
                    case 20:
                        if(!gameObjectPtr)
                        {
                            canhit = false;
                        }
                        break;
                    }//switch itr effewct
                    if(canhit)
                    {
                        if(!_itr->arest)
                        {
                            std::cout << "gameObjectPtr address: " << gameObjectPtr << " this address: " << this << std::endl;
                            if(attacked(gameObjectPtr->hit_impl(itr, this, &vol)))
                            {
                                itr_arest_update(itr);
    
                            }
                        }
                    }
                }//for hit
            }//case 4
            break;
            }//itr.kind
            //delete vol;
        }
    }
    

    Hier wird wiederum in Engine std::vector<LivingObject*> hit = Engine::getInstance().query(&vol, ex, where); aufgerufen.

    std::vector<LivingObject*> Engine::query(const Volume *volume, const std::vector<LivingObject*>& exclude, const Where &where)
    {
        std::vector<LivingObject*> result;
        std::string tag = where.tag;
    
        if(tag.empty())
        {
            tag = "body";
        }
    
        uint16_t tagValue = 0;
        std::vector<std::string> split = splitString(tag, ':');
        tag = "vol_" + split[0];
    
        if(split.size() == 2)
        {
            tagValue = std::stoi(split[1]);
        }
    
        for (auto& go : gameObjects)
        {
            Where *gameObjectdata = &go->where;
    
            // Ausschlussprüfung
            bool excluded = false;
              for(uint8_t ex = 0; ex < exclude.size(); ex++){
                if(go.get() == exclude[ex])
                    {
                        excluded = true;
                        break;
                    }
            }
    
            if(excluded || gameObjectdata->team == where.team || gameObjectdata->not_team == where.not_team ||
                    gameObjectdata->type != where.type || gameObjectdata->not_type == where.not_type)
            {
                continue; // Überspringe das aktuelle Objekt, wenn es ausgeschlossen ist
            }
    
            if(volume == nullptr)
            {
                result.push_back(go.get());
            }
            else
            {
                std::vector<Volume> vol;
                if(tag == "vol_itr")
                {
                    vol = go->vol_itr(tagValue);
                }
                else if(tag == "vol_body")
                {
                    vol = go->vol_body();
                }
    
                for(uint16_t j = 0; j < vol.size(); j++)
                {
                    if(intersect(volume, vol[j]))
                    {
                        result.push_back(go.get());
                        break;
                    }
                }
            }
        }
        //return std::move(result);
        return result;
    }
    

    Hier passiert nun merkwürdiges, was auf den Raspberry nicht passiert.
    Die Ausgabe std::cout << "gameObjectPtr address: " << gameObjectPtr << " this address: " << this << std::endl;
    Zeigt mir an, wer wen angreift (caller hit callee).
    Wenn ich nun eine Combo ausführe (komischerweise passiert das nur bei einer Kombo, dann kommt diese Ausgabe mehrfach und beim Raspberry nur ein mal. Normaler weise wird bei einer Kombo if(!_itr->arest) gesetzt und über einen Timer abgebaut. Das setzen von arest Funktioniert, aber der nächste Aufruf von post Interaktion ist vom falschen caller.

    hier ist die Ausgabe von der Playstation
    gameObjectPtr address: 0x3173b0 this address: 0x317080
    gameObjectPtr address: 0x317080 this address: 0x3173b0
    gameObjectPtr address: 0x317080 this address: 0x3173b0
    gameObjectPtr address: 0x317080 this address: 0x3173b0
    gameObjectPtr address: 0x317080 this address: 0x3173b0

    Die erste Ausgabe passt noch gameObjectPtr address: 0x3173b0 this address: 0x317080
    Die anderen Ausgaben dürfen schon nicht mehr passieren.
    Das Problem ist, irwo in den loops:
    for (auto& go : gameObjects)

    In emit_event wird durch alle Objekte irritiert, wenn query aufgerufen wird auch.
    die loop for (auto& go : gameObjects) in query pausiert die loop in emit_event, so sollte es sein.
    Aber auf der Playstation nicht.

    Ich habe mal in emit_event nach go->TU_update();
    ausgegeben welche Objekt aufgerufen wird:
    std::cout << "aufruf von: " << go.get() << std::endl;
    go->TU_update();

    und inherhalb von TU_update dann mit this gegen geprüft.
    Das ist ja schon Falsch, aber erst wenn query aufgerufen wird.

    Ich begreife es nicht.
    Warschelich muss man was mit mutex machen aber egal wie ich es anstell, ein mutex dead lockt sich.

    Es schint ein relativ komplexex Porblem zu sein, oder ich denke zu Komplizert.
    Asso, ich habe vergessen zu erwähnen was das Eigentliche Problem ist:
    Wenn hier: if(attacked(gameObjectPtr->hit_impl(itr, this, &vol))) die Zeiger vertasucht sind haue ich mich selbst.

    VG, Tor Denny



  • Ich habe bei weitem nicht alles gelesen (sorry, gerade keine Zeit), aber sowas hier:

    @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

        // Ausschlussprüfung
        bool excluded = false;
          for(uint8_t ex = 0; ex < exclude.size(); ex++){
            if(go.get() == exclude[ex])
                {
                    excluded = true;
                    break;
                }
        }
    

    Du kannst auch die STL bemühen:

    bool excluded = std::find(exclude.begin(), exclude.end(), go.get()) != exclude.end();
    

    Und/oder in dies in eine Funktion wrappen.
    (oder via https://en.cppreference.com/w/cpp/algorithm/ranges/contains wenn ranges da sind)

    Alternativ für exclude ein set (oder vergleichbar) nehmen (natürlich nur, wenn das mehrfach abgefragt wird).

    Was ist denn das charakterControllerObject bei dir? Ein festes Array? Ich sehe nur diese 2 Zeilen:

    charakterControllerObject[numChildController] = charPtr.get(); // Wir speichern den rohen Zeiger
    numChildController += 1;
    

    Kann das numChildController überlaufen? Warum ist das ansonsten nicht auch ein vector? Deine Loop scheint ja immer bis zur Kostanten NUM_CHILD_CONTROLLER zu laufen. Und macht dann immer einen check auf nullptr. Irgendwie merkwürdig.

    Das post_interaction finde ich wegen der Loop und den vielen vectoren und switch/cases (+verschachtelt loop+switch/case) und wegen der vielen magischen Zahlen nicht wirklich gut lesbar.

    Ich würde es hilfreich finden, wenn du jede der Funktionen kurz kommentieren würdest, was die so tun soll und was sie returnt. Wenn das schwer zu formulieren ist, dann aufteilen.

    Sorry, dass dir das beim eigentlichen Problem wohl nicht helfen wird 😞



  • Dieser Beitrag wurde gelöscht!


  • @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Wenn ich nun eine Combo ausführe (komischerweise passiert das nur bei einer Kombo, dann kommt diese Ausgabe mehrfach und beim Raspberry nur ein mal.

    Das klingt ja nach einem Timing Problem.

    Wie reagiert deine Software wenn du z.B. ein kleines Delay an gewissen Stellen (z.B. bei deinem std::cout << "gameObjectPtr address: "...) einbaust?

    Also z.B. folgendes:

    std::cout << "gameObjectPtr address: " << gameObjectPtr << " this address: " << this << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    


  • BTW:

    Und seie bitte vorsichtig mit der Verwendung von Pointern, da diese gerne Ärger machen. Ein Beispiel:

    std::vector<int> L{1, 2, 3};
    
    int* P = L[0];
    for (int i = -5; i < 5; i++)
      L.push_back(i);
    std::cout << *P;
    


  • @Quiche-Lorraine sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Das klingt ja nach einem Timing Problem.

    Das habe ich mir auch schon gedacht, aber ich Blicks nicht was hier passiert. Wenn man gdb nutzen könnte, könnte man mit dem Debugger den Code schrittweise durchgehen.

    Ich habe mal in TU_update eine Ausgabe hinzugefügt auf dem PC als auch auf der PS2:

    void LivingObject::TU_update() // update done at every TU (30fps)
    {
        if(effect->stuck){
                std::cout << "effect->timein: " << signed(effect->timein) << " ptr: " << this << std::endl;
        }
        if (state_update(-6) <= 0)
        {
            frame_force();
        }
        if(effect->timein < 0)
        {
            if(effect->oscillate)
            {
                if(effect->oi == 1)
                {
                    effect->oi = -1;
                }
                else
                {
                    effect->oi = 1;
                }
                enginePtr->set_x_y(uID, engineID, metric->sx + effect->oscillate* effect->oi,metric->sy + metric->sz);
            }
            else if(effect->blink)
            {
                if(effect->bi < 0)
                {
                    effect->bi = 0;
                }
                uint8_t mod = effect->bi%4;
                if(mod == 0 || mod == 1)
                {
                    enginePtr->show_hide(engineID, false);
                }
                else if(mod == 2 || mod == 3)
                {
                    enginePtr->show_hide(engineID, true);
                }
                effect->bi++;
            }
            if(effect->timeout == 0)
            {
                effect->num = -99;
                if(effect->stuck)
                {
                    effect->stuck = false;
                }
                if(effect->oscillate)
                {
                    effect->oscillate = 0;
                    enginePtr->set_x_y(uID, engineID, metric->sx, metric->sy + metric->sz);
                }
                if(effect->blink)
                {
                    effect->blink = false;
                    effect->bi = 0;
                    //todo SHOW()
                }
                if(effect->super)
                {
                    effect->super = false;
                }
            }
            else if(effect->timeout == -1)
            {
                if(effect->dvx)
                {
                    metric->vx = effect->dvx;
                }
                if(effect->dvy != 0)
                {
                    metric->vy = effect->dvy;
                }
                effect->dvx = 0;
                effect->dvy = 0;
            }
            effect->timeout--;
        }
        if(effect->timein < 0 && effect->stuck)
        {
            //struck
        }
        else
        {
            std::cout << "aufgerufen wurde: " << this << std::endl;
            state_update(-5);
        }
        if(health->hp <=0)
        {
            //todo health
        }
    
        if(_itr->arest > 0)
        {
            _itr->arest--;
        }
    }
    

    Der PC Spuckt aus:

    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    aufgerufen wurde: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    gameObjectPtr:0x5555561f0588 thisPtr: 0x5555561f0470
    effect->timein: 0 ptr: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    effect->timein: -1 ptr: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    effect->timein: -2 ptr: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    effect->timein: -3 ptr: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    aufgerufen wurde: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    aufgerufen wurde: 0x5555561f0588
    aufruf von: 0x5555561f0470
    aufruf von: 0x5555561f0588
    aufgerufen wurde: 0x5555561f0470
    aufgerufen wurde: 0x5555561f0588
    

    Hier fand der Angriff statt gameObjectPtr:0x5555561f0588 thisPtr: 0x5555561f0470

    Bei der Playstation Sieht man schon, da ist wirr-war:

    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    gameObjectPtr address: 0x317530 this address: 0x317200
    aufruf von: 0x317530
    effect.timein: 0 ptr: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    effect.timein: -1 ptr: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    effect.timein: -2 ptr: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    effect.timein: -3 ptr: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -1 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -2 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -3 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    effect.timein: -4 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    effect.timein: -5 ptr: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -1 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -2 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    gameObjectPtr address: 0x317200 this address: 0x317530
    aufruf von: 0x317200
    effect.timein: -3 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    effect.timein: -4 ptr: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    effect.timein: -5 ptr: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    aufruf von: 0x317200
    aufgerufen wurde: 0x317200
    aufruf von: 0x317530
    aufgerufen wurde: 0x317530
    

    Beim PC kommt immer zwei mal
    aufruf von: 0x5555561dce00
    aufruf von: 0x5555561dcf18

    Das Macht die Playstation nie.
    Von der Sache her ruft ja TU_update -> Query auf, wenn query läuft pausiert TU update, bzw die schleife die TU_update aufruft.
    Es macht aber den Eindruck alls wenn emit_event noch zu ende läuft, während query läuft. Wie zwei Thread's.
    Was mir auch noch anfällt, Ausgabe beim PC

    @wob sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Du kannst auch die STL bemühen:
    bool excluded = std::find(exclude.begin(), exclude.end(), go.get()) != exclude.end();

    Es gibt immer 100 Wege die nach Rom führen

    @wob sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Was ist denn das charakterControllerObject bei dir? Ein festes Array? Ich sehe nur diese 2 Zeilen:
    Das ist ein statisches Array, da landen die Charaktere drinnen, die von einen Menschen gesteuert werden.
    Da man auf der Playstation mehrere Controller anschließen kann.

    Das array ist nur ein Überbleibsel aus meiner verzweifelten such nach dem Bug.
    Da der Fehler erst nach beim Ausführen ein Kombo auf tritt hatte ich ein Problem mit der Controller Klasse vermutet
    Das ist bei der Playstation 2 ,maximal beschissen, denn jede Taste ist eine Anlog Taste, auch X und Viereck.
    Man muss erst mal zwischenspeichern wie weit runter eine Taste gedrückt ist um dann im nächsten Schritt zu erkennen, der Wert hat zu verkleinert ....Taste nicht mehr gedrückt.
    Man hat keine Festen Interrupts wie bei einer Tastatur.

    Ich könnte genau so gut in der emit_event Funktion schauen ob in gameobjekt ein ChildController Pointer ist, wenn ja kann ich auch fetch aufrufen.
    Das spart auch eine schleife😃



  • Was mich wundert, warum gibt die Playstation mehrfach diese Ausgabe aus:

    @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    if(!_itr->arest)
    {
    std::cout << "gameObjectPtr address: " << gameObjectPtr << " this address: " << this << std::endl;

    Klar, weil post_interaction schon von falschen Instanz aufgerufen wurde, da ist arest 0
    Aber hier, ist was nicht in Ordung, dass muss ich mal abklären:
    const uint8_t ITR_Len = thisFrame.D->numOfItr;
    for(uint8_t i=0; i<ITR_Len; i++)

    Itr ist bei dem Zweienten Charakter immer 0, weil der macht ja nix und steht nur rum.

    Esseiden, beim aufrufen der hit Funktion überschrieb ich die Adresse der Instanz, Übertrieben gesagt this=... .
    Ich rufe ja hier hit auf: gameObjectPtr->hit_impl(itr, this, &vol))
    hit ist eine Methode von Charakter, daher die virtuelle Funktion:
    In Charakter.h habe ich

    float hit_impl(const Itr *itr, Charakter *thisChar, const Volume *vol) override
        {
            return hit(itr, thisChar, vol);
        }
    

    Wenn ich return hit(itr, this, vol); schreibe, haue ich mich direkt selbst, nicht erst bei einer Combo.
    Wahrscheinlich doch ein vTable Problem oder der Compiler kann mit this nicht umgehen.
    Wer Spaß hat, kann sich noch durch die hit Funktion hangeln, aber das Problem muss schon beiaufrufen, bzw vor dem aufrufen passieren.

    float Charakter::hit(const Itr *itr, Charakter *att,const Volume *vol)
    {
        Charakter *that = this;
        util utility;
        if(!that->itr_vrest_test(att->engineID))
        {
            return false;
        }
        bool accepthit = false;
        bool defended = false;
        float ef_dvx = 0;
        ef_dvy = 0;
        float inj = 0;
    
        auto falldown = [&](){
              if(itr->dvy == 0)
            {
                ef_dvy = GC._default.fall.dvy;
            }
            that->health.fall = 0;
            that->bdyBox.vy = 0;
            const bool _front = (att->bdyBox.x > that->bdyBox.x) == (that->bdyBox.dir == '>');
            if((itr->dvx != 0 && itr->bdefend >= 0) && (_front && itr->dvx < 0 && itr->bdefend >= 60))
            {
                that->setFrame(186,21);
            }
            else if(_front)
            {
                that->setFrame(180,21);
            }
            else if(!_front)
            {
                that->setFrame(186,21);
            }
        };//lambda fall down
    
        auto fall = [&](){
               if(itr->fall != 0)
            {
                health.fall += itr->fall;
            }else{
                health.fall += GC._default.fall.value;
            }
            const uint16_t _fall = health.fall;
            if(that->getState() == 13)
            {
                falldown();
            }
            else if(that->bdyBox.y < 0 || that->bdyBox.vy < 0)
            {
                falldown();
            }
            else if(that->health.hp - inj <= 0)
            {
               falldown();
            }
            else if(_fall > 0 && _fall <= 20)
            {
                that->setFrame(220,20);
            }
            else if(_fall > 20 && _fall <=30)
            {
                that->setFrame(222,20);
            }
            else if(_fall > 30 && _fall <= 40)
            {
                that->setFrame(224,20);
            }
            else if(_fall > 40 && _fall <= 60)
            {
                that->setFrame(226,20);
            }
            else if(GC.fall.KO < _fall)
            {
                falldown();
            }
        };// lambda Fall
    
    
        if(that->getState() == 10) //beeing caught
        {
            if(that->catching && that->catching->_char->caught_cpointhurtable())
            {
                accepthit = true;
                fall();
            }
            if(catching && catching->_char->caught_cpointhurtable() == 0 && catching->_char->engineID != att->engineID)
            {
                // I am unhurtable as defined by catcher,
                // and I am hit by attacker other than catcher
            }
            else
            {
                accepthit = true;
                inj += abs(itr->injury);
                if(itr->injury > 0)
                {
                    that->effect_create(0, GC.effect.duration);
                    int16_t tar;
                    if(itr->vaction != 0)
                    {
                        tar = itr->vaction;
                    }
                    else
                    {
                        tar = (att->bdyBox.x > that->bdyBox.x) == (that->bdyBox.dir == '>') ? that->thisFrame.D->cpoint.fronthurtact : that->thisFrame.D->cpoint.backhurtact;
                    }
                    that->setFrame(tar, 20);
                }
            }
        }
        else if(that->getState() == 14)
        {
            //lying
        }
        else if(that->getState() == 19 && att->getState() == 3000)
        {
            //firerun
            return false;
        }
        else if(itr->kind < 0 || itr->kind == 0 || itr->kind == 4 || itr->kind == 9)  //normal, falling, reflectif shiled
        {
            accepthit = true;
            const bool compen = bdyBox.y == 0 ? 1 : 0;
            const int8_t attdir = att->bdyBox.vx == 0 ? att->dirh() : (att->bdyBox.vx > 0 ? 1 : -1);
            if(itr->dvx != 0)
            {
                ef_dvx =  attdir * (itr->dvx - compen);
            }
            else
            {
                ef_dvx = 0;
            }
            if(itr->dvy != 0)
            {
                ef_dvy = itr->dvy;
            }
            else
            {
                ef_dvy = 0;
            }
            const uint8_t effectnum = itr->effect >= 0 ? itr->effect : GC._default.effect.num;
            if(that->getState() == 13 && effectnum == 30) // frozen characters are immune to effect 30 'weak ice'
            {
                return false;
            }
            if((that->getState() == 18 || that->getState() == 19) && (effectnum == 20 || effectnum == 21)) //burning and firerun characters are immune to effect 20/21 'weak fire'
            {
                return false;
            }
            if(that->getState() == 7 && (att->bdyBox.x > bdyBox.x) == (bdyBox.dir == '>')) // defend ; // attacked in front
            {
                if(itr->injury != 0)
                {
                    inj += GC.defend.injury.factor * itr->injury;
                }
                if(itr->bdefend >= 0)
                {
                    health.bdefend += itr->bdefend;
                }
                if(that->health.bdefend > GC.defend.break_limit)
                {
                    that->setFrame(112,20);
                }
                else
                {
                    that->setFrame(110,20);
                }
                if(ef_dvx)
                {
                    ef_dvx += (ef_dvx > 0 ? -1 : 1) * utility.lookup_abs(GC.defend.absorb, ef_dvx);
                }
                ef_dvy = 0;
                if(that->health.hp - inj <= 0)
                {
                    falldown();
                }
                else
                {
                    defended = true;
                }
            }
            else  //end defend
            {
                //TODO hold obj
                if(itr->injury != 0)
                {
                    inj += itr->injury;
                }
                health.bdefend = 45;
                fall();
            }
            uint8_t vanish = GC.effect.duration - 1;
            switch(getNext())
            {
            case 111:
            {
                vanish = 3;
                break;
            }
            case 112:
            {
                vanish = 4;
                break;
            }
            }
            that->effect_create(effectnum, vanish, ef_dvx, ef_dvy);
            //TODO Posteffec
        }//TODO itr-kind 15 & itr.kind 16
    
    
        if(accepthit)
        {
            that->_itr->attacker = att;
        }
        that->injury(inj);
        if(accepthit)
        {
            return inj;
        }
        else
        {
            return 0;
        }
    }
    

    Das Problem ist ja, das sich beide Chakrakter KO hauen aber nur bei ienr Kombo.
    Das beduet das der Zeiger in der vTabel zum zeitunkt x noch falsch ist.
    Sehe ich das richtg?



  • @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Wenn ich return hit(itr, this, vol); schreibe

    Ich habe das noch mal gecheckt return hit(itr, this, vol); geht auch und macht genau das selbe Problem.
    Bei einer Combo treffe ich mich selbst.

    Es gibt ja bei der Playstation einige Restriktionen die zu beachten sind. Was mir im klassischen C(fast alles auf der Ps2 ist in c geschrieben) aufgefallen ist, dass an jedem struct attribute((packed)) hängt.
    attribute((packed)) geht in c++ nicht, da müsste ich das linker File neu schreiben. Ich bin bei dem Makefile schon fast gestorben😩
    Dann wird sehr viel mit semaphores gearbeitet und mutex wird auch oft verwendet mit und ohne Zusammenhang der Semaphores.

    __sync() habe ich auch schon mal gesehen, das ist auch so ein Speicher voodoo.

    Es gibt zwei potentiell Probleme die ich jetzt Atok sehe. Entweder kann er "this" nicht verarbeiten, aufgrund von aggresiven Optimierungen ( die ich aus habe ).
    Oder er kann die vTable nicht verarbeiten.

    Eine Idee habe ich noch.
    Ich mache die Engine klasse als Basis Klasse für LivingObject.
    Dann kann ich in über eine Virtuelle Funktion alle abgeleiteten Klassen ansprechen.
    Damit ist dann die Engine Klasse teil der vTable.
    Ist aber auch erst mal nur graue Theorie.



  • @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Es gibt ja bei der Playstation einige Restriktionen die zu beachten sind. Was mir im klassischen C(fast alles auf der Ps2 ist in c geschrieben) aufgefallen ist, dass an jedem struct attribute((packed)) hängt.
    attribute((packed)) geht in c++ nicht, da müsste ich das linker File neu schreiben. Ich bin bei dem Makefile schon fast gestorben😩

    Sicher? Ich habe damit zumindest bei GCC bisher keine Probleme gehabt. Da geht's in C++ sogar mit C++11-Attributen: struct [[gnu::packed]] A { ... }. Ist da der C++-Compiler vielleicht ein anderer als der C-Compiler? Oder vielleicht stand das Attribut auch nur an der falschen Stelle? Das gehört nach struct/class. Was gibt's denn da für ne Fehlermeldung?

    Mit nem Linker-Skript hat man soweit ich weiß eh keinen Einfluss auf diese interne Struktur von Objekten. Da kann man nur festlegen, wo die Objekte als ganzes letztendlich hin sollen (im Speicher oder in der Executable).

    Und sorry dass ich mit deinem Hauptproblem auch keinen Geistesblitz habe. Ich weiß nur, dass die PS2 wegen ihrer besonderen Architektur zwar sehr leistungsfähig für ihre Generation, aber eine echte Herausforderung zu programmieren war. Das riecht ein bisschen als müsse man die Hardware schon sehr intim kennen, um da das Problem zu finden 😉



  • @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Normaler weise wird bei einer Kombo if(!_itr->arest) gesetzt und über einen Timer abgebaut

    Was meinst du mit Timer? Interrupt von einem Hardware-Timer?
    Und hast du mehrere Threads laufen?

    bei Superscalar CPU

    Alle modernen CPUs sind superscalar. Man hat bloss aufgehört das Wort zu verwenden, weil es mittlerweile Standard ist. Daran liegt es also schonmal nicht.

    Falls du unions oder verbotene Pointer-casts verwendest (also z.B. nen int* zu nem short* casten und dann dereferenzieren) könntest du versuchen mit -fno-strict-aliasing zu bauen.

    Was man theoretisch auch nicht ausschliessen kann sind Compiler-Bugs. Soweit ich weiss erfordert der MIPS R5900 dass die Instruktionen vom Compiler schon passend "angeordnet" werden, so dass Abhängigkeiten/Ausführungszeiten berücksichtigt wurden. Wenn der Compiler das nicht macht, dann kann es sein dass der generierte Code auf einem Emulator funktioniert, aber auf echter Hardware dann leider nicht.

    Also vielleicht mal nen anderen Compiler versuchen. Mach dich schlau was der "beste" Compiler für PS2 ist.



  • @D_Key sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Das habe ich mir auch schon gedacht, aber ich Blicks nicht was hier passiert. Wenn man gdb nutzen könnte, könnte man mit dem Debugger den Code schrittweise durchgehen.

    Kann man aber leider nicht, da dies dein Timing komplett stören würde.

    Mein Gedanke zielt in folgende Richtung:

    Du nutzt eine PS2 welche sich vermutlich anders verhält als ein Standard PC. Du sagst dass das Problem bei einem Combo auftritt. Hau mich bitte nicht, aber Spielen ist nicht so mein Ding. Ein Combo war doch eine schnelle Kombination von Tastenkombinationen bzw. Schlägen. In Endeffekt eine schnelle Folge von Tastenevents.

    Von daher wäre es interresant zu wissen welche Events da auftreten. Deswegen würde ich da einen Logger einsetzen um dies genau nachzuverfolgen.

    Folgende Fragen würde ich klären:

    • Wie schnell kommen die Events?
    • Wie lange dauert die Bearbeitung?
    • Wie reagiert deine Eventbehandlung unter Volllast?


  • @Finnegan sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Sicher?

    Da kommt ganz normal error: 'packed' was not declared in this scope

    @hustbaer sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Was meinst du mit Timer? Interrupt von einem Hardware-Timer?
    Und hast du mehrere Threads laufen?

    Nein, ich habe keine Threads am laufen.
    Nein ,keine Hardware Timer. Timing über die game loop. arest steht für attack rest. Quasi die Ausdauer.
    Nach einer Kombo wird arest gesetzt (für this, also den attacker) weil Ausdauer verbraucht wurde.

    Da aber post_interaction schon aus dem falschen Kontext her aufgerufen wurde und this schon auf den falchen Speicher zeigt, setzt er arest auch auf den angegriffen Charakter. Daher feuert cout mehrmals.

    Allerdings hat der angegriffene Charakter keine ITR's (Interaktionen) daher darf er hier:

    void Charakter::post_interaction()
    {
        const uint8_t ITR_Len = thisFrame.D->numOfItr;
        for(uint8_t i=0; i<ITR_Len; i++)
        {
    

    Schon gar nicht rein gehen.
    Der erste aufruf von if(attacked(gameObjectPtr->hit_impl(itr, &vol)))
    passt noch
    Danach sind alle Werte von den angegriffenen Charakter auch die der Angreift.

    Was noch passt, die Kobo geht von frame 70 bis Frame 73, danach muss auf frame 0 Gesprungen werden.
    Frame 72 setzt arest.
    Er springt aber nicht auf frame 0 weil zuvor in hit that->setFrame(180,21); aufgerufen wurde. Weil hit auch schon aus dem falschen Kontext aufgerufen wurde weil post_interaktion schon an falscher stelle ist.
    Das heißt beide Charakter bekommen zum Zeitpunkt x that->setFrame(180,21); aufgebrummt.

    Aber warum nur bei einer Combo???????

    @Quiche-Lorraine sagte in Code Ausführung ist durcheinader bei Superscalar CPU:

    Ein Combo war doch eine schnelle Kombination von Tastenkombinationen bzw. Schlägen. In Endeffekt eine schnelle Folge von Tastenevents.

    Ja richtig, eine Combo ist ein folge von Tastenkombinationen, die in der Richtigen zeit eingegeben werden müssen.
    Zum meinem jetzigen Zeitpunk habe ich nur zwei Kobo's: rennen angriff und drei mal Attacke, beides führt zu Frame 70.

    die Kombo wird ausgewertet kurz vor der Transition in Funktion transit:

    void LivingObject::transit()
    {
        if(childController)
        {
            combo_impl();
        }
        if(effect.timein < 0 && effect.stuck)
        {
            //stuck
        }
        else
        {
            //trans;
            trans(this);
        }
        effect.timein--;
        if(effect.timein < 0 && effect.stuck)
        {
            //stuck
        }
        else
        {
            state_update(-9);
        }
    }
    combo_impl(); ist wider eine virtuelle Funktion:
     ```cpp
    void combo_impl() override
        {
            combo_update();
        }
    
    und
    ```cpp
    void Charakter::combo_update()
    {
        uint16_t K = 0;
        if(comboBuffer)
        {
            K = comboBuffer->combo;
        }
    
        if(comboBuffer->combo && comboBuffer->combo == 67) //67 jump-att
        {
            K = 6;
        }
    
        int16_t tar1 = call(this, thisFrame.D->state, -3, K);
        int16_t tar2 = 0;
        if(tar1 <= 0)
        {
           tar2 = generic(this, -3, K);
        }
       call(this,  thisFrame.D->state, -4, 0);
       generic(this, -4, K);
    
        if(comboBuffer->combo && comboBuffer->combo == 67) //67 jump-att
        {
            if(tar1 || tar2)
            {
                comboBuffer->combo = 7;
            }
        }
        else
        {
            if(tar1 > 0 || tar2 > 0 || K == 1 || K == 2 ||  K ==3 || K==4 )
            {
                comboBuffer->combo = 0;
            }
        }
    }
    

    in combo_update wird wiederum generic aufgerufen und generic ruft post_interaction auf.

    int16_t Char_States::generic(Charakter *basePtr,int8_t e, uint16_t K)
    {
        switch(e)
        {
        case -1: //frame
        {
            if(basePtr->thisFrame.D->mp != 0)
            {
                if(mainCharFrames[basePtr->uID][basePtr->thisFrame.PN]->next == basePtr->thisFrame.N)
                {
                    if(basePtr->thisFrame.D->mp < 0)
                    {
                        if(!Engine::getInstance().F6_mode)
                        {
                            basePtr->health.mp += basePtr->thisFrame.D->mp;
                        }
                        basePtr->health.mp_usage -=  basePtr->thisFrame.D->mp;
                        if(basePtr->health.mp < 0)
                        {
                            basePtr->health.mp = 0;
                            basePtr->setFrame(basePtr->thisFrame.D->hit_d);
                        }
                    }
                }
                else
                {
                    const int16_t dmp = basePtr->thisFrame.D->mp % 1000;
                    const int16_t dhp = floor(basePtr->thisFrame.D->mp / 1000) * 10;//frame.d.Mp correct??
                    if(!Engine::getInstance().F6_mode)
                    {
                        basePtr->health.mp -= dmp;
                    }
                    basePtr->health.mp_usage += dmp;
                    basePtr->injury(dhp);
                }
            }
            //todo Opoint();
        }//frame
        break;
        case -5: //TU
        {
            if(!basePtr->state_update(-100))
            {
                basePtr->post_interaction();
            }
            if (basePtr->bdyBox.y == 0 && basePtr->bdyBox.vy == 0 && basePtr->thisFrame.N == 212 && basePtr->thisFrame.PN != 211)
            {
                basePtr->setFrame(999);
            }
            else if (basePtr->bdyBox.y == 0 && basePtr->bdyBox.vy > 0)   // fell onto ground
            {
                int16_t result = basePtr->state_update(-11);
                if (result > 0)
                {
                    basePtr->setFrame(result, 15);
                }
                else
                {
                    basePtr->bdyBox.vy = 0; // set to zero
                    basePtr->linear_friction(
                        utility.lookup_abs(GC.friction.fell, basePtr->bdyBox.vx),
                        utility.lookup_abs(GC.friction.fell, basePtr->bdyBox.vz)
                    );
                }
            }
            else if (basePtr->bdyBox.y + basePtr->bdyBox.vy >= 0 && basePtr->bdyBox.vy > 0)   // predict falling onto the ground
            {
                int16_t result = basePtr->state_update(-12);
                if (result > 0)
                {
                    basePtr->setFrame(result, 15);
                }
                else
                {
                    if (basePtr->getState() == 13)   // frozen
                    {
                        ; // do nothing
                    }
                    else if (basePtr->thisFrame.N == 212)     // jumping
                    {
                        basePtr->setFrame(215, 15); // crouch
                    }
                    else
                    {
                        basePtr->setFrame(219, 15); // crouch2
                    }
                }
            }
            // basePtr->health recover
            // http://lf2.wikia.com/wiki/Health_and_mana
            if (Engine::getInstance().timeT % 12 == 0)
            {
                if (basePtr->health.hp >= 0 && basePtr->health.hp < basePtr->health.hp_bound)
                {
                    basePtr->health.hp++;
                }
            }
    
            if (basePtr->health.hp >= 0 && basePtr->effect.heal && basePtr->effect.heal > 0)
            {
                if (Engine::getInstance().timeT % unsigned(basePtr->heal_speed) == 0)
                {
                    if (basePtr->health.hp + basePtr->heal_speed  <= basePtr->health.hp_bound)
                    {
                        basePtr->health.hp +=  basePtr->heal_speed;
                    }
                    basePtr->effect.heal -=  basePtr->heal_speed;
                }
            }
            if (Engine::getInstance().timeT % 3 == 0)
            {
                if (basePtr->health.mp < basePtr->health.mp_full)
                {
                    basePtr->health.mp += 1 + floor((basePtr->health.hp_full - (basePtr->health.hp < basePtr->health.hp_full ? basePtr->health.hp : basePtr->health.hp_full)) / 100);
                }
            }
            // recovery
            if (basePtr->health.fall > 0)
            {
                basePtr->health.fall += GC.recover.fall;
            }
            if (basePtr->health.bdefend > 0)
            {
                basePtr->health.bdefend += GC.recover.bdefend;
            }
            basePtr->comboBuffer->timeout--;
            if(basePtr->comboBuffer->timeout == 0)
            {
                if(K == 5 || K == 6  || K == 7 || K == 11 || K == 22 )
                {
                    basePtr->comboBuffer->combo = 0;
                }
            }
    
        }//TU
        break;
        case -9 : //transit
        {
            basePtr->dynamics();
        }
        break;
        }
        return 0;
    }//generic
    

    Die gesamte state machine umfasst über 100 states. das ist ein Riesen Baum an switch und if's.
    In der generic Funktion wird auch Energie hinzugefügt basePtr->health.hp += basePtr->heal_speed;
    wie zuvoor erwähnt wirkt sich eien ineraktion auf beide Charaktere aus.

    Villeicht gibt es auch ein Problem mit Mehrfachvererbung
    class Charakter : public LivingObject, protected Char_States. Weil, health ist ein struct in LivingObject.


Anmelden zum Antworten