AIMBOT 2.0
Στο επεισόδιο 1 του New Game 2, περίπου στις 9:40, υπάρχει μια φωτογραφία του κώδικα που έγραψε ο Nene:
Εδώ είναι σε μορφή κειμένου με τα σχόλια που μεταφράζονται:
// 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(); } } }
Μετά τον πυροβολισμό, ο Umiko, δείχνοντας το βρόχο για το, είπε ότι ο λόγος για τον οποίο ο κώδικας έπεσε είναι ότι υπάρχει ένας άπειρος βρόχος.
Δεν ξέρω πραγματικά το C ++, οπότε δεν είμαι σίγουρος αν αυτό που λέει είναι αλήθεια.
Από ό, τι μπορώ να δω, το for loop απλώς επαναλαμβάνει τα debufs που έχει σήμερα ο Ηθοποιός. Εκτός αν ο Ηθοποιός έχει ένα άπειρο debufs, δεν νομίζω ότι μπορεί να γίνει ένας άπειρος βρόχος.
Αλλά δεν είμαι σίγουρος γιατί ο μόνος λόγος για τον οποίο υπάρχει μια βολή του κώδικα είναι ότι ήθελαν να βάλουν ένα πασχαλινό αυγό εδώ, σωστά; Θα είχαμε μόλις τραβήξει το πίσω μέρος του φορητού υπολογιστή και θα είχαμε ακούσει τον Umiko να λέει "Ω, έχεις έναν άπειρο βρόχο εκεί". Το γεγονός ότι έδειξαν πράγματι κάποιο κωδικό με κάνει να σκεφτώ ότι κάπως ο κώδικας είναι ένα αυγό του Πάσχα κάποιου είδους.
Ο κώδικας θα δημιουργήσει πραγματικά έναν άπειρο βρόχο;
8- Πιθανώς χρήσιμο: πρόσθετο στιγμιότυπο οθόνης του Umiko που λέει ότι "Ήταν καλώντας την ίδια λειτουργία ξανά και ξανά ", το οποίο μπορεί να μην εμφανίζεται στον κώδικα.
- Ω! Δεν το ήξερα! @AkiTanaka, το sub που παρακολούθησα λέει "άπειρος βρόχος"
- @LoganM Δεν συμφωνώ πραγματικά. Δεν είναι απλώς ότι το OP έχει μια ερώτηση σχετικά με κάποιο πηγαίο κώδικα που προέρχεται από ένα anime. Η ερώτηση του OP αφορά μια συγκεκριμένη δήλωση σχετικά με ο πηγαίος κώδικας από έναν χαρακτήρα στο anime, και υπάρχει μια απάντηση που σχετίζεται με το anime, δηλαδή "Ο Crunchyroll έκανε παραπλανητικό και εσφαλμένη μετάφραση της γραμμής".
- @senshin Νομίζω ότι διαβάζετε αυτό που θέλετε να είναι η ερώτηση, αντί για ό, τι πραγματικά τίθεται. Η ερώτηση παρέχει κάποιο πηγαίο κώδικα και ρωτά αν δημιουργεί έναν άπειρο βρόχο ως πραγματικό κώδικα C ++. Νέο παιχνίδι! είναι ένα φανταστικό έργο? Δεν υπάρχει ανάγκη για κωδικό που να παρουσιάζεται για να συμμορφώνεται με τα πραγματικά πρότυπα. Αυτό που λέει ο Umiko σχετικά με τον κώδικα είναι πιο έγκυρο από οποιοδήποτε πρότυπο C ++ ή μεταγλωττιστές. Η κορυφαία (αποδεκτή) απάντηση δεν αναφέρει καμία πληροφορία στο σύμπαν. Νομίζω ότι μια ερώτηση επί του θέματος θα μπορούσε να τεθεί σχετικά με αυτό με μια καλή απάντηση, αλλά όπως διατυπώθηκε αυτό δεν είναι.
Ο κώδικας δεν είναι ένας άπειρος βρόχος αλλά είναι ένα σφάλμα.
Υπάρχουν δύο (πιθανώς τρία) ζητήματα:
- Εάν δεν υπάρχουν debuf, καμία ζημιά δεν θα εφαρμοστεί καθόλου
- Υπερβολική ζημιά θα εφαρμοστεί εάν υπάρχουν περισσότερα από 1 debuf
- Εάν το DestroyMe () διαγράψει αμέσως το αντικείμενο και εξακολουθούν να υπάρχουν m_debufs προς επεξεργασία, ο βρόχος θα εκτελεστεί πάνω από ένα διαγραμμένο αντικείμενο και μνήμη απορριμμάτων. Οι περισσότερες μηχανές παιχνιδιών έχουν μια ουρά καταστροφής για να επιλύσουν αυτό και περισσότερο, ώστε να μην είναι πρόβλημα.
Η εφαρμογή ζημιάς πρέπει να είναι εκτός βρόχου.
Εδώ είναι η διορθωμένη συνάρτηση:
// 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 Είμαστε σε αναθεώρηση κώδικα; :ΡΕ
- 4 πλωτήρες είναι ιδανικοί για την υγεία εάν δεν υπερβαίνετε τα 16777216 HP. Μπορείτε ακόμη και να ρυθμίσετε την υγεία σε άπειρη για να δημιουργήσετε έναν εχθρό που μπορείτε να χτυπήσετε, αλλά δεν θα πεθάνετε και να κάνετε μια επίθεση με ένα άπειρο χρησιμοποιώντας άπειρη ζημιά που ακόμα δεν θα σκοτώσει έναν άπειρο χαρακτήρα HP (το αποτέλεσμα του INF-INF είναι NaN) αλλά θα σκοτώσει τα πάντα. Είναι πολύ χρήσιμο.
- 1 @ cat Κατά συνθήκη σε πολλά πρότυπα κωδικοποίησης το
m_
πρόθεμα σημαίνει ότι είναι μεταβλητή μέλους. Σε αυτήν την περίπτωση, μια μεταβλητή μέλους τουDestructibleActor
. - 2 @HotelCalifornia Συμφωνώ ότι υπάρχει μια μικρή πιθανότητα
ApplyToDamage
δεν λειτουργεί όπως αναμένεται, αλλά στην περίπτωση που δίνετε θα έλεγαApplyToDamage
επίσης πρέπει να ξανασχεδιαστεί για να απαιτείται να περάσει το πρωτότυποsourceDamage
επίσης, ώστε να μπορεί να υπολογίσει σωστά το debuf σε αυτές τις περιπτώσεις. Για να είστε απόλυτα επιτακτικοί: σε αυτό το σημείο οι πληροφορίες dmg θα πρέπει να είναι δομή που να περιλαμβάνει το αρχικό dmg, το τρέχον dmg και τη φύση των ζημιών καθώς και εάν τα debufs έχουν πράγματα όπως "ευπάθεια στη φωτιά". Από την εμπειρία δεν έχει περάσει πολύς χρόνος πριν από κάθε σχεδιασμό παιχνιδιού με debufs. - 1 @StephaneHockenhull είπε καλά!
Ο κώδικας δεν φαίνεται να δημιουργεί έναν άπειρο βρόχο.
Ο μόνος τρόπος που ο βρόχος θα ήταν άπειρος θα ήταν αν
debuf.ApplyToDamage(resolvedDamage);
ή
DestroyMe();
ήταν να προσθέσετε νέα αντικείμενα στο m_debufs
δοχείο.
Αυτό φαίνεται απίθανο. Και αν συνέβαινε αυτό, το πρόγραμμα θα μπορούσε να διακοπεί λόγω αλλαγής του κοντέινερ κατά την επανάληψη.
Το πρόγραμμα πιθανότατα θα διακοπεί λόγω της κλήσης προς DestroyMe();
το οποίο πιθανώς καταστρέφει το τρέχον αντικείμενο που τρέχει επί του παρόντος το βρόχο.
Μπορούμε να το θεωρήσουμε ως το κινούμενο σχέδιο όπου ο «κακός» βλέπει ένα κλαδί για να πέσει ο «καλός», αλλά συνειδητοποιεί πολύ αργά ότι είναι στη λάθος πλευρά της περικοπής. Ή το Midgaard Snake που τρώει τη δική του ουρά.
Θα πρέπει επίσης να προσθέσω ότι το πιο κοινό σύμπτωμα ενός άπειρου βρόχου είναι ότι παγώνει το πρόγραμμα ή το καθιστά μη ανταποκρινόμενο. Θα διακόψει το πρόγραμμα εάν εκχωρεί μνήμη επανειλημμένα ή κάνει κάτι που καταλήγει να διαιρείται με μηδέν ή τα παρόμοια.
Με βάση το σχόλιο του Aki Tanaka,
Πιθανώς χρήσιμο: πρόσθετο στιγμιότυπο οθόνης του Umiko που λέει ότι "Κάλεσε την ίδια λειτουργία ξανά και ξανά", το οποίο μπορεί να μην εμφανίζεται στον κώδικα.
"Καλούσε την ίδια λειτουργία ξανά και ξανά" Αυτό είναι πιο πιθανό.
Υποθέτοντας ότι DestroyMe();
δεν έχει σχεδιαστεί για να καλείται περισσότερες από μία φορές, είναι πιο πιθανό να προκαλέσει σφάλμα.
Ένας τρόπος για να διορθώσετε αυτό το ζήτημα θα ήταν να αλλάξετε το if
για κάτι σαν αυτό:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
Αυτό θα βγαίνει από το βρόχο όταν καταστρέφεται ο DestructibleActor, διασφαλίζοντας ότι 1) το DestroyMe
η μέθοδος καλείται μόνο μία φορά και 2) μην εφαρμόζετε buffs άχρηστα όταν το αντικείμενο θεωρείται ήδη νεκρό.
- 1 Το σπάσιμο από το βρόχο όταν η υγεία <= 0 είναι σίγουρα μια καλύτερη λύση από το να περιμένετε μέχρι το βρόχο για να ελέγξετε την υγεία.
- Νομίζω ότι πιθανώς
break
εκτός βρόχου, και έπειτα κλήσηDestroyMe()
, για να είμαι ασφαλής
Υπάρχουν πολλά προβλήματα με τον κωδικό:
- Εάν δεν υπάρχουν debufs, δεν θα υπάρξει ζημιά.
DestroyMe()
το όνομα της λειτουργίας ακούγεται επικίνδυνο. Ανάλογα με τον τρόπο εφαρμογής του, μπορεί να είναι ή όχι ένα πρόβλημα. Εάν είναι απλώς μια κλήση στον καταστροφέα του τρέχοντος αντικειμένου που είναι τυλιγμένο σε μια συνάρτηση, τότε υπάρχει ένα ζήτημα, καθώς το αντικείμενο θα καταστραφεί στη μέση του εκτελώντας κώδικα. Εάν πρόκειται για μια κλήση σε μια συνάρτηση που περιμένει στην ουρά το συμβάν διαγραφής του τρέχοντος αντικειμένου, τότε δεν υπάρχει πρόβλημα, καθώς το αντικείμενο θα καταστραφεί αφού ολοκληρώσει την εκτέλεση και θα ξεκινήσει ο βρόχος συμβάντος.- Το πραγματικό ζήτημα που φαίνεται να αναφέρεται στο anime, το "Κάλεσε την ίδια λειτουργία ξανά και ξανά" - θα καλέσει
DestroyMe()
όσοm_currentHealth <= 0.f
και απομένουν περισσότερα debuffs για επανάληψη, κάτι που μπορεί να οδηγήσει σεDestroyMe()
καλείται πολλές φορές, ξανά και ξανά. Ο βρόχος θα πρέπει να σταματήσει μετά το πρώτοDestroyMe()
κλήση, επειδή η διαγραφή ενός αντικειμένου περισσότερες από μία φορές έχει ως αποτέλεσμα καταστροφή της μνήμης, η οποία πιθανότατα θα οδηγήσει σε διακοπή μακροπρόθεσμα.
Δεν είμαι πραγματικά σίγουρος γιατί κάθε debuf αφαιρεί την υγεία, αντί να αφαιρεθεί η υγεία μόνο μία φορά, με τα αποτελέσματα όλων των debuffs να εφαρμόζονται στην αρχική ζημιά που έχει ληφθεί, αλλά θα υποθέσω ότι αυτή είναι η σωστή λογική του παιχνιδιού.
Ο σωστός κωδικός θα ήταν
// 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 - Πρέπει να επισημάνω ότι, όπως έχω γράψει εκχωρητές μνήμης στο παρελθόν, η διαγραφή της ίδιας μνήμης δεν πρέπει να αποτελεί πρόβλημα. Θα μπορούσε επίσης να είναι περιττό. Όλα εξαρτώνται από τη συμπεριφορά του κατανεμητή. Το δικό μου λειτούργησε σαν μια λίστα συνδεδεμένων χαμηλού επιπέδου, οπότε ο "κόμβος" για τα διαγραμμένα δεδομένα είτε έχει οριστεί ως δωρεάν πολλές φορές είτε επανακατανεμηθεί αρκετές φορές (κάτι που αντιστοιχεί μόνο σε περιττές ανακατευθύνσεις δείκτη). Ωραία αλίευση.
- Το Double-free είναι σφάλμα και γενικά οδηγεί σε απροσδιόριστη συμπεριφορά και σφάλματα. Ακόμα κι αν έχετε έναν προσαρμοσμένο κατανεμητή που κατά κάποιον τρόπο δεν επιτρέπει την επαναχρησιμοποίηση της ίδιας διεύθυνσης μνήμης, το διπλό-ελεύθερο είναι ένας δύσοσμος κώδικας καθώς δεν έχει νόημα και θα σας φωνάζουν οι αναλυτές στατικών κωδικών.
- Φυσικά! Δεν το σχεδίασα για αυτό το σκοπό. Ορισμένες γλώσσες απαιτούν απλώς έναν κατανεμητή λόγω έλλειψης χαρακτηριστικών. ΟΧΙ ΟΧΙ ΟΧΙ. Απλώς έλεγα ότι το σφάλμα δεν είναι εγγυημένο. Ορισμένες ταξινομήσεις σχεδιασμού δεν καταρρέουν πάντα.