Persistance de données avec Emscripten

Lors du portage d’un jeu vers le web via Emscripten, On remarque rapidement que le système de fichiers virtuel utilisé ne persiste pas les données par défaut. Toutes les modifications sur un fichier sont systématiquement perdues d’une session à une autre. Heureusement, Emscripten dispose d’un système de fichiers spécifique, nommé IDBFS, qui permet de persister les données d’un fichier en utilisant l’API Indexed Db de HTML5. Il est important de garder en tête que la persistance de données se fait manuellement en appelant des fonctions de synchronisation dédiées, ses fonctions de synchronisation étant asynchrones, il faut également prendre en compte le délai entre l’appel de fonction et le moment où les données sont effectivement sauvegardées. Pour plus d’informations sur l’API fichiers de Emscripten, voir la documentation sur le site officiel (en anglais) :
Emscripten API

Sur l’ensemble de mes jeux écrit en C existant, j’utilise SQLite pour la sauvegarde des données joueurs. Les avantages principaux de SQLite sont l’utilisation du langage SQL pour la manipulation de données, l’absence de serveur en arrière plan et le stockage de données dans un seul fichier.

En utilisant IDBFS, il est possible de conserver le fonctionnement de SQLite tel quel, en y ajoutant l’appel de fonction de synchronisation pour sauvegarder les modification du fichier de base de données.

Initialisation du système de fichiers IDBFS :

EM_ASM(
			//Création de notre dossier qui contiendra l'ensemble des données à      persister
			FS.mkdir('/persistent_data');
			//initialisation de IDBFS sur notre dossier
			FS.mount(IDBFS,{},'/persistent_data');

			Module.print("start file sync..");
			//valeur de vérification de fin de synchro
			Module.syncdone = 0;

			//effectue la synchronisation, si des fichiers existe déjà, il sont    ajouté à notre dossier "persistent_data"
			//premier paramètre = "true" signifie synchronisation depuis Indexed Db vers Emscripten, 
			//"false" signifie synchronisation depuis Emscripten vers Indexed Db
			//second paramètre = fonction appelée lorsque les données sont synchronisées
			FS.syncfs(true, function(err) {
				assert(!err);
				Module.print("end file sync..");
				Module.syncdone = 1;
			});
		);

(La macro EM_ASM permet d’intégrer du code JavaScript directement en C)

Le dossier « persistent_data » est toujours vide au début, Il n’est pas possible de monter un dossier qui est déjà monté avec le système de fichiers par défaut de Emscripten.

Une fois la synchronisation effectuée, on peut commencer à utiliser les fichiers du répertoire « persistent_data », ou ajouter un fichier de sauvegarde initial si le joueur lance notre jeu pour la première fois.

if(!save_mngr.ready)
	{
		if(emscripten_run_script_int("Module.syncdone") == 1) //vérification de la valeur de fin de synchro		{
			FILE* file = fopen(save_path,"r"); //vérification de le fichier sqlite existe, sinon on le copie dans notre dossier persistant

			if(file == NULL)
			{
				logprint("save.db file doesn't exist in file system, copying it...");
				size_t size;
				//copie du fichier
				//dans ce cas, on copie un fichier sqlite de base dans le dossier  persistant depuis le dossier "base_data"
				unsigned char* buffer = get_resx_content(&game_state->resx_mngr,"/base_data/save.db",&size,NULL);

				file = fopen(save_path,"w");

				fwrite(buffer,sizeof(unsigned char),size,file);
				fclose(file);
                                
				//persistance des données depuis Emscripten vers Indexed Db
				EM_ASM(
					Module.print("Start File sync..");
					Module.syncdone = 0;
					FS.syncfs(false, function(err) {
						assert(!err);
						Module.print("End File sync..");
						Module.syncdone = 1;
					});
				);
			}
			else
			{
				fclose(file);
			}

			save_mngr_init(&save_mngr,save_path);  //chargement du fichier SQLite, la variable "ready_flag" a maintenant la valeur "true"
		}
		else
		{
			return;  //tant que les données de sauvegarde ne sont pas chargées, on empêche l'exécution du reste de la fonction
		}
	}

(La variable save_mngr gère dans ce cas l’ensemble des données de sauvegarde via SQLite)
(Comme Emscripten ne nous permet pas d’utiliser une boucle while ou la fonction sleep pour attendre l’exécution d’une fonction asynchrone, Le code est ajouté au début de notre fonction principale de jeu appelée en boucle)

Une fois les données de sauvegarde créées / chargées, il est possible de persister les modifications de notre fichier SQLite avec le code suivant :

EM_ASM(
			//persistance des données
			FS.syncfs(false,function (err) {
				assert(!err);
			});
		);

Le code d’initialisation chargera ainsi les données modifiées la prochaine fois que le joueur lancera notre jeu. Via Chrome, il est possible de voir le contenu synchronisé dans Indexed Db, Aller à Outils de développements > Onglet Ressources > Indexed Db.

Ce contenu a été publié dans Emscripten, HTML5, WebGL. Vous pouvez le mettre en favoris avec ce permalien.

Les commentaires sont fermés.