Anonim

AIMBOT 2.0

Di episode 1 New Game 2, sekitar jam 9:40, ada cuplikan kode yang sudah ditulis Nene:

Ini dia dalam bentuk teks dengan komentar yang diterjemahkan:

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

Setelah tembakan, Umiko, menunjuk ke loop for, mengatakan bahwa alasan mengapa kode crash adalah karena ada loop yang tak terbatas.

Saya tidak begitu tahu C ++ jadi saya tidak yakin apakah yang dia katakan itu benar.

Dari apa yang saya lihat, loop for hanya melakukan iterasi melalui debuf yang dimiliki Aktor saat ini. Kecuali Aktor memiliki jumlah debuf yang tak terbatas, saya rasa itu tidak mungkin menjadi loop tanpa batas.

Tapi saya tidak yakin karena satu-satunya alasan ada suntikan kode adalah karena mereka ingin meletakkan telur paskah di sini, bukan? Kami baru saja mengambil gambar bagian belakang laptop dan mendengar Umiko berkata "Oh, Anda punya loop tak terbatas di sana". Fakta bahwa mereka benar-benar menunjukkan beberapa kode membuat saya berpikir bahwa kode itu adalah semacam telur paskah.

Akankah kode tersebut benar-benar membuat pengulangan tanpa batas?

8
  • Mungkin membantu: tangkapan layar tambahan Umiko yang mengatakan bahwa "Itu memanggil operasi yang sama berulang-ulang ", yang mungkin tidak ditampilkan dalam kode.
  • Oh! Saya tidak tahu itu! @AkiTanaka sub yang saya tonton mengatakan "loop tak terbatas"
  • @LoganM Saya tidak begitu setuju. Bukan hanya OP memiliki pertanyaan tentang beberapa kode sumber yang kebetulan berasal dari anime; Pertanyaan OP adalah tentang pernyataan tertentu yang dibuat tentang kode sumber oleh karakter di anime, dan ada jawaban terkait anime, yaitu "Crunchyroll dilakukan salah dan salah menerjemahkan baris".
  • @senshin Saya pikir Anda membaca tentang apa yang Anda inginkan dari pertanyaan itu, daripada apa yang sebenarnya ditanyakan. Pertanyaan tersebut memberikan beberapa kode sumber dan menanyakan apakah itu menghasilkan loop tak terbatas sebagai kode C ++ kehidupan nyata. Permainan baru! adalah karya fiksi; tidak perlu kode yang disajikan di dalamnya untuk menyesuaikan dengan standar kehidupan nyata. Apa yang Umiko katakan tentang kode lebih otoritatif daripada standar atau kompiler C ++. Jawaban teratas (diterima) tidak menyebutkan informasi di alam semesta. Saya pikir pertanyaan tentang topik dapat ditanyakan tentang ini dengan jawaban yang bagus, tetapi seperti yang diungkapkan, ini bukan.

Kode tersebut bukanlah perulangan tanpa batas tetapi merupakan bug.

Ada dua (mungkin tiga) masalah:

  • Jika tidak ada debuf yang muncul, tidak ada kerusakan yang akan diterapkan sama sekali
  • Kerusakan yang berlebihan akan diterapkan jika ada lebih dari 1 debuf
  • Jika DestroyMe () segera menghapus objek dan masih ada m_debuf yang akan diproses, loop akan dieksekusi di atas objek yang dihapus dan memori yang dibuang. Sebagian besar mesin game memiliki antrian penghancuran untuk mengatasi hal ini dan lebih banyak lagi sehingga mungkin tidak menjadi masalah.

Penerapan kerusakan harus di luar lingkaran.

Inilah fungsi yang diperbaiki:

// 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 Apakah kita sedang dalam Peninjauan Kode? : D
  • 4 float bagus untuk kesehatan jika HP Anda tidak melebihi 16777216. Anda bahkan dapat mengatur kesehatan menjadi tak terbatas untuk menciptakan musuh yang dapat Anda pukul tetapi tidak akan mati, dan memiliki serangan satu-kill menggunakan kerusakan tak terbatas yang tetap tidak akan membunuh karakter HP tak terbatas (hasil INF-INF adalah NaN) tetapi akan membunuh yang lainnya. Jadi ini sangat berguna.
  • 1 @cat Berdasarkan konvensi di banyak standar pengkodean, m_ awalan berarti variabel anggota. Dalam hal ini variabel anggota DestructibleActor.
  • 2 @HotelCalifornia Saya setuju bahwa ada peluang kecil ApplyToDamage tidak bekerja seperti yang diharapkan tetapi dalam kasus contoh yang Anda berikan, saya akan katakan ApplyToDamage juga perlu dikerjakan ulang untuk mewajibkan lewat aslinya sourceDamage juga agar dapat menghitung debuf dengan benar dalam kasus tersebut. Untuk menjadi pedant absolut: pada titik ini informasi dmg harus berupa struct yang mencakup dmg asli, dmg saat ini, dan sifat kerusakan serta jika debuf memiliki hal-hal seperti "kerentanan terhadap api". Dari pengalaman, tidak lama kemudian desain game dengan debuf menuntut ini.
  • 1 @StephaneHockenhull berkata dengan baik!

Kode tersebut tampaknya tidak membuat loop tak terbatas.

