Questo sito usa i cookie per migliorare la tua esperienza di navigazione. Continuando a navigare su questo sito acconsenti a usare i nostri cookie. Ottieni maggiori informazioni Ignora
Stai usando un browser non supportato. Alcune caratteristiche potrebbero non funzionare correttamente. Per favore controlla il nostro elenco di browser supportati per una migliore esperienza. Ignora

Torchlight 3

Mise à jour des développeurs : victoire sur la Guerre des codes

Di Fero | ven 18 dic 2020 10:00:00 PST

Consultez nos pages DiscordFacebookTwitter et Twitch pour obtenir les toutes dernières nouvelles et mises à jour.

 

 

La mise à jour des développeurs nous vient de Guy Somberg, Programmeur en chef chez Echtra. Parfois la meilleure manière de comprendre le futur d'un projet, c'est d'en étudier son passé. Bien que la prochaine mise à jour se plongera dans les choses à venir, les problèmes persistants, et les feedbacks de la communauté, ce blog vous permettra d'apprécier l'aspect technique de notre développement en interne ou « Guerre des codes ».

 

Introduction

Au cours du développement, tous les problèmes ne concernent pas les performances, les fonctions et fonctionnalités que le joueur verra. Parfois, vous devez corriger quelque chose de compliqué, et il faut se retrousser les manches pour que ce soit fait.

Il y a une expression que nous utilisons pour ce genre de tâches : « Tondre le yak ». Cela vient du programme télé « Ren et Stimpy », et fait référence à une tâche qui semble complètement indépendante de l'objectif final, mais qu'il faut effectuer afin de l'atteindre. Par exemple, « Je veux construire un pont de pierre pour traverser ce ruisseau. Je dois d'abord tondre un yak afin de donner ma fourrure au fabriquant de laine, qui me laissera en échange utiliser sa charrette pour transporter des pierres depuis la carrière ». Tondre le yak n'est pas nécessaire en soi pour construire le pont, mais vous ne pourrez pas progresser tant que vous n'aurez pas sa fourrure !

Voici une collection de quelques exemples où il a fallu tondre le yak afin de résoudre des problèmes.

 

0D0D0A

Le logiciel de gestion de versions est un des outils fondamentaux que les développeurs de jeux (ou plutôt les développeurs de manière générale) utilisent. C'est une base de données contenant l'historique complet de tous les fichiers qui constituent notre jeu : code source, modèles, sons, et ainsi de suite.

Il y a environ 3 ans, nous avons changé notre logiciel de gestion de versions pour un autre ; peu importe lequel. Le système que nous utilisions ne fonctionnait plus sous la charge que nous lui imposions, il nous en fallait donc un nouveau. Nous avons fait nos recherches, examiné les alternatives et fait notre choix.

Il faut savoir qu'en changeant de logiciel de gestion de versions, il y a deux méthodes possibles. La plus simple est de bloquer l'accès à tout le monde, faire une copie du contenu le plus récent, l'importer dans le nouveau système, le modifier pour qu'il soit conforme et mettre le nouveau système à la disposition de tout le monde. Cette méthode présente l'avantage d'avoir fait ses preuves, mais elle supprime tout l'historique de la source d'avant le transfert, car on ne peut pas aller au-delà du moment de l'import. Si vous voulez vérifier cet historique, il faut aller voir l'ancien logiciel. Ce qui n'est pas un problème tant qu'il est en place ou y avez accès, mais bien souvent, il n'existe plus.

La méthode plus complexe consiste à importer l'historique du vieux système vers le nouveau. La plupart des logiciels le permettent, mais cela prend du temps, peut créer des erreurs et demande une intervention manuelle pour le rendre conforme aux particularités du nouveau système. Bien que cela requiert plus de travail en amont, c'est au final inestimable d'avoir accès à l'historique de la source dans son ensemble.

Nous avons choisi d'importer l'historique, qui (en apparence) semblait bien se passer. Nous avons vu l'historique, les fichiers, et avons effectué des vérifications qui allaient dans ce sens. Certains fichiers avaient des erreurs d'espaces étranges, mais ça n'avait pas l'air bien grave.

