Anonim

AIMBOT 2.0

U epizodi 1 Nove igre 2, oko 9:40, nalazi se snimka koda koji je napisala Nene:

Evo ga u tekstualnom obliku s prevedenim komentarima:

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

Nakon pucnjave, Umiko, pokazujući na petlju for, rekao je da je razlog zašto se kod srušio taj što postoji beskonačna petlja.

Zapravo ne znam C ++ pa nisam siguran je li to što ona govori istina.

Koliko vidim, petlja for samo se ponavlja kroz debufove koje glumac trenutno ima. Ako glumac nema beskonačnu količinu otklona, ​​ne mislim da bi mogao postati beskonačna petlja.

Ali nisam siguran jer je jedini razlog što postoji šifra koda taj što su ovdje htjeli staviti uskrsno jaje, zar ne? Upravo bismo snimili stražnji dio laptopa i čuli kako Umiko govori "Oh, tamo imate beskrajnu petlju". Činjenica da su zapravo pokazali neki kod navodi me na razmišljanje da je nekako nekakvo uskrsno jaje.

Hoće li kod zapravo stvoriti beskonačnu petlju?

8
  • Vjerojatno korisno: dodatna snimka zaslona Umika koja kaže da "Bilo je pozivajući istu operaciju uvijek iznova ", što možda neće biti prikazano u kodu.
  • Oh! Nisam to znao! @AkiTanaka, sub koji sam gledao kaže "beskonačna petlja"
  • @LoganM Zapravo se ne slažem. Ne radi se samo o tome da OP ima pitanje o nekom izvornom kodu koji je slučajno proizašao iz animea; OP-ovo pitanje odnosi se na određenu izrečenu izjavu oko izvorni kod znakom u animeu, a tu je i odgovor na anime, naime "Crunchyroll je goofed i pogrešno preveo liniju".
  • @senshin Mislim da čitate ono o čemu želite da se radi, a ne ono što se zapravo postavlja. Pitanje pruža izvorni kod i pita ga generira li beskonačnu petlju kao stvarni C ++ kôd. Nova igra! je izmišljeno djelo; nema potrebe da se kôd predstavljen u njemu u skladu sa stvarnim standardima. Ono što Umiko kaže o kodu mjerodavnije je od bilo kojih C ++ standarda ili kompajlera. Odgovor na vrhu (prihvaćen) ne spominje nikakve informacije u svemiru. Mislim da bi se o tome moglo postaviti pitanje na temu s dobrim odgovorom, ali kako je sročeno to nije to.

Kôd nije beskonačna petlja, ali je greška.

Postoje dva (moguće tri) problema:

  • Ako ne postoje ispravci, nikakva šteta neće biti primijenjena
  • Ako postoji više od 1 otklona, ​​primijenit će se pretjerana šteta
  • Ako DestroyMe () odmah izbriše objekt, a još uvijek postoji m_debufs za obradu, petlja će se izvršiti nad izbrisanim objektom i razmjestiti u memoriju. Većina pokretača igara ima red uništavanja da bi to zaobišao, pa i više, pa to možda i nije problem.

Primjena štete trebala bi biti izvan petlje.

Evo ispravljene funkcije:

// 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 Jesmo li na pregledu koda? : D
  • 4 plovka izvrsna su za zdravlje ako ne prijeđete iznad 16777216 HP. Možete čak postaviti zdravlje na beskonačno da biste stvorili neprijatelja kojeg možete pogoditi, ali neće umrijeti, i izvršiti napad jednim ubijanjem koristeći beskonačnu štetu koja još uvijek neće ubiti beskonačni HP znak (rezultat INF-INF je NaN), ali ubit će sve ostalo. Dakle, vrlo je korisno.
  • 1 @cat Prema dogovoru u mnogim standardima kodiranja, m_ prefiks znači da je varijabla člana. U ovom slučaju varijabla člana od DestructibleActor.
  • 2 @HotelCalifornia Slažem se da postoji mala šansa ApplyToDamage ne radi kako se očekivalo, ali rekao bih u primjeru koji navedete ApplyToDamage također potrebno je preraditi kako bi se zahtijevalo predavanje izvornika sourceDamage kao i kako bi mogao ispravno izračunati debuf u tim slučajevima. Da budemo apsolutni pedant: u ovom trenutku dmg podaci trebali bi biti struktura koja uključuje izvorni dmg, trenutni dmg i prirodu štete, ako debufovi imaju stvari poput "ranjivosti na požar". Iz iskustva prođe nedugo zatim bilo kakav dizajn igre s debufovima.
  • 1 @StephaneHockenhull dobro rečeno!

