AIMBOT 2.0
V epizodě 1 Nové hry 2, kolem 9:40, je výstřel kódu, který Nene napsala:
Zde je to v textové podobě s přeloženými komentáři:
// 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(); } } }
Po výstřelu Umiko ukázal na smyčku for a řekl, že důvodem zhroucení kódu je to, že existuje nekonečná smyčka.
C ++ opravdu nevím, takže si nejsem jistý, jestli to, co říká, je pravda.
Z toho, co vidím, smyčka for právě iteruje přes debufs, které aktuálně má herec. Pokud herec nemá nekonečné množství ladění, nemyslím si, že by se mohl stát nekonečnou smyčkou.
Ale nejsem si jistý, protože jediným důvodem, proč existuje výstřel kódu, je to, že sem chtěli dát velikonoční vajíčko, že? Právě bychom dostali výstřel do zadní části notebooku a slyšeli jsme, jak Umiko říká „Ach, máš tam nekonečnou smyčku“. Skutečnost, že ve skutečnosti ukázali nějaký kód, mě nutí myslet si, že kód je nějakým způsobem velikonoční vajíčko.
Vytvoří kód ve skutečnosti nekonečnou smyčku?
8- Pravděpodobně užitečné: další snímek obrazovky s Umikem, který říká: „Bylo volání stejné operace znovu a znovu “, což se v kódu nemusí zobrazit.
- Ach! To jsem nevěděl! @AkiTanaka sub, který jsem sledoval, říká „nekonečná smyčka“
- @LoganM opravdu nesouhlasím. Nejde jen o to, že OP má otázku ohledně nějakého zdrojového kódu, který náhodou pochází z anime; Otázka OP se týká konkrétního učiněného prohlášení o zdrojový kód znakem v anime a existuje odpověď související s anime, konkrétně „Crunchyroll udělal falešnou zprávu a nesprávně přeložil řádek“.
- @senshin Myslím, že čtete, o čem chcete, aby otázka byla, spíše než to, na co se ve skutečnosti ptáte. Tato otázka poskytuje nějaký zdrojový kód a ptá se, zda generuje nekonečnou smyčku jako reálný C ++ kód. Nová hra! je fiktivní dílo; není nutné, aby v něm uvedený kód odpovídal standardům reálného života. To, co Umiko říká o kódu, je autoritativnější než jakékoli standardy nebo kompilátory C ++. Nejlepší (přijatá) odpověď neobsahuje žádnou zmínku o informacích ve vesmíru. Myslím, že na tuto otázku lze položit dobrou otázku s dobrou odpovědí, ale ve větě to tak není.
Kód není nekonečná smyčka, ale je to chyba.
Existují dva (možná tři) problémy:
- Pokud nejsou k dispozici žádné ladicí programy, nebude způsobeno žádné poškození
- Pokud dojde k více než 1 debufům, dojde k nadměrnému poškození
- Pokud DestroyMe () objekt okamžitě odstraní a stále existuje m_debufs ke zpracování, smyčka bude spuštěna přes odstraněný objekt a trasuje paměť. Většina herních enginů má frontu pro zničení, která toto a další obejde, aby to nemusel být problém.
Aplikace poškození by měla být mimo smyčku.
Zde je opravená funkce:
// 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 Jsme na kontrole kódu? : D
- 4 plováky jsou skvělé pro zdraví, pokud nepřekročíte 16777216 HP. Můžete dokonce nastavit zdraví na nekonečno, abyste vytvořili nepřítele, kterého můžete zasáhnout, ale nezemřete, a mít útok jednoho zabití s použitím nekonečného poškození, které stále nezabije nekonečný znak HP (výsledek INF-INF je NaN), ale zabije všechno ostatní. Je to tedy velmi užitečné.
- 1 @cat Podle konvence v mnoha standardech kódování
m_
prefix znamená, že jde o členskou proměnnou. V tomto případě členské proměnnéDestructibleActor
. - 2 @HotelCalifornia Souhlasím, že je malá šance
ApplyToDamage
nefunguje podle očekávání, ale v příkladu, který uvedete, bych řeklApplyToDamage
taky je třeba přepracovat, aby vyžadoval předání originálusourceDamage
stejně tak, aby v těchto případech mohl správně vypočítat debuf. Chcete-li být absolutním pedantem: v tomto okamžiku by informace dmg měly být strukturou, která zahrnuje původní dmg, aktuální dmg a povahu poškození (poškození), pokud mají ladicí programy například „zranitelnost vůči ohni“. Ze zkušenosti to není dlouho předtím, než je bude vyžadovat jakýkoli design hry s debufy. - 1 @StephaneHockenhull dobře řečeno!
Zdá se, že kód nevytváří nekonečnou smyčku.
Jediný způsob, jak by smyčka byla nekonečná, by byla, kdyby
debuf.ApplyToDamage(resolvedDamage);
nebo
DestroyMe();
bylo přidat nové položky do m_debufs
kontejner.
To se zdá nepravděpodobné. A pokud by tomu tak bylo, program by mohl selhat kvůli změně kontejneru při iteraci.
Program by s největší pravděpodobností selhal kvůli volání na DestroyMe();
což pravděpodobně zničí aktuální objekt, který aktuálně běží smyčku.
Můžeme si to představit jako karikaturu, kde „zlý člověk“ uvidí větev, aby s ní „dobrý člověk“ spadl, ale příliš pozdě si uvědomí, že je na špatné straně řezu. Nebo had Midgaard, který jí svůj vlastní ocas.
Měl bych také dodat, že nejběžnějším příznakem nekonečné smyčky je to, že program zamrzne nebo nebude reagovat. Program havaruje, pokud opakovaně přiděluje paměť nebo dělá něco, co skončí vydělením nulou nebo lajky.
Na základě komentáře Aki Tanaka,
Pravděpodobně užitečné: další snímek obrazovky Umika, který říká, že „volala pořád stejnou operaci znovu a znovu“, což se v kódu nemusí zobrazit.
„Znovu a znovu volala stejnou operaci.“ To je pravděpodobnější.
Za předpokladu, že DestroyMe();
není navržen tak, aby byl volán více než jednou, je větší pravděpodobnost, že způsobí selhání.
Tento problém lze vyřešit změnou souboru if
pro něco takového:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
To by ukončilo smyčku, když je DestructibleActor zničen, a ujistěte se, že 1) DestroyMe
metoda je volána pouze jednou a 2) nepoužívejte buffy zbytečně, jakmile je objekt již považován za mrtvý.
- 1 Vyřazení ze smyčky for, když je zdraví <= 0 rozhodně lepší oprava než čekání na kontrolu stavu po smyčce.
- Myslím, že bych asi
break
ze smyčky a pak voláníDestroyMe()
, jen pro jistotu
S kódem je několik problémů:
- Pokud neexistují žádné ladicí programy, nedošlo by k žádnému poškození.
DestroyMe()
název funkce zní nebezpečně. V závislosti na tom, jak je implementován, může nebo nemusí být problém. Pokud se jedná pouze o volání destruktoru aktuálního objektu zabaleného ve funkci, pak nastává problém, protože objekt by byl zničen uprostřed jeho provádění kódu. Pokud se jedná o volání funkce, která zařadí frontu na událost odstranění aktuálního objektu, pak není problém, protože objekt by byl zničen poté, co dokončí své provedení a nastane smyčka události.- Skutečný problém, který se zdá být zmíněn v anime, „Volal stejnou operaci znovu a znovu“ - zavolá
DestroyMe()
tak dlouho jakm_currentHealth <= 0.f
a k iteraci zbývá více debuffů, což může mít za následekDestroyMe()
být volán vícekrát, znovu a znovu. Smyčka by se měla zastavit po prvníDestroyMe()
volání, protože smazání objektu vícekrát má za následek poškození paměti, což pravděpodobně z dlouhodobého hlediska povede ke zhroucení.
Nejsem si úplně jistý, proč každý debuf odnáší zdraví, místo aby byl odňat pouze jednou, s účinky všech debuffů aplikovaných na počáteční poškození, ale předpokládám, že je to správná logika hry.
Správný kód by byl
// 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 - Měl bych zdůraznit, že jak jsem v minulosti psal alokátory paměti, vymazání stejné paměti nemusí být problém. Mohlo by to být také nadbytečné. Vše závisí na chování alokátora. Důl fungoval jako nízkoúrovňový propojený seznam, takže „uzel“ odstraněných dat se buď několikrát nastaví jako volný, nebo se několikrát znovu smaže (což by odpovídalo pouze redundantnímu přesměrování ukazatele). Dobrý úlovek.
- Double-free je chyba a obvykle vede k nedefinovanému chování a pádům. Dokonce i když máte vlastní alokátor, který nějakým způsobem zakazuje opětovné použití stejné adresy paměti, double-free je páchnoucí kód, protože to nedává smysl a budete na něj křičet statickými analyzátory kódu.
- Samozřejmě! Za tímto účelem jsem to nenavrhl. Některé jazyky kvůli nedostatku funkcí vyžadují pouze alokátor. Ne ne ne. Jen jsem konstatoval, že havárie není zaručena. Určité klasifikace designu nemusí vždy selhat.