Mais lorsque nous avons tenté de compiler, tout s'est effondré. Le compilateur Visual Studio a signalé des erreurs de « Mac line endings » et a refusé de marcher.

Comment ?! Pourquoi ça n'allait pas ?

Un peu de contexte : lorsqu'un ordinateur veut représenter un caractère, il doit choisir un encodage. Le plus commun de nos jours est appelé UTF-8, il peut coder tous les caractères anglais, signes de ponctuation, et un certain nombre de codes un seul octet de données. (En utilisant plusieurs octets, vous pouvez encoder des données dans n'importe quelle langue, mais c'est un autre sujet.)

Deux de ces codes sont Carriage Return/retour chariot (CR) et Ligne Feed/saut de ligne (LF), qui sont un reste de l'époque où les ordinateurs étaient connectés à une machine à écrire plutôt qu'un écran. À cette époque, nous indiquiez à l'imprimante de revenir à la colonne de départ en envoyant le code CR, et vous voyiez le papier défiler à la ligne suivante en envoyant le code FL. Ainsi pour commencer une nouvelle ligne, il fallait taper CR LF.

Lorsque le changement vers un moniteur a eu lieu, la convention CR LF est restée pour une raison de rétrocompatibilité. Cependant, les développeurs sur de nouveaux systèmes, comme l'Apple Macintosh et Unix chez Bell Labs, n'avaient pas à se soucier de problème de compatibilité et étaient libres de faire comme ils voulaient.

Il s'avère que les 3 systèmes les plus répandus dans le monde de nos jours ont fait des choix différents : DOS a choisi CR LF, Macintosh a choisi CR et Unix a choisi LF. Windows a hérité de DOS, et MacOS utilise désormais LF (comme Unix).

Avec le temps, les différences se sont réglées d'elles-mêmes. Le logiciel est en général capable d'opérer en mode texte et de fournir à l'utilisateur les codes dont il a besoin en fonction du système. Les détails de ces différences fuitent parfois, mais c'est rarement un problème.

C'est à cela que nous avons pensé quand nous avons vu le message d'erreur à propos des « Mac line endings ». Mais pourquoi apparaissait-il ? Nous développons sur Windows, donc toutes les lignes devraient se terminer par CR LF, ou au pire une combinaison de Windows et Unix (LF). D'où ces codes CR pouvaient bien venir ?

Puis nous nous sommes souvenu du problème d'espaces étranges. Tout notre code source apparaissait avec des doubles espaces. D'où provenaient-ils ?

Quelqu'un a eu l'idée de regarder le fichier dans un éditeur hex, un outil qui permet de voir les représentations binaires des fichiers texte en affichant la valeur de chaque octet en hexadécimal. D'ordinaire, sur un fichier avec les codes Windows, il faut s'attendre à voir une ligne de texte, puis un CR (13 ou 0D dans les représentations hexadécimales) et un LF (10 ou 0A). Pour une raison inconnue, nous avions CR (0D), puis un autre CR (0D), et ensuite un LF (0A), ce qui donnait 0D0D0A.

Sans savoir pourquoi, pendant la conversion de la source d'un logiciel de gestion de versions vers l'autre, le programme a décidé que le fichier avait des codes Unix et a effectué un remplacement de tous les LF avec des CR LF, alors que les CR étaient déjà présent ! Tout s'expliquait. Notre éditeur remplaçait les CR par des lignes vides, et il savait comment remplacer les CRLF en lignes vides, ce qui expliquait pourquoi notre code affichait un double espace. À contrario, le compilateur Visual Studio interprétait CR LF comme une nouvelle ligne et voyait le CR qui précédait comme une erreur.

J'ai corrigé cela en écrivant un petit programme dans C++. Il a passé au crible notre code source et ouvert chaque fichier texte pour trouver les 0D0D0A et les remplacer avec 0D0A. Nous n'avons pas l'intention de changer de logiciel de contrôle des versions à nouveau, alors le code de cet outil est perdu dans les sables du temps. (Erratum : un disque contenant la source de ce programme a été découvert depuis, et nous avons envoyé ce code vers notre espace de stockage pour la postérité.)

