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_StatesIn 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: 0x3173b0Die 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
einset
(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 KostantenNUM_CHILD_CONTROLLER
zu laufen. Und macht dann immer einen check aufnullptr
. 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: 0x5555561dcf18Das 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 ichfloat 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 gestorbenSicher? 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 nachstruct
/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 nemshort*
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?