{"id":332,"date":"2015-06-06T17:29:07","date_gmt":"2015-06-06T16:29:07","guid":{"rendered":"http:\/\/www.uncovergame.com\/?p=332"},"modified":"2015-06-06T17:29:07","modified_gmt":"2015-06-06T16:29:07","slug":"persisting-data-with-emscripten","status":"publish","type":"post","link":"https:\/\/uncovergame.com\/fr\/2015\/06\/06\/persisting-data-with-emscripten\/","title":{"rendered":"Persistance de donn\u00e9es avec Emscripten"},"content":{"rendered":"<p>Lors du portage d&rsquo;un jeu vers le web via Emscripten, On remarque rapidement que le syst\u00e8me de fichiers virtuel utilis\u00e9 ne persiste pas les donn\u00e9es par d\u00e9faut. Toutes les modifications sur un fichier sont syst\u00e9matiquement perdues d&rsquo;une session \u00e0 une autre. Heureusement, Emscripten dispose d&rsquo;un syst\u00e8me de fichiers sp\u00e9cifique, nomm\u00e9 IDBFS, qui permet de persister les donn\u00e9es d&rsquo;un fichier en utilisant l&rsquo;API Indexed Db de HTML5. Il est important de garder en t\u00eate que la persistance de donn\u00e9es se fait manuellement en appelant des fonctions de synchronisation d\u00e9di\u00e9es, ses fonctions de synchronisation \u00e9tant asynchrones, il faut \u00e9galement prendre en compte le d\u00e9lai entre l&rsquo;appel de fonction et le moment o\u00f9 les donn\u00e9es sont effectivement sauvegard\u00e9es. Pour plus d&rsquo;informations sur l&rsquo;API fichiers de Emscripten, voir la documentation sur le site officiel (en anglais) :<br \/>\n<a title=\"Emscripten API\" href=\" http:\/\/kripken.github.io\/emscripten-site\/docs\/api_reference\/Filesystem-API.html\" target=\"_blank\">Emscripten API<\/a><\/p>\n<p>Sur l&rsquo;ensemble de mes jeux \u00e9crit en C existant, j&rsquo;utilise SQLite pour la sauvegarde des donn\u00e9es joueurs. Les avantages principaux de SQLite sont l&rsquo;utilisation du langage SQL pour la manipulation de donn\u00e9es, l\u2019absence de serveur en arri\u00e8re plan et le stockage de donn\u00e9es dans un seul fichier.<\/p>\n<p>En utilisant IDBFS, il est possible de conserver le fonctionnement de SQLite tel quel, en y ajoutant l&rsquo;appel de fonction de synchronisation pour sauvegarder les modification du fichier de base de donn\u00e9es.<\/p>\n<p>Initialisation du syst\u00e8me de fichiers IDBFS :<\/p>\n<pre class=\"lang:c decode:true \" title=\"Initialisation IDBFS\">EM_ASM(\r\n\t\t\t\/\/Cr\u00e9ation de notre dossier qui contiendra l'ensemble des donn\u00e9es \u00e0      persister\r\n\t\t\tFS.mkdir('\/persistent_data');\r\n\t\t\t\/\/initialisation de IDBFS sur notre dossier\r\n\t\t\tFS.mount(IDBFS,{},'\/persistent_data');\r\n\r\n\t\t\tModule.print(\"start file sync..\");\r\n\t\t\t\/\/valeur de v\u00e9rification de fin de synchro\r\n\t\t\tModule.syncdone = 0;\r\n\r\n\t\t\t\/\/effectue la synchronisation, si des fichiers existe d\u00e9j\u00e0, il sont    ajout\u00e9 \u00e0 notre dossier \"persistent_data\"\r\n\t\t\t\/\/premier param\u00e8tre = \"true\" signifie synchronisation depuis Indexed Db vers Emscripten, \r\n\t\t\t\/\/\"false\" signifie synchronisation depuis Emscripten vers Indexed Db\r\n\t\t\t\/\/second param\u00e8tre = fonction appel\u00e9e lorsque les donn\u00e9es sont synchronis\u00e9es\r\n\t\t\tFS.syncfs(true, function(err) {\r\n\t\t\t\tassert(!err);\r\n\t\t\t\tModule.print(\"end file sync..\");\r\n\t\t\t\tModule.syncdone = 1;\r\n\t\t\t});\r\n\t\t);<\/pre>\n<p>(La macro EM_ASM permet d&rsquo;int\u00e9grer du code JavaScript directement en C)<\/p>\n<p>Le dossier \u00ab\u00a0persistent_data\u00a0\u00bb est toujours vide au d\u00e9but, Il n&rsquo;est pas possible de monter un dossier qui est d\u00e9j\u00e0 mont\u00e9 avec le syst\u00e8me de fichiers par d\u00e9faut de Emscripten.<\/p>\n<p>Une fois la synchronisation effectu\u00e9e, on peut commencer \u00e0 utiliser les fichiers du r\u00e9pertoire \u00ab\u00a0persistent_data\u00a0\u00bb, ou ajouter un fichier de sauvegarde initial si le joueur lance notre jeu pour la premi\u00e8re fois.<\/p>\n<pre class=\"lang:c decode:true \" title=\"Initialisation gestionnaire sauvegarde\">if(!save_mngr.ready)\r\n\t{\r\n\t\tif(emscripten_run_script_int(\"Module.syncdone\") == 1) \/\/v\u00e9rification de la valeur de fin de synchro\t\t{\r\n\t\t\tFILE* file = fopen(save_path,\"r\"); \/\/v\u00e9rification de le fichier sqlite existe, sinon on le copie dans notre dossier persistant\r\n\r\n\t\t\tif(file == NULL)\r\n\t\t\t{\r\n\t\t\t\tlogprint(\"save.db file doesn't exist in file system, copying it...\");\r\n\t\t\t\tsize_t size;\r\n\t\t\t\t\/\/copie du fichier\r\n\t\t\t\t\/\/dans ce cas, on copie un fichier sqlite de base dans le dossier  persistant depuis le dossier \"base_data\"\r\n\t\t\t\tunsigned char* buffer = get_resx_content(&amp;game_state-&gt;resx_mngr,\"\/base_data\/save.db\",&amp;size,NULL);\r\n\r\n\t\t\t\tfile = fopen(save_path,\"w\");\r\n\r\n\t\t\t\tfwrite(buffer,sizeof(unsigned char),size,file);\r\n\t\t\t\tfclose(file);\r\n                                \r\n\t\t\t\t\/\/persistance des donn\u00e9es depuis Emscripten vers Indexed Db\r\n\t\t\t\tEM_ASM(\r\n\t\t\t\t\tModule.print(\"Start File sync..\");\r\n\t\t\t\t\tModule.syncdone = 0;\r\n\t\t\t\t\tFS.syncfs(false, function(err) {\r\n\t\t\t\t\t\tassert(!err);\r\n\t\t\t\t\t\tModule.print(\"End File sync..\");\r\n\t\t\t\t\t\tModule.syncdone = 1;\r\n\t\t\t\t\t});\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tfclose(file);\r\n\t\t\t}\r\n\r\n\t\t\tsave_mngr_init(&amp;save_mngr,save_path);  \/\/chargement du fichier SQLite, la variable \"ready_flag\" a maintenant la valeur \"true\"\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\treturn;  \/\/tant que les donn\u00e9es de sauvegarde ne sont pas charg\u00e9es, on emp\u00eache l'ex\u00e9cution du reste de la fonction\r\n\t\t}\r\n\t}<\/pre>\n<p>(La variable save_mngr g\u00e8re dans ce cas l&rsquo;ensemble des donn\u00e9es de sauvegarde via SQLite)<br \/>\n(Comme Emscripten ne nous permet pas d&rsquo;utiliser une boucle while ou la fonction sleep pour attendre l&rsquo;ex\u00e9cution d&rsquo;une fonction asynchrone, Le code est ajout\u00e9 au d\u00e9but de notre fonction principale de jeu appel\u00e9e en boucle)<\/p>\n<p>Une fois les donn\u00e9es de sauvegarde cr\u00e9\u00e9es \/ charg\u00e9es, il est possible de persister les modifications de notre fichier SQLite avec le code suivant :<\/p>\n<pre class=\"lang:c decode:true \" title=\"Synchronisation Emscripten &gt; IDBFS\">EM_ASM(\r\n\t\t\t\/\/persistance des donn\u00e9es\r\n\t\t\tFS.syncfs(false,function (err) {\r\n\t\t\t\tassert(!err);\r\n\t\t\t});\r\n\t\t);\r\n<\/pre>\n<p>Le code d&rsquo;initialisation chargera ainsi les donn\u00e9es modifi\u00e9es la prochaine fois que le joueur lancera notre jeu. Via Chrome, il est possible de voir le contenu synchronis\u00e9 dans Indexed Db, Aller \u00e0 Outils de d\u00e9veloppements &gt; Onglet Ressources &gt; Indexed Db.<\/p>","protected":false},"excerpt":{"rendered":"<p>Lors du portage d&rsquo;un jeu vers le web via Emscripten, On remarque rapidement que le syst\u00e8me de fichiers virtuel utilis\u00e9 ne persiste pas les donn\u00e9es par d\u00e9faut. Toutes les modifications sur un fichier sont syst\u00e9matiquement perdues d&rsquo;une session \u00e0 une &hellip; <a href=\"https:\/\/uncovergame.com\/fr\/2015\/06\/06\/persisting-data-with-emscripten\/\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[6,7,8],"tags":[],"_links":{"self":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/332"}],"collection":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/comments?post=332"}],"version-history":[{"count":32,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/332\/revisions"}],"predecessor-version":[{"id":364,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/332\/revisions\/364"}],"wp:attachment":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/media?parent=332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/categories?post=332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/tags?post=332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}