Seulement deux ou trois d'entre nous ont travaillé sur ce problème particulier, mais le seul fait de prononcer « oh doh doa » nous provoque des crises d'angoisse.

 

Problème de dièse

Il y a quelques années, notre designer sonore et notre compositeur ont fait un voyage à Bratislava en Slovaquie pour enregistrer certains morceaux du jeu avec un orchestre. C'était génial (d'après ce qu'on m'a dit), et ils ont produit énormément en l'espace de quelques jours.

Un des résultats de ce voyage consistait en un ensemble de « vzory », « motifs » en slovaque. Il s'agit de petites pièces orchestrales pouvant être combinées de multiples façons pour créer de nouveaux morceaux, et qui sont enregistrées dans diverses combinaisons de touches et des notes. On se retrouve avec un motif particulier en G, G#, F, F# (Sol, Sol dièse, Fa, Fa dièse), etc.

Notre compositeur a fait la chose la plus naturelle : il a édité et organisé tout ce contenu, l'a réparti dans des dossiers et fichiers correspondants aux notes dans lesquelles ils avaient été enregistrés, et les a importés dans notre outil audio, FMOD Studio. Cet outil est lié au logiciel de gestion de versions, et il a ajouté tous ces nouveaux fichiers sans problème.

Tout allait bien jusqu'à ce que les gens reçoivent des alertes à propos des noms de fichiers à partir de notre dernier logiciel de gestion de versions. Il s'agissait simplement d'alertes n'empêchant pas de travailler, mais il fallait tout de même résoudre ce problème.

