Anonim

AIMBOT 2.0

I episode 1 av New Game 2, rundt 9:40, er det et skudd av 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(); } } } 

Etter skuddet sa Umiko, som pekte på for-sløyfen, at grunnen til at koden krasjet er at det er en uendelig sløyfe.

Jeg vet ikke C ++, så jeg er ikke sikker på om det hun sier er sant.

Fra det jeg kan se, går it-loop bare gjennom debufsene som skuespilleren for øyeblikket har. Med mindre skuespilleren har uendelig mye debufs, tror jeg ikke det muligens kan bli en uendelig løkke.

Men jeg er ikke sikker fordi den eneste grunnen til at det er et skudd av koden er at de ønsket å sette et påskeegg her, ikke sant? Vi hadde nettopp fått et skudd på baksiden av den bærbare datamaskinen og hørt Umiko si "Å du har en uendelig løkke der". Det faktum at de faktisk viste litt kode får meg til å tenke at koden på en eller annen måte er et påskeegg av noe slag.

Vil koden faktisk skape en uendelig løkke?

8
  • Sannsynligvis nyttig: ekstra skjermbilde av Umiko som sier at "Det var kaller den samme operasjonen om og om igjen ", som kanskje ikke vises i koden.
  • Åh! Jeg visste ikke det! @AkiTanaka suben jeg så på sier "uendelig løkke"
  • @LoganM Jeg er ikke helt enig. Det er ikke bare at OP har et spørsmål om en eller annen kildekode som tilfeldigvis kommer fra en anime; OPs spørsmål handler om en bestemt uttalelse Om kildekoden etter et tegn i anime, og det er et anime-relatert svar, nemlig "Crunchyroll gjort goofed og feiloversatt linjen".
  • @senshin Jeg tror du leser det du vil at spørsmålet skal handle om, snarere enn det som faktisk blir spurt. Spørsmålet gir litt kildekode og spør om den genererer en uendelig sløyfe som ekte C ++ -kode. Nytt spill! er et fiktivt verk; det er ikke behov for kode presentert i den for å oppfylle virkelige standarder. Det Umiko sier om koden er mer autoritativt enn noen C ++ - standarder eller kompilatorer. Det øverste (aksepterte) svaret nevner ingen informasjon i universet. Jeg tror et spørsmål om temaet kan stilles om dette med et godt svar, men slik det er formulert, er det ikke det.

Koden er ikke en uendelig løkke, men den er en feil.

Det er to (muligens tre) problemer:

  • Hvis ingen debufs er til stede, vil ingen skade bli påført i det hele tatt
  • Overdreven skade vil bli påført hvis det er mer enn 1 debuf
  • Hvis DestroyMe () straks sletter objektet og det fremdeles er m_debufs som skal behandles, utføres sløyfen over et slettet objekt og søppelhukommelse. De fleste spillmotorer har en ødeleggelseskø for å omgå dette og mer, så det er kanskje ikke noe problem.

Påføringen av skader bør være utenfor løkken.

Her er den korrigerte funksjonen:

// 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 på Code Review? : D
  • 4 flottører er gode for helsen hvis du ikke går over 16777216 HP. Du kan til og med sette helsen til uendelig for å skape en fiende du kan slå, men ikke vil dø, og ha et one-kill-angrep med uendelig skade som fremdeles ikke vil drepe en uendelig HP-karakter (resultatet av INF-INF er NaN), men vil drepe alt annet. Så det er veldig nyttig.
  • 1 @cat Etter konvensjon i mange kodestandarder m_ prefikset betyr at det er en medlemsvariabel. I dette tilfellet en medlemsvariabel på DestructibleActor.
  • 2 @HotelCalifornia Jeg er enig i at det er en liten sjanse ApplyToDamage fungerer ikke som forventet, men i eksempelet du gir vil jeg si ApplyToDamage også må omarbeides for å kreve originalen sourceDamage også slik at den kan beregne debuf riktig i de tilfellene. For å være en absolutt pedant: på dette tidspunktet bør dmg-informasjonen være en struktur som inkluderer den opprinnelige dmg, gjeldende dmg, og arten av skaden (e) også, hvis debufs har ting som "sårbarhet for brann". Erfaringsmessig er det ikke lenge før noe spilldesign med debufs krever disse.
  • 1 @StephaneHockenhull vel sagt!

Koden ser ikke ut til å skape en uendelig løkke.

Den eneste måten sløyfen ville være uendelig ville være hvis

