Anonim

AIMBOT 2.0

In aflevering 1 van New Game 2, rond 9:40, is er een shot van de code die Nene heeft geschreven:

Hier is het in tekstvorm met de vertaalde opmerkingen:

// 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(); } } } 

Na de opname zei Umiko, wijzend naar de for-lus, dat de reden waarom de code crashte, is dat er een oneindige lus is.

Ik ken C ++ niet echt, dus ik weet niet zeker of wat ze zegt waar is.

Van wat ik kan zien, itereert de for-lus gewoon door de debufs die de Actor momenteel heeft. Tenzij de acteur een oneindig aantal debufs heeft, denk ik niet dat het een oneindige lus kan worden.

Maar ik weet het niet zeker, want de enige reden dat er een shot van de code is, is dat ze hier een paasei wilden plaatsen, toch? We zouden net een foto van de achterkant van de laptop hebben gemaakt en Umiko horen zeggen: "Oh, je hebt daar een oneindige lus". Het feit dat ze daadwerkelijk code lieten zien, doet me denken dat de code op de een of andere manier een soort paasei is.

Zal de code daadwerkelijk een oneindige lus creëren?

8
  • Waarschijnlijk nuttig: extra screenshot van Umiko die zegt: "Het was dezelfde operatie aanroepen keer op keer ', wat mogelijk niet in de code wordt weergegeven.
  • Oh! Dat wist ik niet! @AkiTanaka de sub die ik heb bekeken, zegt 'oneindige lus'
  • @LoganM Ik ben het er niet echt mee eens. Het is niet alleen dat OP een vraag heeft over een of andere broncode die toevallig uit een anime kwam; OP's vraag gaat over een bepaalde uitspraak over de broncode door een personage in de anime, en er is een anime-gerelateerd antwoord, namelijk "Crunchyroll is misleid en heeft de regel verkeerd vertaald".
  • @senshin Ik denk dat je leest waarover je wilt dat de vraag gaat, in plaats van wat er werkelijk wordt gesteld. De vraag geeft wat broncode en vraagt ​​of het een oneindige lus genereert als echte C ++ - code. Nieuw spel! is een fictief werk; er is geen behoefte aan code die erin wordt gepresenteerd om te voldoen aan real-life standaarden. Wat Umiko zegt over de code is gezaghebbend dan welke C ++ - standaarden of compilers dan ook. Het bovenste (geaccepteerde) antwoord bevat geen vermelding van informatie in de universe. Ik denk dat hier een on-topic vraag over kan worden gesteld met een goed antwoord, maar zoals geformuleerd is dit het niet.

De code is geen oneindige lus, maar het is een bug.

Er zijn twee (mogelijk drie) problemen:

  • Als er geen debufs aanwezig zijn, wordt er helemaal geen schade toegepast
  • Overmatige schade wordt toegepast als er meer dan 1 debuf is
  • Als DestroyMe () het object onmiddellijk verwijdert en er zijn nog steeds m_debufs die moeten worden verwerkt, wordt de lus uitgevoerd over een verwijderd object en het prullenbakgeheugen. De meeste game-engines hebben een vernietigingswachtrij om dit en meer te omzeilen, dus dat is misschien geen probleem.

Het aanbrengen van schade moet buiten de lus vallen.

Hier is de gecorrigeerde functie:

// 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 Zijn we bezig met Code Review? : D
  • 4 drijvers zijn geweldig voor de gezondheid als je niet boven de 16777216 pk komt. Je kunt zelfs de gezondheid instellen op oneindig om een ​​vijand te creëren die je kunt raken maar niet doodgaat, en een one-kill-aanval uitvoeren met oneindige schade die nog steeds geen oneindig HP-personage zal doden (het resultaat van INF-INF is NaN) maar zal al het andere doden. Het is dus erg handig.
  • 1 @cat Volgens afspraak in veel coderingsstandaarden het m_ prefix betekent dat het een lidvariabele is. In dit geval een lidvariabele van DestructibleActor.
  • 2 @HotelCalifornia Ik ben het ermee eens dat er een kleine kans is ApplyToDamage werkt niet zoals verwacht maar in het voorbeeldgeval dat u geeft zou ik zeggen ApplyToDamage ook moet worden herwerkt om het origineel door te geven sourceDamage zodat het in die gevallen de debuf correct kan berekenen. Om een ​​absolute pedant te zijn: op dit punt moet de dmg-informatie een structuur zijn die de originele dmg, de huidige dmg en de aard van de schade (s) bevat, ook als de debufs dingen hebben als "kwetsbaarheid voor vuur". Uit ervaring duurt het niet lang voordat elk game-ontwerp met debufs hierom vraagt.
  • 1 @StephaneHockenhull goed gezegd!

De code lijkt geen oneindige lus te creëren.

De enige manier waarop de lus oneindig zou zijn, zou zijn als

debuf.ApplyToDamage(resolvedDamage); 

of

DestroyMe(); 

waren om nieuwe items toe te voegen aan de m_debufs container.

