Error en la operación de archivo (s) en el juego crash / kill por OS / User en Android

13

Mi juego guarda su estado después de un intervalo fijo en un archivo en el almacenamiento interno del juego / aplicación. Cuando el usuario o el sistema operativo matan o bloquean mi juego respectivamente, escribimos el estado actual del juego en ese archivo. La escritura de archivos después del intervalo de corrección funciona bien, pero cuando el sistema operativo o el usuario bloquean / matan el juego, la operación de escritura de archivos falla. El fallo de la operación da como resultado un estado de juego incompleto o un estado de juego vacío, es decir, ningún dato escrito en el archivo. He intentado múltiples soluciones usando el servicio de Android, en caso de que el servicio sea NO STICKY, el servicio se elimina con la aplicación. Por otro lado, si el servicio es STICKY, se reinicia pero la intención que se adjuntó inicialmente con el servicio es nula o nueva.

La pregunta es: ¿cómo puedo guardar mis datos (podría ser ~ 2-3MB) completamente en el archivo en el almacenamiento interno cuando el usuario / sistema operativo mata mi juego / aplicación?

Faisal Imran
fuente
Tienes que manejar tus SIGTERMs y tus SIGKILLs (bueno, tal vez no SIGKILLs, Android probablemente esté usando SIGTERM). (No tengo tiempo para investigar / escribir una respuesta completa, pero esa es la idea básica).
John Hamilton
2
@JohnHamilton SIGKILL no se puede manejar, por definición.
Darkhogg
@Darkhogg Bueno, no en Unity, eso es seguro. (Cambié el kernel de Linux en un momento para un proyecto para este propósito específico y ciertamente es posible dejar que los programas manejen SIGKILL)
John Hamilton

Respuestas:

20

Sugeriría escribir su estado de guardado de una manera de doble búfer, como era común para los títulos de consola en el pasado (donde tenía que hacer frente a la extracción de la tarjeta de memoria a mitad de la escritura y las escrituras eran lentas).

Salvar:

  • Si no existen archivos, escriba el estado en el archivo A
  • Si existe A, escriba a B
  • Si A y B existen, encuentre cuál es más antiguo, elimínelo y luego escriba en ese

Carga:

  • Si existen A y B, intente cargar la más nueva de las dos. Si eso falla (porque el archivo está incompleto o dañado), elimínelo y cargue el otro
  • Si solo existe uno, cárguelo
  • Si ambos están corruptos, informe al usuario que su estado de guardado era irrecuperable (como presumiblemente ya debe hacerlo)

Este flujo funcionará para garantizar que siempre haya al menos un guardado válido presente en su almacenamiento, incluso si la aplicación se cierra a mitad de la escritura. El jugador no obtendrá ninguno de los cambios en el estado del juego desde el guardado anterior si se produce ese tipo de falla, pero no tendrá que comenzar de nuevo.

Además, debe guardar inmediatamente después de los eventos clave (como las compras en la aplicación), además de guardar el intervalo, para minimizar la ventana de vulnerabilidad en la que un bloqueo / muerte causaría la pérdida de ese evento clave. Esto también es protección contra las vulnerabilidades del juego (por ejemplo, matar a la fuerza la aplicación después de perder una vida, porque sabes que volverás a un estado de juego desde antes de perder una vida y volverás a intentarlo). Por supuesto, si hace esto, debe proteger su intervalo de guardado con una lógica que dice "si un guardado ya está en progreso, no intente comenzar a guardar nuevamente".

MrCranky
fuente
Gracias MrCranky, esta es una solución parcial a mi problema, ¿cómo puedo guardar los datos de la última sesión, es decir, justo antes de matar la aplicación / juego? Por ejemplo, hizo una compra en la aplicación y mató la aplicación.
Faisal Imran
12
Ni siquiera necesitas dos archivos. Escriba en B, si y solo si la escritura fue exitosa, elimine A y cambie el nombre de B a A. java.nio.file.Files.move(Path source, Path target, CopyOption... options)Puede cambiar el nombre y sobrescribir como una operación atómica.
Polygnome
66
@Polygnome: Tenga en cuenta que en el caso de fallas de energía, esto no siempre proporcionará las garantías que espera, a menos que llame sync()al archivo B antes de moverlo sobre el archivo A (incluso entonces no hay garantías completas, pero sync()es bastante bueno).
Dietrich Epp
1
@FaisalImran Guardar datos justo después de la operación importante, por ejemplo, después de IAP. Si la operación fuera interrumpida por el teléfono apagado, supongo que nunca le quitaría dinero a la persona de todos modos. Pero por si acaso, es posible que desee guardar los datos de su cuenta en una compra en alguna plataforma como un intento de comprar algo. Luego, puede comparar sus ganancias con esos datos y ver si esta persona realmente compró algo. Y luego abra esta función para él a través del servicio en línea que está utilizando para guardar todos los datos. Si está utilizando partidas guardadas locales para IAP, entonces es aún peor que este problema.
Candid Moon _Max_
1
Por último, aunque esto puede no resolver todos sus problemas, yo diría que su problema no es completamente solucionable. No puede admitir tanto "el usuario puede matar mi aplicación en cualquier momento" como "nunca se deben perder datos" porque siempre habrá una ventana después de que se haya producido un evento pero antes de que se haya guardado en el almacenamiento donde el usuario puede matar la aplicación y perder datos.
MrCranky
3

Android solo mata las aplicaciones cuando están en segundo plano. ¿Realmente deben actualizarse los datos del juego cuando la aplicación está en segundo plano, o puede dejar de actualizar los datos hasta que la aplicación vuelva al primer plano? Es posible que pueda diferir las actualizaciones incluso en un juego multijugador, si puede obtener un historial de eventos o incluso solo el estado actual cuando la aplicación vuelve al primer plano. Esto es algo que probablemente debería considerar hacer de todos modos por la eficiencia de la CPU, la red y la batería.

Si el usuario mata su aplicación, los servicios en ejecución deberían recibir una llamada onTaskRemoved. No sé qué pasaría si intentaras hacer mucho procesamiento con este método. Espero que si no regresa dentro de cierto tiempo, Android será kill -9tu aplicación.

Kevin Krumwiede
fuente
la aplicación no necesita actualizarse en segundo plano, pero la aplicación debe guardar sus datos, es decir, el archivo json antes de pasar a segundo plano o destruir.
Faisal Imran
tiene razón en TareasRemoved método llamará pero el tiempo requerido para guardar el archivo es más o podemos decir que el tiempo de cálculo es grande. Cuando el usuario mata la aplicación, este método se detendrá.
Faisal Imran