Il y a beaucoup d'articles sur le développement de jeux sur Habré, mais parmi eux il y a trÚs peu d'articles qui se rapportent à des sujets «en coulisses». L'un de ces sujets est l'organisation de la livraison du jeu à un grand nombre d'utilisateurs depuis longtemps (un, deux, trois). Malgré le fait que pour certains la tùche puisse sembler futile, j'ai décidé de partager mon expérience de marche du rùteau dans cette affaire pour un projet spécifique. Toute personne intéressée - s'il vous plaßt.Une petite digression sur la divulgation d'informations. La plupart des entreprises sont trÚs jalouses que la «cuisine intérieure» ne devienne pas accessible au grand public. Pourquoi - je ne sais pas, mais qu'est-ce que - c'est. Dans ce projet particulier - The Universim - j'ai eu de la chance et le PDG de Crytivo Inc. (anciennement Crytivo Games Inc.) Alex Wallet s'est avéré absolument sain d'esprit dans une telle affaire, j'ai donc l'occasion de partager mon expérience avec les autres.Un peu de patcher en soi
Je participe au développement de jeux depuis longtemps. Dans certains - en tant que concepteur de jeux et programmeur, dans certains - en tant que fusion d'un administrateur systÚme et d'un programmeur (je n'aime pas le terme «devops», car il ne reflÚte pas exactement l'essence des tùches que j'exécute dans de tels projets).
Fin 2013 (l'horreur du temps qui passe), j'ai pensé à proposer de nouvelles versions (builds) aux utilisateurs. Bien sûr, à cette époque, il y avait de nombreuses solutions pour une telle tùche, mais le désir de fabriquer un produit et le désir de «construction de vélos» ont gagné. De plus, je voulais apprendre le C # plus profondément - j'ai donc décidé de créer mon propre patcher. Pour l'avenir, je dirai que le projet a été un succÚs, plus d'une dizaine d'entreprises l'ont utilisé et l'utilisent dans leurs projets, certaines ont demandé de réaliser une version prenant en compte exactement leurs souhaits.
La solution classique consiste à créer des packages delta (ou diffs) d'une version à l'autre. Cependant, cette approche n'est pas pratique pour les joueurs et les développeurs de testeurs - dans un cas, pour obtenir la derniÚre version du jeu, vous devez parcourir toute la chaßne des mises à jour. C'est-à -dire le joueur doit rassembler séquentiellement une certaine quantité de données qu'il (a) n'utilisera jamais, et le développeur de stocker sur son serveur (ou serveurs) un tas de données obsolÚtes dont certains des joueurs pourraient avoir besoin une fois.
Dans un autre cas, vous devez tĂ©lĂ©charger le correctif pour votre version au plus tard, mais le dĂ©veloppeur doit garder tout ce zoo de correctifs Ă la maison. Certaines implĂ©mentations de systĂšmes de correctifs nĂ©cessitent certains logiciels et une certaine logique sur les serveurs - ce qui crĂ©e Ă©galement un casse-tĂȘte supplĂ©mentaire pour les dĂ©veloppeurs. De plus, les dĂ©veloppeurs de jeux ne veulent souvent rien faire qui ne soit directement liĂ© au dĂ©veloppement du jeu lui-mĂȘme. Je dirai mĂȘme plus - la plupart ne sont pas des spĂ©cialistes capables de configurer des serveurs pour la distribution de contenu - ce n'est tout simplement pas leur domaine d'activitĂ©.
Avec tout cela à l'esprit, je voulais trouver une solution qui serait aussi simple que possible pour les utilisateurs (qui veulent jouer plus vite et ne pas danser avec des correctifs de différentes versions), ainsi que pour les développeurs qui ont besoin d'écrire un jeu sans savoir quoi et pourquoi non mis à jour par le prochain utilisateur.
Connaissant le fonctionnement de certains protocoles de synchronisation des donnĂ©es - lorsque les donnĂ©es sont analysĂ©es sur le client et que seules les modifications du serveur sont transmises - j'ai dĂ©cidĂ© d'utiliser la mĂȘme approche.
De plus, en pratique, de version en version tout au long de la pĂ©riode de dĂ©veloppement, de nombreux fichiers de jeu changent lĂ©gĂšrement - la texture est lĂ , le modĂšle lui-mĂȘme, quelques sons.
En consĂ©quence, il semblait logique de considĂ©rer chaque fichier dans le rĂ©pertoire du jeu comme un ensemble de blocs de donnĂ©es. Ă la sortie de la prochaine version, la construction du jeu est analysĂ©e, une carte de blocs est créée et les fichiers du jeu eux-mĂȘmes sont compressĂ©s bloc par bloc. Le client analyse les blocs existants et seule la diffĂ©rence est tĂ©lĂ©chargĂ©e.
Initialement, le patcher était prévu comme module dans Unity3D, cependant, un détail désagréable est apparu qui m'a fait reconsidérer cela. Le fait est que Unity3D est une application (moteur) totalement indépendante de votre code. Et pendant que le moteur fonctionne, tout un tas de fichiers sont ouverts, ce qui crée des problÚmes lorsque vous souhaitez les mettre à jour.
Dans les systĂšmes de type Unix, l'Ă©crasement d'un fichier ouvert (sauf s'il est spĂ©cifiquement verrouillĂ©) ne pose aucun problĂšme, mais sous Windows, sans danser avec un tambourin, une telle «feinte avec des oreilles» ne fonctionne pas. C'est pourquoi j'ai créé le patcher comme une application distincte qui ne charge que des bibliothĂšques systĂšme. De fait, le patcher lui-mĂȘme s'est avĂ©rĂ© ĂȘtre un utilitaire complĂštement indĂ©pendant du moteur Unity3D, ce qui n'a cependant pas empĂȘchĂ© de l'ajouter au magasin Unity3D.
Algorithme Patcher
Ainsi, les dĂ©veloppeurs publient de nouvelles versions avec une certaine frĂ©quence. Les joueurs veulent obtenir ces versions. L'objectif du dĂ©veloppeur est de fournir ce processus Ă un coĂ»t minimal et avec un minimum de maux de tĂȘte pour les joueurs.
Du développeur
Lors de la préparation d'un patch, l'algorithme des actions du patcher ressemble à ceci:
â CrĂ©ez une arborescence de fichiers de jeu avec leurs attributs et les sommes de contrĂŽle SHA512
â Pour chaque fichier:
âș Divisez le contenu en blocs.
âș Enregistrez la somme de contrĂŽle SHA256.
âș Compressez un bloc et ajoutez-le Ă la carte des blocs de fichiers.
âș Enregistrez l'adresse du bloc dans l'index.
â Enregistrez l'arborescence des fichiers avec leurs sommes de contrĂŽle.
â Enregistrez le fichier de donnĂ©es de version.
Le développeur doit télécharger les fichiers reçus sur le serveur.
CÎté joueur
Sur le client, le patcher effectue les opérations suivantes:
â Se copie dans un fichier avec un nom diffĂ©rent. Cela mettra Ă jour le fichier exĂ©cutable du patcher si nĂ©cessaire. Le contrĂŽle est ensuite transfĂ©rĂ© sur la copie et l'original est terminĂ©.
â TĂ©lĂ©chargez le fichier de version et comparez-le avec le fichier de version local.
â Si la comparaison n'a pas rĂ©vĂ©lĂ© de diffĂ©rence - vous pouvez jouer, nous avons la derniĂšre version. S'il y a une diffĂ©rence, passez Ă l'Ă©lĂ©ment suivant.
â TĂ©lĂ©chargez une arborescence de fichiers avec leurs sommes de contrĂŽle.
â Pour chaque fichier dans l'arborescence du serveur:
âș S'il y a un fichier, il considĂšre sa somme de contrĂŽle (SHA512). Sinon, il le considĂšre comme Ă©tant vide, (c'est-Ă -dire composĂ© de zĂ©ros solides) et considĂšre Ă©galement sa somme de contrĂŽle.
âș Si la somme du fichier local ne correspond pas Ă la somme de contrĂŽle du fichier de la derniĂšre version:
âș CrĂ©e une carte de blocs locale et la compare avec la carte de blocs du serveur.
âș Pour chaque bloc local qui diffĂšre du bloc distant, il tĂ©lĂ©charge un bloc compressĂ© depuis le serveur et l'Ă©crase localement.
â S'il n'y a aucune erreur, met Ă jour le fichier de version.
J'ai fait la taille du bloc de données un multiple de 1024 octets, aprÚs un certain nombre de tests, j'ai décidé qu'il était plus facile de fonctionner avec des blocs de 64 Ko. Bien que l'universalité du code demeure:
#region DQPatcher class public class DQPatcher {
Si vous rĂ©duisez les blocs, le client requiert moins de modifications lorsque les modifications elles-mĂȘmes sont peu nombreuses. Cependant, un autre problĂšme se pose - la taille du fichier d'index augmente inversement avec la diminution de la taille du bloc - c'est-Ă -dire si nous fonctionnons avec des blocs de 8 Ko, le fichier d'index sera 8 fois plus grand qu'avec des blocs de 64 Ko.
J'ai choisi SHA256 / 512 pour les fichiers et les blocs Ă partir des considĂ©rations suivantes: la vitesse diffĂšre lĂ©gĂšrement par rapport au (obsolĂšte) MD5 / SHA128, mais vous devez toujours lire les blocs et les fichiers. Et la probabilitĂ© de collisions avec SHA256 / 512 est nettement moindre qu'avec MD5 / SHA128. Ătre complĂštement ennuyeux - c'est dans ce cas, mais il est si petit que cette probabilitĂ© peut ĂȘtre nĂ©gligĂ©e.
De plus, le client prend en compte les points suivants:
âș Les blocs de donnĂ©es peuvent ĂȘtre dĂ©placĂ©s dans diffĂ©rentes versions, c'est-Ă -dire localement, nous avons le bloc numĂ©ro 10, et sur le serveur, nous avons le bloc numĂ©ro 12, ou vice versa. Ceci est pris en compte afin de ne pas tĂ©lĂ©charger de donnĂ©es supplĂ©mentaires.
âș Les blocs ne sont pas demandĂ©s un par un, mais en groupes - le client essaie de combiner les plages des blocs nĂ©cessaires et les demande au serveur en utilisant l'en-tĂȘte Range. Cela minimise Ă©galement la charge du serveur:
Bien sĂ»r, il s'est avĂ©rĂ© que le client peut ĂȘtre interrompu Ă tout moment et lors du lancement ultĂ©rieur, il poursuivra de facto son travail et ne tĂ©lĂ©chargera pas tout Ă partir de zĂ©ro.
Ici vous pouvez regarder une vidéo illustrant le travail du patcher sur le projet d'exemple Angry Bots:
Ă propos de l'organisation du patch de l'univers du jeu
En septembre 2015, Alex Koshelkov m'a contactĂ© et m'a proposĂ© de rejoindre le projet - ils avaient besoin d'une solution qui fournirait Ă 30 000 (avec une queue) de joueurs des mises Ă jour mensuelles. La taille initiale du jeu dans l'archive est de 600 mĂ©gaoctets. Avant de me contacter, il y avait eu des tentatives de faire votre propre version en utilisant Electron, mais tout s'est heurtĂ© au mĂȘme problĂšme de fichiers ouverts (Ă propos, la version actuelle d'Electron peut le faire) et quelques autres. De plus, aucun des dĂ©veloppeurs n'a compris comment tout cela fonctionnerait - ils m'ont fourni plusieurs conceptions de vĂ©los, la partie serveur Ă©tait complĂštement absente - ils voulaient le faire aprĂšs que toutes les autres tĂąches aient Ă©tĂ© rĂ©solues.
En outre, il Ă©tait nĂ©cessaire de rĂ©soudre le problĂšme de la prĂ©vention de la fuite des clĂ©s des joueurs - le fait est que les clĂ©s Ă©taient destinĂ©es Ă la plate-forme Steam, bien que le jeu lui-mĂȘme sur Steam ne soit pas encore accessible au public. La distribution du jeu Ă©tait strictement requise par la clĂ© - bien que les joueurs puissent partager la clĂ© du jeu avec des amis, cela pourrait ĂȘtre nĂ©gligĂ©, car si le jeu apparaissait sur Steam, la clĂ© ne pouvait ĂȘtre activĂ©e qu'une seule fois.
Dans la version normale du patcher, l'arborescence des données du patch ressemble à ceci:
./
| - linux
| | - 1.0.0
| `- version.txt
| - macosx
| | - 1.0.0
| `- version.txt
`- fenĂȘtres
| - 1.0.0
`- version.txt
Je devais m'assurer que seuls ceux avec la bonne clé avaient accÚs.
J'ai trouvĂ© la solution suivante - pour chaque clĂ©, nous obtenons son hachage (SHA1), puis nous l'utilisons comme chemin d'accĂšs aux donnĂ©es de patch sur le serveur. Sur le serveur, nous transfĂ©rons les donnĂ©es du correctif Ă un niveau supĂ©rieur Ă docroot et ajoutons des liens symboliques au rĂ©pertoire avec les donnĂ©es du correctif dans le docroot lui-mĂȘme. Les liens symboliques ont les mĂȘmes noms que les hachages de touches, uniquement divisĂ©s en plusieurs niveaux (pour faciliter le fonctionnement du systĂšme de fichiers), c'est-Ă -dire le hachage 0f99e50314d63c30271 ... ... ade71963e7ff sera reprĂ©sentĂ© comme
./0f/99/e5/0314d63c30271.....ade71963e7ff -----> / full / path / to / patch-data /.
Ainsi, il n'est pas nĂ©cessaire de distribuer les clĂ©s elles-mĂȘmes Ă quelqu'un qui prendra en charge les serveurs de mise Ă jour - il suffit de transfĂ©rer leurs hachages, qui sont absolument inutiles aux joueurs eux-mĂȘmes.
Pour ajouter de nouvelles clés (ou supprimer les anciennes) - ajoutez / supprimez simplement le lien symbolique correspondant.
Avec cette implĂ©mentation, la vĂ©rification de la clĂ© elle-mĂȘme n'est Ă©videmment effectuĂ©e nulle part; la rĂ©ception de 404 erreurs sur le client indique que la clĂ© est incorrecte (ou a Ă©tĂ© dĂ©sactivĂ©e).
Il convient de noter que l'accĂšs aux clĂ©s n'est pas une protection DRM Ă part entiĂšre - ce ne sont que des restrictions au stade des tests alpha et bĂȘta (fermĂ©s). Et la recherche est facilement interrompue par le biais du serveur Web lui-mĂȘme (au moins dans Nginx, que j'utilise).
Au cours du mois de lancement, seulement 2,5 To de trafic ont Ă©tĂ© livrĂ©s le premier jour seulement, et les jours suivants, environ le mĂȘme montant est distribuĂ© en moyenne par mois:

Par conséquent, si vous prévoyez de distribuer beaucoup de contenu - il est préférable de calculer à l'avance combien cela vous coûtera. Selon des observations personnelles - le trafic le moins cher des hébergeurs européens, le plus cher (je dirais «or») d'Amazon et de Google.
En pratique, les économies de trafic par an en moyenne chez The Universim sont énormes - comparez les chiffres ci-dessus. Bien sûr, si l'utilisateur n'a pas de jeu du tout ou s'il est trÚs obsolÚte, un miracle ne se produira pas et il devra télécharger beaucoup de données depuis le serveur - si à partir de zéro, alors un peu plus que le jeu ne prend dans les archives. Cependant, avec les mises à jour mensuelles, les choses vont vraiment bien. En moins de 6 mois, le miroir américain a donné un peu plus de 10 To de trafic, sans l'utilisation d'un patcher, cette valeur aurait considérablement augmenté.
Voici Ă quoi ressemble le trafic annuel du projet:

Quelques mots sur le "rake" le plus mémorable que nous avons dû franchir dans le processus de travail sur un patcher personnalisé pour le jeu "The Universim":
â Le plus gros problĂšme m'attendait des antivirus. Eh bien, ils n'aiment pas les applications qui tĂ©lĂ©chargent quelque chose sur Internet, modifient les fichiers (y compris les fichiers exĂ©cutables), puis essaient Ă©galement d'exĂ©cuter les fichiers tĂ©lĂ©chargĂ©s. Certains antivirus ne bloquaient pas seulement l'accĂšs aux fichiers locaux - ils bloquaient Ă©galement les appels au serveur de mise Ă jour eux-mĂȘmes, pĂ©nĂ©trant directement dans les donnĂ©es tĂ©lĂ©chargĂ©es par le client. La solution Ă©tait d'utiliser une signature numĂ©rique valide pour le patcher - cela rĂ©duit considĂ©rablement la paranoĂŻa des antivirus et l'utilisation du protocole HTTPS au lieu de HTTP - Ă©limine rapidement certaines des erreurs associĂ©es Ă la curiositĂ© des antivirus.
â Mise Ă jour des progrĂšs. De nombreux utilisateurs (et clients) souhaitent voir la progression de la mise Ă jour. Il faut improviser, car il n'est pas toujours possible de montrer de maniĂšre fiable les progrĂšs sans avoir Ă faire un travail supplĂ©mentaire. Oui, et l'heure exacte de la fin du processus de correction ne peut pas non plus ĂȘtre affichĂ©e, car le correctif lui-mĂȘme ne dispose pas de donnĂ©es sur les fichiers qui doivent ĂȘtre mis Ă jour Ă l'avance.
â Un grand nombre d'utilisateurs des Ătats-Unis ont des vitesses de connexion Ă des serveurs europĂ©ens pas trĂšs Ă©levĂ©es. La migration du serveur de mise Ă jour vers les Ătats-Unis a rĂ©solu ce problĂšme. Pour les utilisateurs d'autres continents, nous avons quittĂ© le serveur en Allemagne. Soit dit en passant, le trafic aux Ătats-Unis est beaucoup plus cher qu'europĂ©en, dans certains cas - plusieurs dizaines de fois.
â Apple n'est pas trĂšs Ă l'aise avec cette mĂ©thode d'installation des applications. Politique officielle - les applications doivent ĂȘtre installĂ©es uniquement Ă partir de leur magasin. Mais le problĂšme est que les applications aux Ă©tapes de test alpha et bĂȘta ne sont pas autorisĂ©es dans le magasin. Et plus encore, il n'y a rien Ă dire sur la vente d'applications brutes d'accĂšs anticipĂ©. Par consĂ©quent, vous devez Ă©crire des instructions sur la façon de danser sur les coquelicots. L'option avec AppAnnie (alors ils Ă©taient encore indĂ©pendants) n'a pas Ă©tĂ© envisagĂ©e en raison de la limite du nombre de testeurs.
â La mise en rĂ©seau est assez imprĂ©visible. Pour que l'application ne renonce pas immĂ©diatement, j'ai dĂ» entrer un compteur d'erreurs. 9 exceptions dĂ©tectĂ©es vous permettent de dire fermement Ă l'utilisateur qu'il a des problĂšmes avec le rĂ©seau.
â Les systĂšmes d'exploitation 32 bits ont des restrictions sur la taille des fichiers affichĂ©s en mĂ©moire (Memory Mapped Files - MMF) pour chaque thread d'exĂ©cution et pour le processus dans son ensemble. Les premiĂšres versions du patcher utilisaient MMF pour accĂ©lĂ©rer le travail, mais comme les fichiers de ressources de jeu peuvent ĂȘtre Ă©normes, j'ai dĂ» abandonner cette approche et utiliser des flux de fichiers ordinaires. Soit dit en passant, une perte de performances spĂ©ciale n'a pas Ă©tĂ© observĂ©e, probablement en raison de la lecture proactive du systĂšme d'exploitation.
â Vous devez ĂȘtre prĂȘt Ă ce que les utilisateurs se plaignent. Peu importe la qualitĂ© de votre produit, il y en aura toujours qui ne seront pas satisfaits. Et plus il y a d'utilisateurs de votre produit (dans le cas de l'Universim, il y en a plus de 50 000 en ce moment) - plus vous aurez de plaintes quantitativement. En pourcentage, c'est un trĂšs petit nombre, mais en termes quantitatifs ...
Bien que le projet dans son ensemble ait été un succÚs, il présente certains inconvénients:
â MĂȘme si j'ai initialement retirĂ© toute la logique principale sĂ©parĂ©ment, la partie GUI est diffĂ©rente dans l'implĂ©mentation pour MAC et Windows. La version Linux n'a pas causĂ© de problĂšmes - tous les problĂšmes Ă©taient principalement liĂ©s Ă l'utilisation d'une construction monolithique qui ne nĂ©cessitait pas l'environnement d'exĂ©cution mono - MRE. Mais comme vous avez besoin d'une licence supplĂ©mentaire pour distribuer de tels fichiers exĂ©cutables, il a Ă©tĂ© dĂ©cidĂ© d'abandonner les versions monolithiques et de simplement exiger MRE. La version Linux ne diffĂšre de la version Windows que par la prise en charge des attributs de fichier spĂ©cifiques aux systĂšmes * nix. Pour mon deuxiĂšme projet, qui sera plus qu'un simple patcher, je prĂ©vois d'utiliser une approche modulaire sous la forme d'un processus noyau qui s'exĂ©cute en arriĂšre-plan et permet de tout gĂ©rer sur l'interface locale. Et le contrĂŽle lui-mĂȘme peut ĂȘtre effectuĂ© Ă partir d'une application basĂ©e sur Electron et similaires (ou simplement Ă partir d'un navigateur). Avec n'importe quelle petite chose. Avant de parler de la taille de la distribution de ces applications - regardez la taille des jeux. Les versions de dĂ©monstration (!!!) de certains occupent 5 gigaoctets ou plus dans l'archive (!!!).
â Les structures utilisĂ©es actuellement ne permettent pas d'Ă©conomiser de l'espace lorsque le jeu est publiĂ© pour 3 plates-formes - de facto, vous devez conserver 3 copies de donnĂ©es presque identiques, bien que compressĂ©es.
â La version actuelle du patcher ne met pas en cache son travail - chaque fois que toutes les sommes de contrĂŽle de tous les fichiers sont recalculĂ©es. Il serait possible de rĂ©duire considĂ©rablement le temps si le patcher mettait en cache les rĂ©sultats des fichiers dĂ©jĂ prĂ©sents sur le client. Mais il y a un dilemme - si le fichier est endommagĂ© (ou manquant), mais l'entrĂ©e de cache pour ce fichier est enregistrĂ©e, le patcher l'ignorera, ce qui entraĂźnera des problĂšmes.
â La version actuelle ne peut pas fonctionner simultanĂ©ment avec plusieurs serveurs (sauf si vous effectuez un Round-robin en utilisant DNS) - je voudrais passer Ă une technologie de type torrent afin que vous puissiez utiliser plusieurs serveurs en mĂȘme temps. Il n'est pas question d'utiliser les clients comme source de donnĂ©es, car cela pose de nombreux problĂšmes juridiques et il est plus facile de refuser dĂšs le dĂ©part.
â Si vous souhaitez restreindre l'accĂšs aux mises Ă jour, cette logique devra ĂȘtre implĂ©mentĂ©e indĂ©pendamment. En fait, cela peut difficilement ĂȘtre qualifiĂ© d'inconvĂ©nient, car chacun peut avoir ses propres souhaits concernant les restrictions. La restriction de clĂ© la plus simple - sans aucune partie serveur - est rendue assez simple, comme je l'ai montrĂ© ci-dessus.
â Un correctif est créé pour un seul projet Ă la fois. Si vous souhaitez crĂ©er quelque chose de similaire Ă Steam, un systĂšme complet de distribution de contenu est dĂ©jĂ requis. Et c'est un projet d'un niveau complĂštement diffĂ©rent.
Je prĂ©vois de mettre moi-mĂȘme le patcher dans le domaine public aprĂšs la mise en Ćuvre de la «deuxiĂšme gĂ©nĂ©ration» - un systĂšme de livraison de contenu de jeu qui comprendra non seulement le patcher Ă©voluĂ©, mais aussi un module de tĂ©lĂ©mĂ©trie (car les dĂ©veloppeurs doivent savoir ce que font les joueurs), Module Cloud Save et quelques autres modules.
Si vous avez un projet à but non lucratif et avez besoin d'un patcher, écrivez-moi les détails de votre projet et je vous en donnerai une copie gratuitement. Il n'y aura pas de liens ici, car ce n'est pas le hub «I PR».
Je me ferai un plaisir de répondre à vos questions.