Dit lijkt onwaarschijnlijk. En als dat het geval was, zou het programma kunnen crashen door het wijzigen van de container terwijl het wordt herhaald.

Het programma zou hoogstwaarschijnlijk crashen vanwege de aanroep naar DestroyMe(); die vermoedelijk het huidige object vernietigt dat momenteel de lus uitvoert.

We kunnen het zien als de tekenfilm waarin de 'slechterik' een tak zaagt om de 'goede man' ermee te laten vallen, maar beseft te laat dat hij aan de verkeerde kant van de snee staat. Of de Midgaard-slang die zijn eigen staart eet.


Ik moet ook toevoegen dat het meest voorkomende symptoom van een oneindige lus is dat het programma vastloopt of niet meer reageert. Het zal het programma laten crashen als het herhaaldelijk geheugen toewijst, of iets doet dat uiteindelijk door nul wordt gedeeld, of iets dergelijks.


Op basis van de opmerking van Aki Tanaka,

Waarschijnlijk nuttig: extra screenshot van Umiko die zegt dat "Het dezelfde operatie steeds opnieuw aanriep", die misschien niet in de code wordt weergegeven.

"Het riep steeds weer dezelfde operatie op" Dit is waarschijnlijker.

In de veronderstelling dat DestroyMe(); is niet ontworpen om meer dan één keer te worden gebeld, het is waarschijnlijker dat het een crash veroorzaakt.

Een manier om dit probleem op te lossen, is door het if voor zoiets als dit:

 if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; } 

Dit zou de lus verlaten wanneer de DestructibleActor wordt vernietigd, en ervoor zorgen dat 1) de DestroyMe methode wordt slechts één keer aangeroepen en 2) pas buffs niet nutteloos toe als het object al als dood wordt beschouwd.

2
  • 1 De for-lus doorbreken als health <= 0 is zeker een betere oplossing dan wachten tot na de lus om de gezondheid te controleren.
  • Ik denk dat ik het waarschijnlijk zou doen break uit de lus, en vervolgens bellen DestroyMe(), gewoon voor de veiligheid

Er zijn verschillende problemen met de code:

  1. Als er geen debufs zijn, wordt er geen schade opgelopen.
  2. DestroyMe() functienaam klinkt gevaarlijk. Afhankelijk van hoe het is geïmplementeerd, kan dit een probleem zijn. Als het slechts een aanroep is naar de destructor van het huidige object verpakt in een functie, dan is er een probleem, aangezien het object zou worden vernietigd tijdens het uitvoeren van code. Als het een aanroep is naar een functie die de verwijderingsgebeurtenis van het huidige object in de wachtrij plaatst, is er geen probleem, omdat het object zou worden vernietigd nadat het de uitvoering ervan heeft voltooid en de gebeurtenislus begint.
  3. Het eigenlijke probleem dat in de anime genoemd lijkt te worden, de "Het riep dezelfde operatie keer op keer" - het zal bellen DestroyMe() zolang m_currentHealth <= 0.f en er zijn meer debuffs die moeten worden herhaald, wat kan resulteren in DestroyMe() meerdere keren gebeld worden, keer op keer. De lus moet na de eerste stoppen DestroyMe() call, omdat het meer dan eens verwijderen van een object resulteert in geheugenbeschadiging, wat op de lange termijn waarschijnlijk zal resulteren in een crash.

Ik weet niet precies waarom elke debuf de gezondheid wegneemt, in plaats van dat de gezondheid maar één keer wordt weggenomen, waarbij de effecten van alle debuffs worden toegepast op de aanvankelijke schade, maar ik neem aan dat dit de juiste spellogica is.

De juiste code zou zijn

// 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
  • Ik moet erop wijzen dat, aangezien ik in het verleden geheugentoewijzers heb geschreven, het verwijderen van hetzelfde geheugen geen probleem hoeft te zijn. Het kan ook overbodig zijn. Het hangt allemaal af van het gedrag van de allocator. De mijne fungeerde gewoon als een gekoppelde lijst op laag niveau, zodat het "knooppunt" voor de verwijderde gegevens ofwel meerdere keren als vrij wordt gesteld of meerdere keren opnieuw wordt verwijderd (wat gewoon zou overeenkomen met overtollige aanwijzeromleidingen). Maar goede vangst.
  • Dubbelvrij is een bug en leidt over het algemeen tot ongedefinieerd gedrag en crashes. Zelfs als je een aangepaste allocator hebt die op de een of andere manier hergebruik van hetzelfde geheugenadres niet toestaat, is dubbelvrij een stinkende code omdat het nergens op slaat en er naar je wordt geschreeuwd door statische code-analysers.
  • Natuurlijk! Ik heb het niet voor dat doel ontworpen. Sommige talen hebben alleen een allocator nodig vanwege een gebrek aan functies. Nee nee nee. Ik zei alleen maar dat een crash niet gegarandeerd is. Bepaalde ontwerpclassificaties crashen niet altijd.