AIMBOT 2.0
I afsnit 1 af New Game 2, omkring 9:40, er der et skud af koden, som Nene har skrevet:
Her er det i tekstform med oversatte kommentarer:
// 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(); } } }
Efter skuddet sagde Umiko, der pegede på for-løkken, at grunden til, at koden styrtede ned, var, at der er en uendelig løkke.
Jeg kender ikke rigtig C ++, så jeg er ikke sikker på, om det, hun siger, er sandt.
Fra hvad jeg kan se, gentages for-sløjfen bare gennem de debufs, som skuespilleren i øjeblikket har. Medmindre skuespilleren har en uendelig mængde debufs, tror jeg ikke, det muligvis kan blive en uendelig løkke.
Men jeg er ikke sikker, for den eneste grund til, at der er et skud af koden, er, at de ville lægge et påskeæg her, ikke? Vi ville lige have fået et skud på bagsiden af den bærbare computer og hørt Umiko sige "Åh, du har en uendelig løkke der". Det faktum, at de faktisk viste noget kode, får mig til at tænke, at koden på en eller anden måde er et påskeæg af en eller anden slags.
Vil koden faktisk skabe en uendelig løkke?
8- Sandsynligvis nyttigt: yderligere skærmbillede af Umiko, der siger, at "Det var kalder den samme operation igen og igen ", som muligvis ikke vises i koden.
- Åh! Det vidste jeg ikke! @AkiTanaka den sub, som jeg så, siger "uendelig løkke"
- @LoganM Jeg er ikke rigtig enig. Det er ikke kun, at OP har et spørgsmål om en kildekode, der tilfældigvis kommer fra en anime; OPs spørgsmål handler om en bestemt erklæring om kildekoden med et tegn i anime, og der er et anime-relateret svar, nemlig "Crunchyroll gjort fedtet og forkert oversat linjen".
- @senshin Jeg tror, du læser, hvad du vil have, at spørgsmålet handler om, snarere end hvad der faktisk bliver stillet. Spørgsmålet giver nogle kildekoder og spørger, om det genererer en uendelig løkke som den virkelige C ++ - kode. Nyt spil! er et fiktivt værk; der er ikke behov for kode, der præsenteres i den for at overholde de virkelige standarder. Hvad Umiko siger om koden er mere autoritativ end nogen C ++ - standarder eller kompilatorer. Det øverste (accepterede) svar indeholder ingen omtale af oplysninger i universet. Jeg tror, et spørgsmål om emnet kunne stilles om dette med et godt svar, men som formuleret er det ikke det.
Koden er ikke en uendelig løkke, men den er en fejl.
Der er to (muligvis tre) problemer:
- Hvis der ikke er debufs, overhovedet ikke beskadiges
- Overdreven skade vil blive anvendt, hvis der er mere end 1 debuf
- Hvis DestroyMe () straks sletter objektet, og der stadig er m_debufs, der skal behandles, udføres sløjfen over et slettet objekt og papirkurven. De fleste spilmotorer har en ødelæggelseskø til at omgå dette og mere, så det er måske ikke et problem.
Anvendelsen af skader skal være uden for sløjfen.
Her er den korrigerede 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 Er vi i kodevaluering? : D
- 4 floats er gode til helbredet, hvis du ikke går over 16777216 HP. Du kan endda indstille helbredet til uendelig for at skabe en fjende, du kan ramme, men ikke vil dø, og have et one-kill-angreb ved hjælp af uendelig skade, der stadig ikke dræber en uendelig HP-karakter (resultatet af INF-INF er NaN), men vil dræbe alt andet. Så det er meget nyttigt.
- 1 @cat Efter konvention i mange kodningsstandarder
m_
præfiks betyder, at det er en medlemsvariabel. I dette tilfælde en medlemsvariabel påDestructibleActor
. - 2 @HotelCalifornia Jeg er enig i, at der er en lille chance
ApplyToDamage
fungerer ikke som forventet, men i det eksempel du giver, vil jeg sigeApplyToDamage
også skal bearbejdes for at kræve, at den sendes originalensourceDamage
også så det kan beregne debuf korrekt i disse tilfælde. For at være en absolut pedant: på dette tidspunkt skal dmg-informationen være en struktur, der inkluderer den oprindelige dmg, aktuelle dmg og arten af skaden (e) også, hvis debufs har ting som "sårbarhed over for ild". Erfaringsmæssigt tager det ikke længe, før ethvert spildesign med debufs kræver disse. - 1 @StephaneHockenhull godt sagt!
Koden ser ikke ud til at skabe en uendelig løkke.
Den eneste måde, hvorpå sløjfen ville være uendelig, ville være, hvis
debuf.ApplyToDamage(resolvedDamage);
eller
DestroyMe();
skulle tilføje nye ting til m_debufs
beholder.
Dette synes usandsynligt. Og hvis det var tilfældet, kunne programmet gå ned på grund af skift af container, mens det blev gentaget.
Programmet vil sandsynligvis gå ned på grund af opkaldet til DestroyMe();
som formodentlig ødelægger det aktuelle objekt, der i øjeblikket kører løkken.
Vi kan tænke på det som tegneserien, hvor den 'dårlige fyr' savner en gren for at få den 'gode fyr' til at falde med, men indser for sent, at han er på den forkerte side af snittet. Eller Midgaard Snake, der spiser sin egen hale.
Jeg skal også tilføje, at det mest almindelige symptom på en uendelig løkke er, at det fryser programmet eller gør det ikke lydhørt. Det styrter programmet, hvis det tildeler hukommelse gentagne gange eller gør noget, der ender med at dividere med nul eller lignende.
Baseret på kommentaren fra Aki Tanaka,
Sandsynligvis nyttigt: yderligere skærmbillede af Umiko, der siger, at "Det kaldte den samme handling igen og igen", som muligvis ikke vises i koden.
"Det kaldte den samme operation igen og igen" Dette er mere sandsynligt.
Antages det DestroyMe();
er ikke designet til at kaldes mere end én gang, det er mere sandsynligt, at det forårsager et nedbrud.
En måde at løse dette problem på ville være at ændre if
til noget som dette:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
Dette vil forlade sløjfen, når DestructibleActor ødelægges og sørge for, at 1) DestroyMe
metoden kaldes kun en gang og 2) anvend ikke buffs ubrugeligt, når objektet allerede betragtes som død.
- 1 At bryde ud af for-sløjfen, når sundhed <= 0 er bestemt en bedre løsning end at vente til efter sløjfen for at kontrollere helbredet.
- Jeg tror sandsynligvis
break
ud af sløjfen, og derefter opkaldDestroyMe()
bare for at være sikker
Der er flere problemer med koden:
- Hvis der ikke er debufs, vil der ikke blive taget skade.
DestroyMe()
funktionsnavn lyder farligt. Afhængigt af hvordan det implementeres, er det muligvis et problem. Hvis det bare er et opkald til destruktøren af det aktuelle objekt indpakket i en funktion, så er der et problem, da objektet ville blive ødelagt midt i det at udføre kode. Hvis det er et opkald til en funktion, der sætter sletningen af det aktuelle objekt i kø, er der ikke noget problem, da objektet ville blive ødelagt, når det har fuldført dets udførelse, og begivenhedsløbet sparker ind.- Det egentlige problem, der synes at være nævnt i anime, "Det kaldte den samme operation igen og igen" - det vil kalde
DestroyMe()
så længem_currentHealth <= 0.f
og der er flere debuffs tilbage til at gentage, hvilket kan resultere iDestroyMe()
bliver kaldt flere gange, igen og igen. Sløjfen skal stoppe efter den førsteDestroyMe()
opkald, fordi sletning af et objekt mere end en gang resulterer i hukommelseskorruption, hvilket sandsynligvis vil resultere i et nedbrud i det lange løb.
Jeg er ikke rigtig sikker på, hvorfor hver debuf fjerner helbredet i stedet for, at helbredet kun tages en gang, hvor virkningerne af alle debuffs anvendes på den oprindelige skade, men jeg antager, at det er den rigtige spillogik.
Den korrekte kode ville væ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 - Jeg skal påpege, at da jeg tidligere har skrevet hukommelsesallokatorer, behøver sletning af den samme hukommelse ikke at være et problem. Det kunne også være overflødigt. Det hele afhænger af tildelers adfærd. Mine fungerede bare som en sammenkædet liste på lavt niveau, så "noden" for de slettede data bliver enten sat som fri flere gange eller gentaget flere gange (hvilket bare svarer til redundante markøromdirigeringer). God fangst dog.
- Dobbeltfri er en fejl og fører generelt til udefineret adfærd og nedbrud. Selvom du har en brugerdefineret tildeler, der på en eller anden måde ikke tillader genbrug af den samme hukommelsesadresse, er dobbeltfri en ildelugtende kode, da det ikke giver mening, og du bliver råbt af statiske kodeanalysatorer.
- Selvfølgelig! Jeg designede det ikke til det formål. Nogle sprog kræver bare en tildeler på grund af manglende funktioner. Nej Nej Nej. Jeg sagde blot, at et sammenbrud ikke er garanteret. Visse designklassifikationer går ikke altid sammen.