Les pistes vzory étaient les coupables. Il s'avère que notre logiciel n'aime pas les fichiers contenant un croisillon (#, parfois appelé, hash, hashtag, et dièse- à tort), dans le nom du fichier. Il les acceptera, mais ne sera pas content. Or notre compositeur avait nommé les fichiers et leurs destinations après les pistes vzory et leurs notes A sharp (La dièse) devient « A# » (et ainsi de suite pour les autres morceaux en dièse).

Le logiciel était très mécontent de ce choix.

Renommer les fichiers n'était pas suffisant, car FMOD garde une trace du fichier et de ses méta-données dans des fichiers XML, disposant chacun d'un GUID (une séquence de lettre, nombres et symboles).

Une fois de plus, un code est venu à la rescousse. Cette fois-ci il a été écrit dans C# (ironie du sort), il a vérifié tous les fichiers et trouvé ceux contenant un croisillon dans leur nom, et les a renommés en remplaçant le # par le mot sharp (dièse). Ansi A# est devenu Asharp (La dièse). Puis il a fait de même dans les fichiers XML du dossier, trouver tous ceux ayant une dièse dans le contenu du fichier (qui était donc des méta-données sur les fichiers ou des répertoires qui avaient été renommés), et remplacer le « # » dans cette ligne par le mot « sharp ».

À Part dire à nos collègues de ne pas le faire, il n'y a pas grand-chose que l'on puisse faire pour empêcher ce problème de ne pas se produire à nouveau. Cette fois-ci nous avons conservé le code source, donc si un autre problème survient, nous avons l'outil prêt pour le corriger.

 

Manque de PO

La localisation et l'internationalisation sont des parties importantes de tout projet de jeu. Nous utilisons l'Unreal engine, qui a un ensemble d'outils de localisation intégrés. En utilisant une certaine structure de données dans nos fichiers, Unreal peut trouver toutes les lignes localisées dans le jeu. Nous pouvons ensuite les exporter dans un format standardisé appelé un fichier « portable object » (.po), utilisé notamment par les outils gettext GNU.

Il s'agit d'un format avec lesquels nos traducteurs peuvent travailler. Ils prennent les fichiers, traduisent le texte, et les renvoient. Nous pouvons ensuite les importer dans un endroit spécifique et Unreal affiche le texte. Simple comme bonjour, tant que vous restez dans les lignes et suivez la méthode qu'Unreal veut que vous suiviez.

Naturellement, nous avons construit quelques trucs qui nous sont propres à l'intérieur d'Unreal et qui marchent très bien avec lui, mais qui sont suffisamment à part pour ne pas être reconnus par certains des autres systèmes d'Unreal. Un de ces systèmes est celui de segments de texte localisés, qui ne voyait aucun de nos merveilleux outils.

Nous avons écrit un outil pour les rendre visibles, et pensions en avoir terminé. Notre premier passage de localisation est parti chez les traducteurs. Nous avons essayé de l'importer... pour nous rendre compte que rien n'était importé !

Que s'est-il passé ?

Unreal vous permet d'identifier chaque chaîne de caractères localisée avec une paire de segments de texte : une catégorie et une entrée dans cette catégorie. Si vous ne fournissez pas l'une ou l'autre de ces entrées, il les générera pour vous. Il semble que l'outil que nous avions écrit pour rendre nos ressources visibles pour les traducteurs a généré une nouvelle entrée et catégorie pour chaque ligne de texte localisée et chaque fois qu'il est utilisé, ce qui signifie que chaque ligne de code recevait un code différent à chaque fois que nous importions ou exportions du texte.

Oh, mince. Nous avons résolu le problème sous-jacent et rendu les catégories et paires d'entrées consistantes à chaque version, mais nous avions ce bloc de texte massif dans toutes les langues qui était incompatible avec les données réparées ! Il nous a fallu trouver comment effectuer une réparation unique de ces segments pour les faire correspondre.

Bien entendu, chaque chaîne de caractères venait avec un tas de méta-données sur son contexte et sa provenance La plupart d'entre elles n'a pas changé, ou alors de façon prévisible. Ces méta-données étaient suffisamment complètes pour que nous puissions comparer une ligne importée et une ligne nouvellement exportée et faire correspondre les segments.

Comme auparavant, écrire un code était ici la solution. Nous avons écrit un programme (encore en C++) pour lire dans le fichier traduit (contenant les vieilles catégories/paires d'entrées incorrectes) et un fichier en langue anglaise nouvellement importé (contenant les nouvelles catégories et paires d'entrées correctes), les associer par méta-données, puis écrire une version corrigée du fichier contenant le texte traduit avec les catégories et paires d'entrées correctes.

Voici une situation où simplement corriger les données n'était pas suffisant. Nous avons dû résoudre en premier le problème sous-jacent avant de pouvoir écrire l'outil pour corriger les données.

 

Conclusion

Ces problèmes avaient tous un thème en commun : à travers une suite d'événements, d'erreurs humaines ou informatiques, un nombre de fichiers importants a été abimé d'une façon ou d'une autre. Au final, pour les personnes qui travaillent sur les données, cela n'a pas d'importance comment les choses sont arrivées. Il s'agit de prendre ce qui est cassé et de le réparer. C'est toujours gratifiant de trouver la cause d'un problème et d'y remédier afin qu'il ne se reproduise plus, mais parfois il faut vraiment mettre le pied à l'étrier. Tous les analyses a posteriori ou le travail préventif du monde ne servirait à rien s'il n'y avait pas des personnes pour réparer les fichiers abimés.

Les exemples dont j'ai parlé ici étaient tous très importants et devaient être accomplis, mais les trois programmes créés n'ont servi qu'une seule fois chacun. Il se trouve que bon nombre des systèmes que nous créons sont complexes, et ce genre de problème fait partie du développement normal d'un jeu lorsqu'on découvre des cas atypiques.

Parfois, vous devez créer un outil de toutes pièces pour ne l'utiliser qu'une seule fois, mais ce n'est pas un problème... Il faut simplement serrer les dents et tondre le yak.

- Guy Somberg

 

 

tl3-news, tl3-general, tl3-frontpage, tl3-featured, tl3-dev,

Più recenti Altro

Un'introduzione del lupo di mare, il Pirata maledetto.
Leggi altro
Date un'occhiata alle Patch notes del nostro nuovissimo aggiornamento di Torchlight 3!
Leggi altro
La classe del Capitano maledetto arriverà con il prossimo aggiornamento di Torchlight III...
Leggi altro

hover media query supported