debuf.ApplyToDamage(resolvedDamage); 

eller

DestroyMe(); 

skulle legge til nye ting i m_debufs container.

Dette virker lite sannsynlig. Og hvis det var tilfelle, kunne programmet krasje på grunn av endring av beholderen mens den ble iterert.

Programmet vil mest sannsynlig krasje på grunn av samtalen til DestroyMe(); som antagelig ødelegger det nåværende objektet som for tiden kjører løkken.

Vi kan tenke på det som tegneserien der den 'dårlige fyren' så en gren for å få den 'gode fyren' til å falle med, men innser for sent at han er på feil side av kuttet. Eller Midgaard Snake som spiser sin egen hale.


Jeg bør også legge til at det vanligste symptomet på en uendelig sløyfe er at den fryser programmet eller gjør det ikke responsivt. Det vil krasje programmet hvis det tildeles minne gjentatte ganger, eller gjør noe som ender opp med å dele med null, eller lignende.


Basert på kommentaren fra Aki Tanaka,

Sannsynligvis nyttig: ekstra skjermbilde av Umiko som sier at "Det kalte den samme operasjonen igjen og igjen", som kanskje ikke vises i koden.

"Det kalte den samme operasjonen om og om igjen" Dette er mer sannsynlig.

Antar at DestroyMe(); er ikke designet for å kalles mer enn en gang, det er mer sannsynlig å forårsake et krasj.

En måte å løse dette problemet på er å endre if for noe sånt:

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

Dette vil gå ut av sløyfen når DestructibleActor blir ødelagt, og sørge for at 1) DestroyMe metoden kalles bare en gang og 2) ikke bruk buffs ubrukelig når objektet allerede er ansett som død.

2
  • 1 Å bryte ut for-loop når helse <= 0 er definitivt en bedre løsning enn å vente til etter loop for å sjekke helse.
  • Jeg tror sannsynligvis break ut av løkken, og deretter anrop DestroyMe()bare for å være trygg

Det er flere problemer med koden:

  1. Hvis det ikke er debufs, vil ingen skade bli tatt.
  2. DestroyMe() funksjonsnavnet høres farlig ut. Avhengig av hvordan den implementeres, kan det være et problem. Hvis det bare er et kall til destruktøren av det nåværende objektet pakket inn i en funksjon, er det et problem, da objektet ville bli ødelagt midt i det å utføre kode. Hvis det er et kall til en funksjon som står i kø for slettingshendelsen til det nåværende objektet, er det ikke noe problem, da objektet vil bli ødelagt etter at det er fullført og hendelsessløyfen sparker inn.
  3. Det faktiske problemet som ser ut til å bli nevnt i anime, "Det kalte den samme operasjonen om og om igjen" - det vil ringe DestroyMe() så lenge som m_currentHealth <= 0.f og det er flere debuffs igjen å gjenta, noe som kan resultere i DestroyMe() blir kalt flere ganger, om og om igjen. Sløyfen skal stoppe etter den første DestroyMe() ring, fordi det å slette et objekt mer enn en gang resulterer i minnekorrupsjon, noe som sannsynligvis vil føre til et krasj i det lange løp.

Jeg er ikke helt sikker på hvorfor hver debuf tar bort helsen, i stedet for at helsen blir tatt bort bare en gang, med effekten av at alle debuffs blir brukt på den opprinnelige skaden, men jeg vil anta at det er den riktige spilllogikken.

Den riktige koden 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 må påpeke at ettersom jeg tidligere har skrevet minnetildlere, trenger ikke det å være slett å slette det samme minnet. Det kan også være overflødig. Alt avhenger av tildelers oppførsel. Mine fungerte bare som en koblet liste på et lavt nivå, slik at "noden" for de slettede dataene enten blir satt fri gratis flere ganger eller slettes på nytt flere ganger (noe som bare tilsvarer redundante pekeromdirigeringer). God fangst skjønt.
  • Dobbeltfri er en feil, og fører generelt til udefinert oppførsel og krasj. Selv om du har en egendefinert tildeler som på en eller annen måte ikke tillater gjenbruk av den samme minneadressen, er dobbeltfri en stinkende kode, da det ikke gir mening, og du vil bli kjeftet av analysatorer for statisk kode.
  • Selvfølgelig! Jeg designet den ikke for det formålet. Noen språk krever bare en tildeler på grunn av manglende funksjoner. Nei nei nei. Jeg sa bare at et krasj ikke er garantert. Visse designklassifiseringer krasjer ikke alltid.