AIMBOT 2.0
In Episode 1 von New Game 2, gegen 9:40 Uhr, gibt es eine Aufnahme des Codes, den Nene geschrieben hat:
Hier ist es in Textform mit den übersetzten Kommentaren:
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); } } }
Nach der Aufnahme sagte Umiko und zeigte auf die for-Schleife. Der Grund für den Absturz des Codes sei, dass es eine Endlosschleife gibt.
Ich kenne C ++ nicht wirklich, daher bin ich mir nicht sicher, ob das, was sie sagt, wahr ist.
Soweit ich sehen kann, durchläuft die for-Schleife nur die Debufs, die der Schauspieler derzeit hat. Wenn der Schauspieler nicht unendlich viele Debufs hat, denke ich nicht, dass es möglicherweise zu einer Endlosschleife werden kann.
Aber ich bin mir nicht sicher, denn der einzige Grund, warum es eine Aufnahme des Codes gibt, ist, dass sie hier ein Osterei legen wollten, oder? Wir hätten gerade eine Aufnahme von der Rückseite des Laptops bekommen und Umiko sagen hören "Oh, du hast dort eine Endlosschleife". Die Tatsache, dass sie tatsächlich Code zeigten, lässt mich denken, dass der Code irgendwie ein Osterei ist.
Wird der Code tatsächlich eine Endlosschleife erzeugen?
8- Wahrscheinlich hilfreich: zusätzlicher Screenshot von Umiko mit den Worten: "Es war Aufruf der gleichen Operation immer und immer wieder ", was im Code möglicherweise nicht angezeigt wird.
- Oh! Das wusste ich nicht! @AkiTanaka das U-Boot, das ich gesehen habe, sagt "Endlosschleife"
- @LoganM Ich stimme nicht wirklich zu. Es ist nicht nur so, dass OP eine Frage zu einem Quellcode hat, der zufällig aus einem Anime stammt. Die Frage von OP bezieht sich auf eine bestimmte Aussage Über den Quellcode durch ein Zeichen im Anime, und es gibt eine anime-bezogene Antwort, nämlich "Crunchyroll hat die Zeile vermasselt und falsch übersetzt".
- @senshin Ich denke, Sie lesen, worum es bei der Frage gehen soll, und nicht, was tatsächlich gestellt wird. Die Frage enthält einen Quellcode und fragt, ob eine Endlosschleife als realer C ++ - Code generiert wird. Neues Spiel! ist eine fiktive Arbeit; Es ist nicht erforderlich, dass der darin enthaltene Code den tatsächlichen Standards entspricht. Was Umiko über den Code sagt, ist maßgeblicher als alle C ++ - Standards oder Compiler. In der obersten (akzeptierten) Antwort werden keine Informationen im Universum erwähnt. Ich denke, eine themenbezogene Frage könnte mit einer guten Antwort dazu gestellt werden, aber wie formuliert ist dies nicht der Fall.
Der Code ist keine Endlosschleife, aber ein Fehler.
Es gibt zwei (möglicherweise drei) Probleme:
- Wenn keine Debufs vorhanden sind, wird überhaupt kein Schaden angerichtet
- Übermäßiger Schaden wird angewendet, wenn mehr als 1 Debuf vorhanden ist
- Wenn DestroyMe () das Objekt sofort löscht und noch m_debufs zu verarbeiten sind, wird die Schleife über ein gelöschtes Objekt ausgeführt und den Speicher verworfen. Die meisten Spiele-Engines haben eine Zerstörungswarteschlange, um dies zu umgehen, und dies ist möglicherweise kein Problem.
Die Anwendung von Schaden sollte außerhalb der Schleife erfolgen.
Hier ist die korrigierte Funktion:
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); } m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); } }
12 - 15 Sind wir auf Code Review? : D.
- 4 Schwimmer sind gut für die Gesundheit, wenn Sie 16777216 HP nicht überschreiten. Sie können sogar die Gesundheit auf unendlich einstellen, um einen Feind zu erschaffen, den Sie treffen können, der aber nicht stirbt, und einen One-Kill-Angriff mit unendlichem Schaden ausführen, der einen unendlichen HP-Charakter immer noch nicht tötet (Ergebnis von INF-INF ist NaN) wird alles andere töten. Es ist also sehr nützlich.
- 1 @cat Gemäß Konvention in vielen Codierungsstandards die
m_
Präfix bedeutet, dass es sich um eine Mitgliedsvariable handelt. In diesem Fall eine Mitgliedsvariable vonDestructibleActor
. - 2 @HotelCalifornia Ich stimme zu, dass es eine kleine Chance gibt
ApplyToDamage
funktioniert nicht wie erwartet, aber im Beispielfall würde ich sagenApplyToDamage
ebenfalls muss überarbeitet werden, damit das Original übergeben werden kannsourceDamage
auch, damit es das Debuf in diesen Fällen richtig berechnen kann. Um ein absoluter Pedant zu sein: Zu diesem Zeitpunkt sollten die dmg-Informationen eine Struktur sein, die das ursprüngliche dmg, das aktuelle dmg und die Art des Schadens (der Schäden) enthält, auch wenn die Debufs Dinge wie "Brandanfälligkeit" aufweisen. Aus Erfahrung dauert es nicht lange, bis ein Spieldesign mit Debufs diese verlangt. - 1 @StephaneHockenhull gut gesagt!
Der Code scheint keine Endlosschleife zu erzeugen.
Die Schleife wäre nur dann unendlich, wenn
debuf.ApplyToDamage(resolvedDamage);
oder
DestroyMe();
sollten neue Elemente zum hinzufügen m_debufs
Container.
Dies scheint unwahrscheinlich. In diesem Fall könnte das Programm abstürzen, weil der Container während der Iteration geändert wird.
Das Programm würde höchstwahrscheinlich aufgrund des Aufrufs von abstürzen DestroyMe();
Dies zerstört vermutlich das aktuelle Objekt, auf dem die Schleife gerade ausgeführt wird.
Wir können uns das als den Cartoon vorstellen, in dem der "Böse" einen Ast sägt, damit der "Gute" damit fällt, aber zu spät merkt, dass er auf der falschen Seite des Schnitts ist. Oder die Midgaard-Schlange frisst ihren eigenen Schwanz.
Ich sollte auch hinzufügen, dass das häufigste Symptom einer Endlosschleife darin besteht, dass das Programm eingefroren wird oder nicht reagiert. Das Programm stürzt ab, wenn es wiederholt Speicher zuweist oder etwas tut, das durch Null geteilt wird, oder ähnliches.
Basierend auf dem Kommentar von Aki Tanaka,
Wahrscheinlich hilfreich: Zusätzlicher Screenshot von Umiko mit der Aufschrift "Es wurde immer wieder dieselbe Operation aufgerufen", der möglicherweise nicht im Code angezeigt wird.
"Es wurde immer wieder dieselbe Operation aufgerufen" Dies ist wahrscheinlicher.
Vorausgesetzt, dass DestroyMe();
ist nicht dafür ausgelegt, mehrmals aufgerufen zu werden, es ist wahrscheinlicher, dass es zu einem Absturz kommt.
Eine Möglichkeit, dieses Problem zu beheben, besteht darin, das zu ändern if
für so etwas:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
Dies würde die Schleife verlassen, wenn der DestructibleActor zerstört wird, und sicherstellen, dass 1) der DestroyMe
Die Methode wird nur einmal aufgerufen und 2) Buffs nicht nutzlos anwenden, wenn das Objekt bereits als tot betrachtet wird.
- 1 Aus der for-Schleife auszubrechen, wenn health <= 0 ist, ist definitiv eine bessere Lösung, als bis nach der Schleife zu warten, um den Zustand zu überprüfen.
- Ich denke ich würde wahrscheinlich
break
aus der Schleife und dann AnrufDestroyMe()
, nur um sicher zu gehen
Es gibt verschiedene Probleme mit dem Code:
- Wenn es keine Debufs gibt, wird kein Schaden genommen.
DestroyMe()
Funktionsname klingt gefährlich. Je nachdem, wie es implementiert ist, kann es ein Problem sein oder auch nicht. Wenn es sich nur um einen Aufruf des Destruktors des aktuellen Objekts handelt, das in eine Funktion eingeschlossen ist, liegt ein Problem vor, da das Objekt während der Ausführung des Codes zerstört wird. Wenn es sich um einen Aufruf einer Funktion handelt, die das Löschereignis des aktuellen Objekts in die Warteschlange stellt, gibt es kein Problem, da das Objekt zerstört wird, nachdem es seine Ausführung abgeschlossen hat und die Ereignisschleife beginnt.- Das eigentliche Problem, das im Anime erwähnt zu werden scheint, ist "Es wurde immer wieder dieselbe Operation aufgerufen" - es wird aufgerufen
DestroyMe()
so lange wiem_currentHealth <= 0.f
und es sind noch mehr Debuffs zu iterieren, was dazu führen kannDestroyMe()
immer wieder aufgerufen werden. Die Schleife sollte nach der ersten anhaltenDestroyMe()
Aufruf, da das mehrmalige Löschen eines Objekts zu einer Speicherbeschädigung führt, die auf lange Sicht wahrscheinlich zu einem Absturz führen wird.
Ich bin mir nicht sicher, warum jedes Debuf die Gesundheit wegnimmt, anstatt die Gesundheit nur einmal wegzunehmen, wobei die Auswirkungen aller Debuffs auf den erlittenen Anfangsschaden angewendet werden, aber ich gehe davon aus, dass dies die richtige Spiellogik ist.
Der richtige Code wäre
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; } } }
3 - Ich sollte darauf hinweisen, dass das Löschen desselben Speichers kein Problem sein muss, da ich in der Vergangenheit Speicherzuordnungen geschrieben habe. Es könnte auch redundant sein. Es hängt alles vom Verhalten des Allokators ab. Meins hat sich einfach wie eine verknüpfte Liste auf niedriger Ebene verhalten, sodass der "Knoten" für die gelöschten Daten entweder mehrmals als frei gesetzt oder mehrmals neu gelöscht wird (was nur redundanten Zeigerumleitungen entsprechen würde). Guter Fang.
- Double-Free ist ein Fehler und führt im Allgemeinen zu undefiniertem Verhalten und Abstürzen. Selbst wenn Sie einen benutzerdefinierten Allokator haben, der die Wiederverwendung derselben Speicheradresse irgendwie nicht zulässt, ist Double-Free ein stinkender Code, da er keinen Sinn ergibt und Sie von statischen Code-Analysatoren angeschrien werden.
- Na sicher! Ich habe es nicht für diesen Zweck entworfen. Einige Sprachen erfordern aufgrund fehlender Funktionen lediglich einen Allokator. Nein nein Nein. Ich habe nur gesagt, dass ein Absturz nicht garantiert ist. Bestimmte Designklassifizierungen stürzen nicht immer ab.