Satu-satunya cara loop menjadi tak terbatas adalah jika

debuf.ApplyToDamage(resolvedDamage); 

atau

DestroyMe(); 

adalah menambahkan item baru ke m_debufs wadah.

Ini sepertinya tidak mungkin. Dan jika demikian, program bisa macet karena mengubah penampung saat sedang diiterasi.

Program kemungkinan besar akan macet karena panggilan ke DestroyMe(); yang mungkin menghancurkan objek saat ini yang sedang menjalankan loop.

Kita dapat menganggapnya sebagai kartun di mana 'orang jahat' melihat cabang untuk membuat 'orang baik' jatuh bersamanya, tetapi terlambat menyadari bahwa dia berada di sisi yang salah. Atau Ular Midgaard yang memakan ekornya sendiri.


Saya juga harus menambahkan bahwa gejala paling umum dari pengulangan tak terbatas adalah program itu membekukan atau membuatnya tidak responsif. Ini akan merusak program jika mengalokasikan memori berulang kali, atau melakukan sesuatu yang akhirnya membaginya dengan nol, atau sejenisnya.


Berdasarkan komentar Aki Tanaka,

Mungkin membantu: tangkapan layar tambahan Umiko yang mengatakan bahwa "Itu memanggil operasi yang sama berulang kali", yang mungkin tidak ditampilkan dalam kode.

"Itu memanggil operasi yang sama berulang kali" Ini lebih mungkin.

Berasumsi bahwa DestroyMe(); tidak dirancang untuk dipanggil lebih dari sekali, ini lebih cenderung menyebabkan crash.

Cara untuk memperbaiki masalah ini adalah dengan mengubah if untuk sesuatu seperti ini:

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

Ini akan keluar dari loop ketika DestructibleActor dihancurkan, memastikan bahwa 1) file DestroyMe metode ini dipanggil hanya sekali dan 2) jangan menerapkan buff secara sia-sia setelah objek sudah dianggap mati.

2
  • 1 Keluar dari loop for ketika kesehatan <= 0 jelas merupakan perbaikan yang lebih baik daripada menunggu sampai setelah loop untuk memeriksa kesehatan.
  • Saya pikir saya mungkin akan melakukannya break keluar dari lingkaran, dan kemudian panggilan DestroyMe(), untuk amannya

Ada beberapa masalah dengan kode:

  1. Jika tidak ada debuf, tidak ada kerusakan yang terjadi.
  2. DestroyMe() nama fungsi terdengar berbahaya. Bergantung pada bagaimana penerapannya, itu mungkin atau mungkin tidak menjadi masalah. Jika ini hanya panggilan ke destruktor dari objek saat ini yang dibungkus dalam suatu fungsi, maka ada masalah, karena objek akan dihancurkan di tengah-tengah kode yang sedang dieksekusi. Jika itu adalah panggilan ke fungsi yang mengantri acara penghapusan objek saat ini, maka tidak ada masalah, karena objek akan dimusnahkan setelah menyelesaikan eksekusinya dan loop peristiwa dimulai.
  3. Masalah sebenarnya yang tampaknya disebutkan di anime, "Itu memanggil operasi yang sama berulang kali" - itu akan memanggil DestroyMe() selama m_currentHealth <= 0.f dan ada lebih banyak debuff yang tersisa untuk melakukan iterasi, yang mungkin mengakibatkan DestroyMe() dipanggil berkali-kali, berulang kali. Loop harus berhenti setelah yang pertama DestroyMe() panggilan, karena menghapus objek lebih dari sekali mengakibatkan kerusakan memori, yang kemungkinan akan mengakibatkan crash dalam jangka panjang.

Saya tidak begitu yakin mengapa setiap debuf menghilangkan kesehatan, alih-alih kesehatan diambil hanya sekali, dengan efek dari semua debuff diterapkan pada kerusakan awal yang diambil, tetapi saya akan berasumsi bahwa itu logika permainan yang benar.

Kode yang benar adalah

// 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
  • Saya harus menunjukkan bahwa karena saya telah menulis pengalokasi memori di masa lalu, menghapus memori yang sama tidak harus menjadi masalah. Itu juga bisa berlebihan. Itu semua tergantung pada perilaku pengalokasi. Milik saya hanya bertindak seperti daftar tertaut tingkat rendah sehingga "simpul" untuk data yang dihapus dapat disetel sebagai gratis beberapa kali atau dihapus ulang beberapa kali (yang hanya akan sesuai dengan pengalihan penunjuk yang berlebihan). Tangkapan yang bagus.
  • Double-free adalah bug, dan umumnya menyebabkan perilaku yang tidak terdefinisi dan crash. Bahkan jika Anda memiliki pengalokasi khusus yang entah bagaimana melarang penggunaan kembali alamat memori yang sama, double-free adalah kode yang tidak menyenangkan karena tidak masuk akal dan Anda akan dimarahi oleh penganalisis kode statis.
  • Tentu saja! Saya tidak mendesainnya untuk tujuan itu. Beberapa bahasa hanya membutuhkan pengalokasi karena kurangnya fitur. Tidak tidak Tidak. Saya hanya menyatakan bahwa kecelakaan tidak dijamin. Klasifikasi desain tertentu tidak selalu macet.