Čini se da kod ne stvara beskonačnu petlju.

Jedini način na koji bi petlja bila beskonačna bio bi ako

debuf.ApplyToDamage(resolvedDamage); 

ili

DestroyMe(); 

su dodali nove stavke u m_debufs kontejner.

To se čini malo vjerojatnim. A da je to slučaj, program bi se mogao srušiti zbog promjene spremnika tijekom ponavljanja.

Program bi se najvjerojatnije srušio zbog poziva na DestroyMe(); što vjerojatno uništava trenutni objekt koji trenutno izvodi petlju.

Možemo ga zamisliti kao crtić u kojem 'negativac' vidi granu da bi i 'dobri momak pao s njim, ali prekasno shvati da je na pogrešnoj strani reza. Ili Midgaard Snake koja jede svoj rep.


Također bih trebao dodati da je najčešći simptom beskonačne petlje da zamrzne program ili ne reagira. Program će srušiti ako dodijeli memoriju više puta ili učini nešto što na kraju podijeli s nulom ili slično.


Na temelju komentara Akija Tanake,

Vjerojatno korisno: dodatna snimka zaslona Umika koja kaže da je "Opet pozivao istu operaciju", koja možda neće biti prikazana u kodu.

"Zvao je istu operaciju iznova i iznova" To je vjerojatnije.

Pod pretpostavkom da DestroyMe(); nije dizajniran za pozivanje više puta, vjerojatnije je da će uzrokovati pad.

Način da se riješi ovaj problem bio bi promjena if za ovako nešto:

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

To bi izašlo iz petlje kad se DestructibleActor uništi, pazeći da 1) DestroyMe metoda se poziva samo jednom i 2) nemojte beskorisno primjenjivati ​​buffove kad se objekt već smatra mrtvim.

2
  • 1 Probijanje petlje for kada je zdravlje <= 0 definitivno je bolji popravak od čekanja da se provjeri stanje nakon petlje.
  • Mislim da bih vjerojatno break izvan petlje i zatim poziv DestroyMe(), samo da budem siguran

Postoji nekoliko problema s kodom:

  1. Ako nema otklona, ​​ne bi se nastala šteta.
  2. DestroyMe() naziv funkcije zvuči opasno. Ovisno o načinu na koji se provodi, to može biti problem, ali ne mora. Ako je to samo poziv destruktoru trenutnog objekta umotanog u funkciju, tada postoji problem jer bi objekt bio uništen usred izvršavanja koda. Ako se radi o pozivu funkcije koja stavlja na čekanje događaj brisanja trenutnog objekta, tada nema problema, jer bi objekt bio uništen nakon što izvrši svoje izvršavanje i pokrene se petlja događaja.
  3. Stvarni problem koji se čini spomenut u animeu, "Pozivao je istu operaciju iznova i iznova" - nazvat će DestroyMe() dugo kao m_currentHealth <= 0.f i preostalo je još otklona za ponavljanje, što bi moglo rezultirati DestroyMe() koji se zovu više puta, uvijek iznova. Petlja bi se trebala zaustaviti nakon prve DestroyMe() poziva, jer brisanje objekta više puta rezultira oštećenjem memorije, što će dugoročno vjerojatno rezultirati padom.

Nisam baš siguran zašto svaki debuf oduzima zdravlje, umjesto da se oduzima samo jednom, s učincima svih debuffa na početnu štetu, ali pretpostavit ću da je to ispravna logika igre.

Točan bi kôd bio

// 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
  • Trebao bih naglasiti da, kao što sam u prošlosti napisao alokatore memorije, brisanje iste memorije ne mora predstavljati problem. Također bi mogao biti suvišan. Sve ovisi o ponašanju raspodjeljivača. Moj se jednostavno ponašao poput povezanog popisa na niskoj razini, tako da se "čvor" za izbrisane podatke nekoliko puta postavi besplatnim ili ponovno briše nekoliko puta (što bi samo odgovaralo suvišnim preusmjeravanjima pokazivača). Dobar ulov ipak.
  • Double-free je bug i uglavnom dovodi do nedefiniranog ponašanja i rušenja. Čak i ako imate prilagođeni alokator koji nekako onemogućava ponovnu upotrebu iste memorijske adrese, double-free je smrdljiv kôd jer nema smisla i na vas će vikati statički analizatori koda.
  • Naravno! Nisam ga dizajnirao u tu svrhu. Neki jezici samo zahtijevaju alokator zbog nedostatka značajki. Ne ne ne. Samo sam izjavljivao da pad nije zajamčen. Određene klasifikacije dizajna ne padaju uvijek.