Arma: Créer une extension en GOLANG

Je profite d’un peu de temps pour t’annoncer une excellente nouvelle 🙂 Il est possible de rapidement créer une extension ARMA en utilisant du GOLANG.

Mais tout d’abord parlons un peu de ce qu’est une extension ? Une extension est en fait une librairie dynamique qui va permettre d’étendre les fonctionnalités du jeu en lui même.

Cela se passe donc à plus bas niveau que le sqf, au même niveau que le moteur de jeu ARMA. Les extensions permettent par exemple de créer des interactions avec des bases de données, avec des services web distants, avec des objets connectés, ou avec d’autres programmes extérieurs TS, Discord, etc 🙂

Je te joins une vidéo d’un d’objet connecté que j’avais développé en 2012 en utilisant une extension ARMA: Détecteur IED

Techniquement parlant une extension est une .dll sous windows, ou .so sous linux.

Les extensions doivent se trouver dans le répertoire d’ARMA, ou dans un répertoire @mods, et peuvent être soit chargée côté client en utilisant le paramètre au lancement -mod=tonextension et côté serveur -servermod=tonextension.

Tu peux trouver des exemples d’extension en C, C++, C# dans les tools ARMA sur Steam . Chacun des 3 langages a ses propres particularités avantages et inconvénients.

Mieux encore tu n’es pas limité à ces 3 langages, tu peux en utiliser d’autres du moment que tu respectes l’interface communiquée par BIS.

Quand tu développes une extension, il faut penser portabilité, car tu auras certainement besoin de rendre ton extension également disponible pour les serveurs Linux. C’est la raison pour laquelle aujourd’hui je te parle du GOLANG.

Je n’ai pas la certitude que les extensions 32Bits soient encore supportées par Arma, c’est la raison pour laquelle je ne parlerais ici que des extensions 64bits.

Petit rappel: Si tu utilises l’extension d’un autre développeur, une extension a les mêmes droits qu’un programme normal. Ainsi avant de lancer une extension, tu dois t’assurer que le développeur de l’extension est au dessus de tout soupçon sous peine d’avoir des problèmes de sécurité.

Coup d’oeil sur l’interface BIS
Bis a mis à disposition des développeurs une interface pour permettre aux extensions d’interagir avec ARMA.

Comment cela fonctionne ?
Pour exécuter le code de ton extension à partir de ton code sqf, tu dois utiliser la commande callExtension de cette façon.

_return = "tonxtension" callExtension "lesparametres";

Comme tu le vois, le code d’une extension est synchrone car il attend un résultat et il est exécuté en mode non schédulé. Le callExtension ne rendra donc pas la main tant que ton code ne sera pas exécuté entièrement (ce qui peut provoquer des freezes). Il faudra donc être regardant sur l’optimisation de l’extension.

Coté extension, l’interface à respecter est la suivante:

void RVExtension(char *output, int outputsize, const char *input);

Pour une meilleur compréhension, BIS nous demande de créer une méthode dans le code de notre extension qui s’appellera RVExtension. Cette méthode est le point d’entré de ton extension qui sera appelé par le callExtension.

Cette méthode ne doit rien renvoyer (void) car elle donne en fait en paramètre un espace mémoire dans lequel écrire le résultat:

  • un pointeur vers une variable de type char : le pointeur pointe sur le premier espace dans la mémoire ou tu as le droit commencer à écrire le résultat
  • un entier : qui te donne la taille de l’espace dans lequel tu as le droit d’écrire à partir du pointeur précédent
  • un pointeur vers une variable de type char : le pointeur pointe sur les informations qui sont envoyés par ARMA.

Pour que cette méthode soit utilisable et visible à partir d’ARMA, il faut également l’exporter. Bien maintenant que nous avons vu les grandes lignes, passons au code en GOLANG 🙂

Le code en GOLANG

Au préalable, tu dois installer le compileur gcc 64bits TDD, tu trouveras tous les détails dans le README Armago extension .

Je te joins en copie un code que j’ai conservé plus simple pour me concentrer sur l’essentiel.

package main

/*
#include stdlib.h
#include stdio.h
#include string.h
*/

import "C"
import (
    "fmt"
    "unsafe"
)

Comme tu peux le voir il faut importer le package C, et inclure des headers de libraire C (j’ai du supprimer des caractères car non compatible avec le blog). Il n’y a pas de nécessité de déplacer les .h dans le répertoire de ton extension. Le compileur GO va les trouver tout seul.

Il faut également ajouter le package unsafe qui va nous permettre de gérer la libération de la mémoire quand l’extension ne sera plus utilisé.

Déclaration de l’interface et du main

//export RVExtension
func RVExtension(output *C.char, outputsize C.int, input *C.char) {
    temp := fmt.Sprintf("Hello %s !", C.GoString(input))
    result := C.CString(temp)
    defer C.free(unsafe.Pointer(result))
    var size = C.strlen(result) + 1
    C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
}

func main() {}

Dans un premier temps, on exporte la méthode via la ligne commentée //export. Ici, cette méthode va modifier la chaine de caractère passées en paramètre par le callExtension en sqf.

Comme tu peux le voir il faut utiliser les commandes du package C pour convertir les variables type C en type GOLANG(C.GoString), et ensuite de type GOLANG vers type C(C.CString). Les variables de type C ne sont pas libérés par le garbage collector c’est la raison pour laquelle on utilise le defer C.free pour libérer l’espace dès qu’il n’est plus utilisé.

La commande c.memmove permet de copier le résultat dans la mémoire à partir du pointeur de sortie jusqu’à une taille définie. Il faut s’assurer que cette taille ne dépasse pas l’espace donnée par ARMA en INPUT, sinon cela provoquera le plantage du jeu avec un access violation. Ce contrôle n’est pas fait ici dans cette exemple.

Ta fonction main ne doit rien contenir car il s’agit d’une librairie.

Une fois ton code GO, réalisé tu peux build ta .dll ou ton .so
go build -o armago_x64.dll -buildmode=c-shared armago_x64.go

Il ne te reste plus qu’à tester sous ARMA en déplaçant ta .dll ou ton.so dans le répertoire d’Arma 🙂 Petit rappel, le nom de fichier de ton extension doit se terminer impérativement par _x64.dll ou _x64.so.

_result = "tonextension" callExtension "John";
hint format ["Resultat: %1", _result];

En espérant que cela va t’aider pour tes futurs projets. N’hésite pas à forker Armago Extension qui est un exemple prêt à l’emploi.

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s