Échec des opérations sur les fichiers en cas de plantage / destruction du jeu par le système d'exploitation / l'utilisateur dans Android

13

Mon jeu enregistre son état après un intervalle fixe sur un fichier dans le stockage interne du jeu / de l'application. Lorsque mon jeu est tué ou bloqué respectivement par l'utilisateur ou le système d'exploitation, nous écrivons l'état actuel du jeu sur ce fichier. L'écriture de fichiers après l'intervalle de correction fonctionne correctement mais lorsque le jeu est bloqué / tué par le système d'exploitation ou l'utilisateur, l'opération d'écriture de fichiers échoue. L'échec de l'opération entraîne un état de jeu incomplet ou un état de jeu vide, c'est-à-dire aucune donnée écrite dans le fichier. J'ai essayé plusieurs solutions en utilisant le service Android, dans le cas où le service est NON STICKY, le service est tué avec l'application. D'un autre côté, si le service est STICKY, il est redémarré mais l'intention qui a été attachée initialement avec le service est nulle ou nouvelle.

La question est de savoir comment puis-je enregistrer mes données (pourrait être ~ 2-3 Mo) complètement sur fichier dans le stockage interne lorsque mon jeu / application est tué par l'utilisateur / OS?

Faisal Imran
la source
Vous devez gérer vos SIGTERMs et vos SIGKILLs (enfin peut-être pas SIGKILLs, Android utilise probablement SIGTERM). (Vous n'avez pas le temps de rechercher / rédiger une réponse complète, mais c'est l'idée de base.)
John Hamilton
2
@JohnHamilton SIGKILL ne peut pas être géré, par définition.
Darkhogg
@Darkhogg Eh bien, pas dans Unity, c'est sûr. (J'ai changé le noyau Linux à un moment donné pour un projet dans ce but spécifique et il est certainement possible de laisser les programmes gérer SIGKILL)
John Hamilton

Réponses:

20

Je suggérerais d'écrire votre état de sauvegarde d'une manière à double tampon, comme cela était courant pour les titres de console dans le passé (où vous deviez faire face à la carte mémoire retirée en cours d'écriture et les écritures étaient lentes).

Sauvegarder:

  • Si aucun fichier n'existe, écrivez l'état dans le fichier A
  • Si A existe, écrivez à B
  • Si A et B existent tous les deux, recherchez celui qui est le plus ancien, supprimez-le, puis écrivez dans celui-ci

Charge:

  • Si A et B existent, essayez de charger le plus récent des deux. Si cela échoue (car le fichier est incomplet ou corrompu), supprimez-le et chargez l'autre
  • S'il n'en existe qu'un, chargez-le
  • Si les deux sont corrompus, informez l'utilisateur que leur état de sauvegarde était irrécupérable (comme vous l'avez probablement déjà fait)

Ce flux fonctionnera pour garantir qu'il y ait toujours au moins une sauvegarde valide dans votre stockage, même si l'application est supprimée en cours d'écriture. Le joueur n'obtiendra aucun des changements dans l'état du jeu depuis la sauvegarde précédente si ce genre d'échec se produit, mais il n'aura pas à recommencer.

De plus, vous devez enregistrer immédiatement après les événements clés (tels que les achats intégrés) en plus de votre enregistrement d'intervalle, pour minimiser la fenêtre de vulnérabilité où un crash / kill entraînerait la perte de cet événement clé. C'est également une protection contre les exploits du jeu (par exemple, tuer l'application de force après avoir perdu une vie, car vous savez que vous reviendrez à un état de jeu avant de perdre une vie, et que vous pourrez réessayer). Bien sûr, si vous effectuez cette opération, vous devez protéger votre sauvegarde par intervalles avec une logique qui dit "si une sauvegarde est déjà en cours, n'essayez pas de recommencer la sauvegarde".

MrCranky
la source
Merci MrCranky, c'est une solution partielle à mon problème, comment puis-je enregistrer les données de la dernière session, c'est-à-dire juste avant de tuer l'application / le jeu? Par exemple, il a fait un achat in-app et tué l'appli.
Faisal Imran
12
Vous n'avez même pas besoin de deux fichiers. Écrivez sur B, si et seulement si l'écriture a réussi, supprimez A et renommez B en A. java.nio.file.Files.move(Path source, Path target, CopyOption... options)peut renommer et écraser comme une opération atomique.
Polygnome
6
@Polygnome: Notez qu'en cas de panne de courant, cela ne fournira pas toujours les garanties que vous attendez, sauf si vous appelez sync()le fichier B avant de le déplacer sur le fichier A (même dans ce cas, il n'y a pas de garanties complètes, mais sync()c'est assez bon).
Dietrich Epp
1
@FaisalImran Enregistrez les données juste après l'opération importante, par exemple après IAP. Si l'opération était interrompue par la coupure du téléphone, je suppose que cela ne prendrait jamais de l'argent à la personne. Mais juste au cas où, vous voudrez peut-être enregistrer ses données de compte lors d'un achat sur une plate-forme pour tenter d'acheter quelque chose. Plus tard, vous pourrez comparer vos revenus à ces données et voir si cette personne a vraiment acheté quelque chose. Et puis ouvrez cette fonctionnalité pour lui via le service en ligne que vous utilisez pour enregistrer toutes les données. Si vous utilisez des sauvegardes locales pour IAP, c'est encore pire que ce problème.
Candid Moon _Max_
1
Enfin, même si cela ne résout pas tous vos problèmes, je dirais que votre problème n'est pas entièrement résoluble. Vous ne pouvez pas prendre en charge à la fois "l'utilisateur peut tuer mon application à tout moment" et "aucune donnée ne doit jamais être perdue" car il y aura toujours une fenêtre après qu'un événement se soit produit mais avant qu'il ne soit conservé dans le stockage où l'utilisateur peut tuer l'application et perdre des données.
MrCranky
3

Android ne tue les applications que lorsqu'elles sont en arrière-plan. Vos données de jeu doivent-elles vraiment être mises à jour lorsque l'application est en arrière-plan, ou pouvez-vous arrêter de mettre à jour les données jusqu'à ce que l'application revienne au premier plan? Vous pourrez peut-être différer les mises à jour même dans un jeu multijoueur, si vous pouvez récupérer un historique des événements ou même simplement l'état actuel lorsque l'application revient au premier plan. C'est quelque chose que vous devriez probablement envisager de toute façon pour le processeur, le réseau et l'efficacité de la batterie.

Si l'utilisateur tue votre application, les services en cours d'exécution doivent recevoir un appel à onTaskRemoved. Je ne sais pas ce qui se passerait si vous essayiez de faire beaucoup de traitement avec cette méthode. Je m'attends à ce que s'il ne revient pas dans un certain délai, Android utilisera kill -9votre application.

Kevin Krumwiede
la source
l'application n'a pas besoin d'être mise à jour en arrière-plan, mais l'application doit enregistrer ses données, c'est-à-dire le fichier json avant de passer en arrière-plan ou de détruire.
Faisal Imran
vous avez raison, la méthode onTaskRemoved appellera mais le temps nécessaire pour enregistrer le fichier est plus long ou nous pouvons dire que le temps de calcul est long. Lorsque l'utilisateur tue l'application, cette méthode est arrêtée.
Faisal Imran