Hou.la Connect · Documentation développeur
Documentation développeur de Hou.la Connect
Hou.la Connect joue, sur le PC du streamer, les réactions aux événements d'un live. Cette documentation décrit comment y brancher un jeu, un programme ou un appareil, et comment écrire les packs qui les pilotent.
Bases
Vue d'ensemble
Connect reçoit les événements d'un live (cadeaux, likes, messages, commandes) depuis Hou.la et d'autres plateformes, puis joue des actions sur le PC du streamer. Trois objets décrivent ce comportement. Ce sont des fichiers JSON validés à la publication : aucun ne contient de code exécuté par Connect.
Pack
Associe des déclencheurs (quand) à des chaînes d'actions (alors). Un pack s'écrit dans le Lab de Connect, à la main ou avec une IA, et se partage dans le Store.
Plugin de jeu
Décrit un jeu ou un programme : ses actions, leurs réglages et son mode de liaison (mod, rcon, telnet, rcon-evrima, ws ou http). Un plugin par jeu, utilisable par tous les packs.
Connexion
Les paramètres d'accès propres à un streamer : adresse, port, mot de passe, jeton. Saisie dans « Mes plugins », elle reste sur son PC. Un pack ne contient aucun identifiant : il désigne une connexion par son rôle.
Tout s'exécute sur le PC du streamer. Les réglages sont validés à la publication, puis de nouveau avant chaque envoi.
Ce que tu peux construire
- Un plugin de jeu : rendre un jeu pilotable par les packs, au moyen d'un mod, de RCON, d'un WebSocket ou d'une API HTTP.
- Une intégration avec un programme ou un appareil : un outil local, un logiciel de stream, un objet connecté, par HTTP, WebSocket, MQTT ou OSC.
- Un pack : des interactions prêtes à installer, à partager dans le Store.
Tutoriel
Démarrage rapide : un plugin HTTP
Ce tutoriel relie un serveur HTTP local à Connect par un plugin de jeu en mode http, puis joue une de ses actions depuis un pack, sans être en live.
Prérequis : Hou.la Connect installé et connecté à ton compte, et Node.js pour le serveur d'exemple. Tout langage capable de servir du HTTP convient pour ton propre programme.
Lance un serveur HTTP local
Enregistre ce code dans
server.js, puis lancenode server.js. Le serveur écoute sur127.0.0.1:7777, expose une routePOSTpar action et répond204quand l'action a réussi. Si la variableTOKENest définie, il exige l'en-têteAuthorization: Bearer <TOKEN>.server.js JavaScript// server.js : node server.js const http = require('node:http') const TOKEN = process.env.TOKEN || '' const ROUTES = new Set(['/actions/soin', '/actions/ennemi']) http .createServer((req, res) => { if (TOKEN && req.headers.authorization !== `Bearer ${TOKEN}`) { return res.writeHead(401).end() } let body = '' req.on('data', (chunk) => (body += chunk)) req.on('end', () => { if (req.method !== 'POST' || !ROUTES.has(req.url)) { return res.writeHead(404).end() } console.log(req.method, req.url, body) res.writeHead(204).end() }) }) .listen(7777, '127.0.0.1', () => console.log('http://127.0.0.1:7777'))Vérifie-le
Dans un autre terminal, envoie la requête que Connect enverra pour l'action
soin:Shellcurl -i -X POST http://127.0.0.1:7777/actions/soinRésultat attendu :
HTTP/1.1 204 No Content, et le serveur affichePOST /actions/soin. Une route inconnue rend404.Importe le plugin
Le fichier ci-dessous déclare deux actions :
soin(POST /actions/soin) etennemi(POST /actions/ennemi, avec un corps JSON). Dans Connect : « Mes plugins », « + Créer un plugin », « Un jeu », onglet « JSON », « Importer ». Termine avec « Ajouter mon jeu » : il s'installe sur ce PC, sans relecture.plugin-mode-http.json Télécharger ce fichierplugin-mode-http.json JSON{ "id": "mon-outil-compagnon", "nom": "Mon outil compagnon", "protocole": "jeu", "mode": "http", "description": "Exemple : un programme que le joueur lance à côté du jeu, et qui expose une API HTTP locale (son adresse se saisit dans « URL de base », par exemple http://127.0.0.1:7777).", "actions": [ { "id": "soin", "libelle": { "fr": "Soins : trousse", "en": "Health: medipack" }, "effet": "aide", "method": "POST", "path": "/actions/soin" }, { "id": "ennemi", "libelle": { "fr": "Ennemi : au choix", "en": "Enemy: pick one" }, "effet": "gene", "params": [ { "id": "quoi", "type": "choix", "defaut": "loup", "libelle": { "fr": "Ennemi", "en": "Enemy" }, "valeurs": [ { "id": "loup", "libelle": { "fr": "Loup", "en": "Wolf" } }, { "id": "ours", "libelle": { "fr": "Ours", "en": "Bear" } } ] } ], "method": "POST", "path": "/actions/ennemi", "json": { "type": "{param.quoi}", "de": "{sender.name}" } } ] }Crée la connexion
Dans « Mes plugins », carte « HTTP / API », « Configurer » : ajoute une connexion dont l'« URL de base » est
http://127.0.0.1:7777. Le jeton est facultatif ; s'il est rempli, Connect l'envoie dans l'en-têteAuthorization: Bearer …, ou dans l'en-tête nommé par la connexion. Quand un pack utilise ton plugin, Connect lui lie une connexion HTTP existante, ou ouvre ce formulaire s'il n'en trouve aucune.Joue une action depuis un pack
Dans le Lab, crée un pack et un écouteur (un cadeau, par exemple). Ajoute à sa chaîne un bloc « Dans le jeu », choisis « Mon outil compagnon », puis l'action « Soins : trousse ». Clique « Tester » : l'écouteur est joué tout de suite, sans live.
Résultat attendu : le serveur affiche
POST /actions/soin, et la notification du test indique l'action comme jouée. En cas d'échec, elle donne la cause : aucune réponse en 5 secondes, statut HTTP hors 2xx, ou aucune connexion liée au pack.
Étapes suivantes : ajoute des réglages à tes actions (Référence, « Format d'un plugin de jeu »), puis publie le plugin pour tous les streamers (Publication).
Concepts
Concepts
Les notions employées dans le reste de cette documentation.
Déclencheurs
Un déclencheur est l'événement qui lance une interaction : cadeau Hou.la (dont les cadeaux personnalisés), follow, message ou commande du chat, likes et partages, spectateurs, sondage, roue, raccourci du streamer, événements d'overlay ou de boutique, et événements de TikTok, YouTube et Kick ("type": "platform"). Une interaction accepte jusqu'à 8 déclencheurs : n'importe lequel lance la chaîne. Chaque déclencheur remplit ses propres variables, comme {sender.name} ou {gift.quantity}.
Actions et chaînes
Une action est ce que Connect joue : un son, une requête OBS, un message de chat, une action de jeu… Connect connaît quinze types d'actions (effect.type), listés dans la référence. Un pack choisit dans cette liste ; un type inconnu fait refuser le pack.
Une interaction (rules[i]) relie ses déclencheurs à une chaîne de blocs (do) jouée dans l'ordre : actions, attentes, conditions, tirages au sort, répétitions, compteurs, variables. La durée d'une chaîne est calculée avant de la jouer : 30 secondes au plus.
Connexions et rôles
Une action qui joint un service (RCON, OBS, HTTP, WebSocket, MQTT…) passe par une connexion du streamer. Le pack la désigne par un rôle : par défaut le type de l'action (rcon, http…), ou connector pour en nommer un autre ; un plugin de jeu utilise le rôle jeu:<id>. À l'installation d'un pack, Connect lie chaque rôle à une connexion existante du bon type, ou demande d'en créer une.
Modes de liaison d'un plugin de jeu
Le mode dit comment Connect joint le jeu. Il fixe les champs de la connexion que le streamer remplit, et la forme de chaque action.
Un mod dans le jeu
"mode": "mod"
Pour un jeu sans serveur ni API, que tu peux moder. Le mod se connecte au pont local de Connect. Hou.la héberge l'archive du mod ; Connect l'installe dans le dossier du jeu, trouvé par Steam.
Plugins officiels dans ce mode : 1
Le serveur du jeu, par RCON
"mode": "rcon"
Pour un jeu à serveur qui accepte des commandes RCON. Chaque action envoie 1 à 5 commandes, dans l'ordre. Connexion : hôte, port, mot de passe et pseudo en jeu.
Plugins officiels dans ce mode : 9
La console du serveur, par Telnet
"mode": "telnet"
Pour un serveur qui ouvre une console texte sur TCP, une commande par ligne (7 Days to Die). Chaque action envoie 1 à 5 lignes, dans l'ordre. Si le serveur demande un mot de passe à la connexion, Connect le donne. Une réponse « *** ERROR » est un échec. Connexion : hôte, port, mot de passe (facultatif) et pseudo en jeu. Telnet n'est pas chiffré.
Plugins officiels dans ce mode : 1
Le RCON d'Evrima (The Isle)
"mode": "rcon-evrima"
Le RCON binaire de The Isle (Evrima), pas celui de Source. Chaque action envoie 1 à 5 commandes nommées, prises dans une liste fermée : announce, entities:wipe:corpses, ai:toggle, ai:density, growth:multiplier:toggle, growth:multiplier:set, migrations:toggle, globalchat:toggle, humans:toggle, pause. Bannir, expulser et la liste blanche ne sont pas permis. Connexion : hôte, port (8888 par défaut) et mot de passe.
Plugins officiels dans ce mode : 1
Un WebSocket local
"mode": "ws"
Pour un jeu ou un programme qui écoute un WebSocket. Chaque action envoie un message texte de 2 000 caractères au plus. Connexion : l'adresse du WebSocket.
Plugins officiels dans ce mode : 0
Une API HTTP locale
"mode": "http"
Pour un jeu ou un programme qui expose une API HTTP. Chaque action est une requête GET, POST ou PUT sur un chemin, avec un corps JSON facultatif. Connexion : l'URL de base et, si besoin, un jeton. Si l'API demande le compte du joueur, le bloc oauth fait la connexion avec un compte (OAuth 2 avec PKCE) : Connect ouvre le navigateur sur la page de l'éditeur, garde le jeton de renouvellement chiffré et l'envoie en Authorization: Bearer.
Plugins officiels dans ce mode : 1
Cycle de vie : privé, en relecture, public
Un plugin et un pack passent par ces états.
- PrivéAjouté avec « Ajouter mon jeu » : installé sur ton PC, utilisable dans tes packs, invisible des autres.
- En relectureEnvoyé avec le premier pack publié dans le Store qui l'utilise : relu par Hou.la avant d'entrer dans le Store, en même temps que le pack. Le motif d'un refus de plugin s'affiche dans Connect.
- PublicVisible dans le Store et installable par tous les streamers. Toute modification repasse en relecture.
Guides
Guides
Des procédures pas à pas, classées par objectif.
Les trois façons de relier un jeu
Connect joint un jeu de l'une de trois façons, selon ce que le jeu offre déjà. Identifie la tienne avant d'écrire le plugin : elle fixe son mode, ce que tu écris, et ce que le streamer doit faire.
Le jeu accepte des commandes
Hou.la Connect RCON, WebSocket ou HTTPLe serveur ou l'API du jeu- Tu écris
- un plugin
- Le streamer
- saisit au plus l'adresse de son serveur
Le jeu accepte les mods
Hou.la Connect Pont local (WebSocket)Ton mod, chargé par le jeu- Tu écris
- un plugin et un mod
- Le streamer
- n'installe rien : Connect installe le mod
Le jeu n'a ni l'un ni l'autre
Hou.la Connect Pont local (WebSocket)Un mod que tu crées (recommandé)Ou : un programme externe, en HTTP ou WebSocket, qui agit sur le jeu- Tu écris
- un plugin, et un mod ou un programme
- Le streamer
- rien avec un mod ; lance le programme sinon
Le jeu a un serveur ou une API qui accepte des commandes
RCON, console Telnet, WebSocket ou HTTP, comme Minecraft, Counter-Strike 2, Rust ou 7 Days to Die. Un plugin suffit, en mode
rcon,telnet,rcon-evrima,wsouhttp: chaque action devient une commande, un message ou une requête. Le streamer n'installe rien d'autre que le pack ; il saisit au plus l'adresse de son serveur dans la connexion.Le jeu accepte les mods
Écris le mod une fois, puis envoie son archive dans l'assistant, mode « Un mod dans le jeu ». Hou.la l'héberge. Chez chaque streamer qui installe un pack de ce jeu, Connect installe le mod automatiquement dans le dossier du jeu. Exemple : Pratfall.
Le jeu n'a ni l'un ni l'autre
Crée-lui une porte d'entrée. La voie recommandée est un mod chargé par le jeu : même circuit qu'à la voie 2, rien à installer pour le streamer. À défaut, un programme externe qui expose une API locale, en mode
httpouws: le streamer doit le télécharger et le lancer lui-même, ce qui est plus contraignant.
Quelle voie pour mon jeu ?
Réponds aux questions dans l'ordre : la première réponse « oui » donne la voie.
| Question | Oui | Non |
|---|---|---|
| 1. Le jeu, ou son serveur, accepte-t-il des commandes RCON ou par une console Telnet, ou expose-t-il une API WebSocket ou HTTP ? | OuiVoie 1 : un plugin en mode rcon, telnet, rcon-evrima, ws ou http. | NonQuestion 2. |
| 2. Le jeu charge-t-il des mods ? | OuiVoie 2 : un plugin en mode mod, et ton mod. | NonQuestion 3. |
| 3. Peux-tu ajouter au jeu du code qu'il charge, par exemple en tant que développeur du jeu ? | OuiVoie 3, recommandée : crée ce mod, en mode mod, comme à la voie 2. | NonVoie 3, à défaut : un programme externe avec une API locale, en mode http ou ws. Le streamer le lance lui-même. |
Créer un plugin de jeu
Ce guide part du cas courant : écrire un pack pour un jeu que Connect ne connaît pas encore. Le plugin du jeu se trouve ou se crée depuis le pack, sans quitter le Lab. Un plugin de jeu est une fiche JSON : l'assistant de Connect l'écrit et la valide, et son onglet « JSON » importe et exporte le fichier.
Ouvre la liste des jeux depuis ton pack
Dans le Lab, onglet « Informations », ouvre le champ « Jeu » de la fiche du pack (le bloc « Dans le jeu » propose la même liste). Si ton jeu n'y figure pas, choisis « Mon jeu n'est pas dans la liste », puis « Ajouter mon jeu » : l'assistant s'ouvre sans quitter le pack.
Installe un plugin existant, s'il y en a un
Connect propose les plugins de la communauté pour les jeux qui ne sont pas encore installés sur ce PC. Un clic installe le plugin, mod compris, et le jeu est choisi dans le pack : passe à l'étape 8.
Sinon, ouvre l'assistant
« Ajouter mon jeu » ouvre l'assistant sans quitter le pack (il s'ouvre aussi depuis « Mes plugins », « + Créer un plugin », « Un jeu »). Ses étapes : « Le jeu », « La liaison », « Les actions », « Le mod » (mode
modseulement), « Terminer ». Si ton mod est déjà connecté au pont, le jeu est proposé « en développement » et l'assistant reprend les actions qu'il annonce.Le jeu
Nom, identifiant (minuscules, chiffres et tirets, 2 à 40 caractères ; le même que dans le mod), numéro Steam et description. Le numéro Steam est facultatif, mais Connect en a besoin pour trouver le dossier où installer un mod. Dans la description, indique ce que le streamer doit installer ou lancer.
La liaison
Choisis le mode selon ce que le jeu offre. Un serveur qui accepte RCON :
rcon. Une console texte sur TCP :telnet. Le serveur de The Isle :rcon-evrima. Une API HTTP ou un WebSocket, du jeu ou d'un programme compagnon :httpouws. Aucun des deux, mais un jeu modable :mod. Les champs de la connexion découlent du mode : le plugin ne les déclare pas.Les actions
Pour chacune : un nom (en français, et en anglais si tu le souhaites), une description, un effet (
aide,geneouneutre) et jusqu'à 8 réglages (un entier borné, ou un choix dans une liste). 60 actions au plus. Enrcon,wsethttp, écris le modèle de la commande, du message ou de la requête (Référence, « Format d'un plugin de jeu »).Le mod (mode mod)
Moteur, dossier d'installation relatif au dossier du jeu (3 niveaux au plus, sans
..), version (1.0.0), prérequis, puis l'archive.zip(100 Mo au plus). Hou.la l'héberge et en calcule l'empreinte. Le code du mod suit le guide « Écrire un mod qui parle au pont local ».Ajoute ton jeu, puis teste
Termine avec « Ajouter mon jeu » : le plugin s'installe sur ce PC, sans relecture. De retour dans le pack, le jeu est déjà choisi, et chaque bloc « Dans le jeu » ajouté le reprend : choisis une action et ses réglages, puis « Tester » joue l'écouteur tout de suite, sans live. Vérifie ainsi chaque action.
Publie ton pack, le plugin suit
Publie le pack « dans le Store (public) » : le plugin part en relecture avec lui, sans autre geste. Le pack entre dans le Store une fois le plugin validé ; d'ici là, Connect affiche « Attend son jeu » (voir Publication).
Règle de la plateforme : le streamer installe le pack, rien d'autre
Installer un pack installe automatiquement les plugins de jeu qu'il utilise et, en mode mod, dépose leur mod dans le dossier du jeu trouvé par Steam. Si une connexion manque (rcon, ws, http), Connect la demande à l'installation. Un jeu absent de ce PC n'empêche pas l'installation : Connect le signale et réessaie à la première action du pack. Seul le programme externe d'un plugin http ou ws s'installe à part, par le streamer.
Un exemple par mode
Chaque fichier passe le validateur de Hou.la. Importe-le dans l'assistant (onglet « JSON ») comme point de départ.
plugin-mode-mod.jsonUn mod dans le jeu
Les actions ne portent pas de modèle : le mod reçoit l'identifiant de l'action et ses réglages.
{
"id": "mon-jeu-godot",
"nom": "Mon jeu Godot",
"protocole": "jeu",
"mode": "mod",
"description": "Exemple : un jeu Godot 4 .NET piloté par un mod qui se relie à Hou.la Connect.",
"actions": [
{
"id": "ralenti",
"libelle": {
"fr": "Ralenti",
"en": "Slow motion"
},
"description": {
"fr": "Le jeu tourne au ralenti quelques secondes.",
"en": "The game runs in slow motion for a few seconds."
},
"effet": "gene",
"params": [
{
"id": "secondes",
"type": "entier",
"min": 1,
"max": 10,
"defaut": 3,
"libelle": {
"fr": "Durée (secondes)",
"en": "Duration (seconds)"
}
}
]
},
{
"id": "soigner",
"libelle": {
"fr": "Soigner",
"en": "Heal"
},
"effet": "aide",
"params": [
{
"id": "cible",
"type": "choix",
"defaut": "moi",
"libelle": {
"fr": "Qui",
"en": "Who"
},
"valeurs": [
{
"id": "moi",
"libelle": {
"fr": "Le streamer",
"en": "The streamer"
}
},
{
"id": "tous",
"libelle": {
"fr": "Tout le monde",
"en": "Everyone"
}
}
]
}
]
}
],
"mod": {
"moteur": "godot",
"dossier": "mods/MonMod",
"version": "1.0.0",
"prerequis": {
"fr": "Aucun : le jeu charge les mods de son dossier mods.",
"en": "None: the game loads mods from its mods folder."
}
}
}plugin-mode-rcon.jsonLe serveur du jeu, par RCON
{param.valeur} est remplacé par le réglage choisi dans le pack ; {sender.name} et {gift.name} par les données de l'événement.
{
"id": "mon-serveur-rcon",
"nom": "Mon serveur RCON",
"protocole": "jeu",
"mode": "rcon",
"description": "Exemple : un jeu à serveur dédié qui accepte des commandes RCON.",
"actions": [
{
"id": "gravite",
"libelle": {
"fr": "Changer la gravité",
"en": "Change gravity"
},
"effet": "neutre",
"params": [
{
"id": "valeur",
"type": "entier",
"min": 100,
"max": 2000,
"defaut": 200,
"libelle": {
"fr": "Gravité (800 = normale)",
"en": "Gravity (800 = normal)"
}
}
],
"commandes": [
"sv_gravity {param.valeur}"
]
},
{
"id": "annonce",
"libelle": {
"fr": "Annonce",
"en": "Announcement"
},
"effet": "neutre",
"commandes": [
"say {sender.name} a offert {gift.name} !"
]
}
]
}plugin-mode-ws.jsonUn WebSocket local
Le message est du texte, ici du JSON. Pour un réglage à choix, la valeur (goblin) remplace l'identifiant du choix.
{
"id": "mon-jeu-websocket",
"nom": "Mon jeu WebSocket",
"protocole": "jeu",
"mode": "ws",
"description": "Exemple : un jeu qui écoute déjà un WebSocket sur ce PC (son adresse se saisit dans la connexion, par exemple ws://127.0.0.1:8123).",
"actions": [
{
"id": "ennemis",
"libelle": {
"fr": "Faire apparaître des ennemis",
"en": "Spawn enemies"
},
"effet": "gene",
"params": [
{
"id": "nombre",
"type": "entier",
"min": 1,
"max": 10,
"defaut": 3,
"libelle": {
"fr": "Nombre",
"en": "Count"
}
},
{
"id": "type",
"type": "choix",
"defaut": "petit",
"libelle": {
"fr": "Type",
"en": "Type"
},
"valeurs": [
{
"id": "petit",
"libelle": {
"fr": "Petits",
"en": "Small"
},
"valeur": "goblin"
},
{
"id": "gros",
"libelle": {
"fr": "Gros",
"en": "Big"
},
"valeur": "troll"
}
]
}
],
"message": "{\"action\":\"spawn\",\"enemy\":\"{param.type}\",\"count\":{param.nombre},\"by\":\"{sender.name}\"}"
}
]
}plugin-mode-http.jsonUne API HTTP locale
Un chemin par action, un corps JSON facultatif. C'est le plugin du démarrage rapide.
{
"id": "mon-outil-compagnon",
"nom": "Mon outil compagnon",
"protocole": "jeu",
"mode": "http",
"description": "Exemple : un programme que le joueur lance à côté du jeu, et qui expose une API HTTP locale (son adresse se saisit dans « URL de base », par exemple http://127.0.0.1:7777).",
"actions": [
{
"id": "soin",
"libelle": {
"fr": "Soins : trousse",
"en": "Health: medipack"
},
"effet": "aide",
"method": "POST",
"path": "/actions/soin"
},
{
"id": "ennemi",
"libelle": {
"fr": "Ennemi : au choix",
"en": "Enemy: pick one"
},
"effet": "gene",
"params": [
{
"id": "quoi",
"type": "choix",
"defaut": "loup",
"libelle": {
"fr": "Ennemi",
"en": "Enemy"
},
"valeurs": [
{
"id": "loup",
"libelle": {
"fr": "Loup",
"en": "Wolf"
}
},
{
"id": "ours",
"libelle": {
"fr": "Ours",
"en": "Bear"
}
}
]
}
],
"method": "POST",
"path": "/actions/ennemi",
"json": {
"type": "{param.quoi}",
"de": "{sender.name}"
}
}
]
} Créer un mod
Un mod Hou.la est une archive .zip que Connect extrait dans le dossier du jeu. Connect n'exécute jamais son contenu : c'est le jeu qui le charge, par son propre système de mods ou par un chargeur comme BepInEx.
Relie le mod à Connect
Une fois chargé par le jeu, le mod se connecte au pont local de Connect, un WebSocket ouvert sur le PC du streamer. Il annonce le jeu et ses actions, reçoit chaque action et répond. Le protocole est décrit dans le guide « Écrire un mod qui parle au pont local » et dans la référence « Protocole du pont ».
Prépare l'archive
Place à la racine de l'archive ce qui doit se trouver dans le dossier d'installation, rangé comme le jeu l'attend : la racine de l'archive devient ce dossier. Un installateur ou un exécutable n'y sert à rien : aucun fichier de l'archive n'est lancé. Si le jeu a besoin d'un chargeur de mods, indique-le dans les prérequis du plugin : Connect les affiche à l'installation, sans les installer.
Choisis le dossier d'installation
Un chemin relatif au dossier du jeu, par exemple
mods/MonJeuouBepInEx/plugins/MonJeu: 3 niveaux et 120 caractères au plus, chaque niveau fait de lettres non accentuées, de chiffres, de_,.et-, sans..ni chemin absolu. Connect trouve le dossier du jeu par Steam, grâce au numéro Steam du plugin. Pour un jeu hors de Steam, Connect n'installe pas le mod : le streamer l'installe à la main, d'après la description du plugin.Respecte les limites de l'archive
Zip classique (ni zip64 ni chiffrement), fichiers stockés ou compressés en deflate. 100 Mo au plus, 250 Mo une fois décompressée, 500 entrées au plus. Chaque chemin est relatif, séparé par
/, de 200 caractères au plus, sans..ni antislash, et ne contient que des lettres non accentuées, des chiffres, des espaces et_ . ( ) + -. Aucun lien symbolique, aucun fichier en double. Une archive hors de ces règles est refusée dès l'envoi, avec la raison.Installation et mises à jour
Connect télécharge l'archive, vérifie son empreinte, contrôle chaque chemin, puis écrit chaque fichier à côté avant de le renommer : le jeu ne charge jamais un fichier à moitié écrit. Sous Windows, un jeu lancé verrouille souvent ses fichiers : Connect demande alors de fermer le jeu. Change la version du mod à chaque nouvelle archive : Connect compare la version installée à celle du plugin et met le mod à jour quand elles diffèrent.
La relecture
« Ajouter mon jeu » installe le plugin sur ton PC seulement, pour tes tests. Publier un pack public qui l'utilise envoie le plugin et son mod en relecture : un mod est du code, il est toujours relu par une personne avant d'être proposé aux autres streamers. Chaque nouvelle version repasse en relecture.
Note
Des kits de mod existent pour Godot .NET, Unity (BepInEx 5) et Lua, mais ils ne sont pas en téléchargement public. Le protocole du pont s'implémente directement, dans tout langage qui dispose d'un client WebSocket.
À lire ensuite : Écrire un mod qui parle au pont local · Protocole du pont (mode mod)
Écrire un mod qui parle au pont local
En mode mod, le mod est le client et Connect le serveur : le jeu n'ouvre aucun port, et le mod se reconnecte quand Connect redémarre. Les messages sont du JSON texte sur un WebSocket local. Le détail de chaque message est dans la référence (« Protocole du pont »).
Lis le fichier d'appairage
Au démarrage, Connect écrit
~/.houla/pont-jeu.json:{ "v": 1, "port": 30212, "jeton": "<48 caractères hexadécimaux>" }. Le jeton change à chaque démarrage de Connect et le fichier est effacé à sa fermeture : relis-le avant chaque tentative de connexion.Connecte-toi
Ouvre
ws://127.0.0.1:<port>/pont/v1avec l'en-têteX-Houla-Pont: <jeton>, sans en-têteOrigin.Envoie bonjour dans les 5 secondes
Envoie
bonjouravec l'identifiant du jeu, la version du mod et ses actions. En version 2, chaque action est décrite (identifiant, libellé, réglages) et l'assistant s'en sert pour préremplir le plugin. Connect répondbienvenue.Reçois les actions
Chaque message
actionporte unid, l'identifiant de l'action, ses réglages et l'auteur (de: pseudo, quantité, nom du cadeau). Joue l'action dans le jeu.Réponds dans les 5 secondes
Renvoie
faitavec le mêmeid:"ok": true, ou"ok": falseavec uneraison(codes de refus dans la référence) et undetailfacultatif. Sans réponse en 5 secondes, l'action échoue.Teste avant d'avoir un plugin
Un identifiant de jeu valide sans plugin est accepté et affiché « en développement » dans « Mes plugins » : crée ensuite le plugin depuis ce jeu. Une seconde connexion du même jeu remplace la première (code de fermeture
4004).
Note
Aucun kit de mod n'est proposé en téléchargement public, et il n'existe pas de kit pour Unreal Engine. Le protocole s'implémente dans tout langage qui dispose d'un client WebSocket.
Piloter un programme externe ou un objet connecté
Connect ne lance aucun programme. Un programme externe (outil compagnon d'un jeu, logiciel de ton choix) tourne à côté de Connect et expose une API locale ; un plugin de jeu en mode http ou ws lui envoie les actions.
Un programme local avec une API
Expose une API locale
Ton programme ouvre une API HTTP (ou un WebSocket) sur
127.0.0.1, à un port fixe. Une route par action :POST /actions/soin,POST /actions/ennemi… Réponds par un statut 2xx quand l'action a réussi. Délai maximal : 5 secondes en HTTP, 4 en WebSocket ; tout autre statut est affiché comme un échec dans le journal du live.Publie un plugin en mode http
Une action par route, avec ses réglages. Dans la description du plugin, indique où télécharger ton programme et quelle adresse saisir dans la connexion.
Le streamer installe ton programme
Il le télécharge depuis ton site, le lance, puis saisit dans Connect l'« URL de base » (par exemple
http://127.0.0.1:7777) et un jeton si ton programme en exige un.Les packs utilisent tes actions
Dans le Lab, le bloc « Dans le jeu » propose ton plugin et ses actions. Une adresse locale passe obligatoirement par une connexion : un pack ne peut pas écrire
http://127.0.0.1lui-même.
Un logiciel ou un objet connecté courant
Les plugins officiels génériques couvrent les protocoles courants : HTTP / API, WebSocket, MQTT (Zigbee2MQTT, Tasmota…), OSC (VRChat, TouchDesigner, Resolume), RCON, OBS et Streamlabs. D'autres sont prêts à l'emploi : Home Assistant, Elgato Key Light, Discord, Telegram, webhook. Un pack les utilise directement avec les actions http, ws, mqtt ou osc, par une connexion du streamer.
Écrire un pack
Le Lab écrit le JSON d'un pack à ta place ; les recettes ci-dessous montrent les constructions courantes. Chaque fichier passe le validateur de Hou.la et s'importe dans le Lab : onglet « Écouteurs », « JSON », « Importer un fichier… », puis « Vérifier mon pack ».
pack-multi-sources.jsonPlusieurs plateformes, une seule action
Une interaction à cinq déclencheurs : un cadeau personnalisé Hou.la à 100 pièces, un cadeau TikTok d'au moins 99 diamants, un Super Chat YouTube, 100 KICKs ou 100 bits Twitch. Chacun joue la même action de jeu (plugin Minecraft). Les événements Twitch sont acceptés par le format ; leur réception n'est pas encore disponible dans Connect.
{
"schema": 3,
"rules": [
{
"id": "creeper",
"label": "Creeper",
"on": [
{
"type": "gift",
"giftSlug": "ix_slot_01",
"coinCost": 100,
"group": "Pagaille"
},
{
"type": "platform",
"platform": "tiktok",
"event": "gift",
"min": 99
},
{
"type": "platform",
"platform": "youtube",
"event": "superchat"
},
{
"type": "platform",
"platform": "kick",
"event": "kicks",
"min": 100
},
{
"type": "platform",
"platform": "twitch",
"event": "bits",
"min": 100
}
],
"do": [
{
"kind": "action",
"id": "creeper",
"effect": {
"type": "jeu",
"jeu": "minecraft",
"action": "invoquer",
"params": {
"creature": "creeper",
"position": "devant_3",
"qui": "joueur"
}
}
}
]
}
]
}pack-paliers-et-montants.jsonPaliers, switch et repeat
Les likes de Hou.la et de TikTok passent par la même chaîne ; un switch sur hearts.palier choisit la réaction (trois fanfares à 10 000, avec repeat). Les partages lisent leur total. Un cadeau quelconque est routé selon coins, et repeat joue cinq klaxons quand la quantité atteint 5.
{
"schema": 3,
"rules": [
{
"id": "paliers-likes",
"label": "Paliers de likes",
"on": [
{
"type": "hearts",
"milestones": [
1000,
5000,
10000
]
},
{
"type": "platform",
"platform": "tiktok",
"event": "like_total",
"milestones": [
1000,
5000,
10000
]
}
],
"do": [
{
"kind": "switch",
"id": "selon-palier",
"conditions": [
{
"all": [
{
"left": "hearts.palier",
"op": ">=",
"right": 10000
}
]
},
{
"all": [
{
"left": "hearts.palier",
"op": ">=",
"right": 5000
}
]
}
],
"branches": [
[
{
"kind": "repeat",
"id": "trois-fanfares",
"times": 3,
"gapMs": 1500,
"branches": [
[
{
"kind": "action",
"id": "fanfare",
"effect": {
"type": "sound",
"soundId": "34f7bfb4-b51f-11f1-a1b0-920007989f15",
"volume": 0.8
}
}
]
]
}
],
[
{
"kind": "action",
"id": "wow",
"effect": {
"type": "sound",
"soundId": "2ebf44c5-b51a-11f1-a1b0-920007989f15"
}
}
]
],
"else": [
{
"kind": "action",
"id": "merci",
"effect": {
"type": "chat",
"text": "{hearts.palier} likes, merci à tous !",
"cooldownMs": 10000
}
}
]
}
]
},
{
"id": "paliers-partages",
"label": "Paliers de partages",
"on": {
"type": "share_total",
"milestones": [
10,
50,
100
]
},
"do": [
{
"kind": "action",
"id": "voix",
"effect": {
"type": "tts",
"text": "{shares.total} partages, merci !"
}
}
]
},
{
"id": "selon-montant",
"label": "Selon le montant du cadeau",
"on": {
"type": "gift",
"minCoins": 1
},
"do": [
{
"kind": "switch",
"id": "selon-pieces",
"conditions": [
{
"all": [
{
"left": "coins",
"op": ">=",
"right": 500
}
]
},
{
"all": [
{
"left": "coins",
"op": ">=",
"right": 100
}
]
}
],
"branches": [
[
{
"kind": "action",
"id": "gros",
"effect": {
"type": "sound",
"soundId": "34f7bfb4-b51f-11f1-a1b0-920007989f15"
}
}
],
[
{
"kind": "repeat",
"id": "klaxons",
"times": 1,
"gapMs": 600,
"cases": [
{
"when": {
"all": [
{
"left": "quantity",
"op": ">=",
"right": 5
}
]
},
"times": 5
}
],
"branches": [
[
{
"kind": "action",
"id": "klaxon",
"effect": {
"type": "sound",
"soundId": "301b847c-b529-11f1-a1b0-920007989f15"
}
}
]
]
}
]
],
"else": [
{
"kind": "action",
"id": "petit",
"effect": {
"type": "chat",
"text": "Merci {sender.name} pour {gift.name} !",
"cooldownMs": 5000
}
}
]
}
]
}
]
}pack-commandes-chat.jsonCommandes de chat
!klaxon est gratuite, limitée à une fois toutes les 30 secondes par spectateur, et tire un son au hasard (70 / 30). !dis coûte 10 pièces et lit le texte du spectateur à voix haute. « !gg » est écouté sur les chats de Hou.la, TikTok, YouTube et Kick.
{
"schema": 3,
"rules": [
{
"id": "commande-klaxon",
"label": "Klaxon",
"on": {
"type": "command",
"name": "klaxon",
"description": "Joue un klaxon au hasard",
"perViewerMs": 30000
},
"enchainement": "ignorer",
"do": [
{
"kind": "random",
"id": "tirage",
"weights": [
70,
30
],
"branches": [
[
{
"kind": "action",
"id": "police",
"effect": {
"type": "sound",
"soundId": "051db1d2-b523-11f1-a1b0-920007989f15",
"maxMs": 3000
}
}
],
[
{
"kind": "action",
"id": "dj",
"effect": {
"type": "sound",
"soundId": "301b847c-b529-11f1-a1b0-920007989f15"
}
}
]
]
}
]
},
{
"id": "commande-dis",
"label": "Dis-le à voix haute",
"on": {
"type": "command",
"name": "dis",
"description": "Le streamer lit ton message à voix haute",
"coinCost": 10,
"args": "texte",
"who": "tous"
},
"do": [
{
"kind": "action",
"id": "lire",
"effect": {
"type": "tts",
"text": "{sender.name} dit : {commande.texte}"
}
}
]
},
{
"id": "mot-gg",
"label": "GG dans le chat",
"on": {
"type": "comment",
"startsWith": "!gg",
"platforms": [
"houla",
"tiktok",
"youtube",
"kick"
]
},
"enchainement": "ignorer",
"do": [
{
"kind": "action",
"id": "roulement",
"effect": {
"type": "sound",
"soundId": "5703a3d3-b522-11f1-a1b0-920007989f15",
"volume": 0.6
}
}
]
}
]
}pack-cadeaux-personnalises.jsonCadeaux personnalisés
Quatre cadeaux personnalisés (ix_slot_01 à 04), chacun avec son nom, son prix dans l'échelle fermée et son rayon, reliés aux actions d'un plugin de jeu (Pratfall). L'icône d'un cadeau se règle ensuite dans le Lab.
{
"schema": 3,
"rules": [
{
"id": "bombes",
"label": "Pluie de bombes",
"on": {
"type": "gift",
"giftSlug": "ix_slot_01",
"coinCost": 30,
"group": "Pagaille"
},
"do": [
{
"kind": "action",
"id": "bombes",
"effect": {
"type": "jeu",
"jeu": "pratfall",
"action": "bombes",
"params": {
"nombre": 3,
"cible": "hasard"
}
}
}
]
},
{
"id": "soins",
"label": "Soins",
"on": {
"type": "gift",
"giftSlug": "ix_slot_02",
"coinCost": 45,
"group": "Coup de main"
},
"do": [
{
"kind": "action",
"id": "soigner",
"effect": {
"type": "jeu",
"jeu": "pratfall",
"action": "soigner",
"params": {}
}
}
]
},
{
"id": "klaxon",
"label": "Klaxon",
"on": {
"type": "gift",
"giftSlug": "ix_slot_03",
"coinCost": 5,
"group": "Ambiance"
},
"do": [
{
"kind": "action",
"id": "klaxon",
"effect": {
"type": "jeu",
"jeu": "pratfall",
"action": "klaxon",
"params": {}
}
}
]
},
{
"id": "resurrection",
"label": "Résurrection",
"on": {
"type": "gift",
"giftSlug": "ix_slot_04",
"coinCost": 280,
"group": "Coup de main"
},
"do": [
{
"kind": "action",
"id": "ressusciter",
"effect": {
"type": "jeu",
"jeu": "pratfall",
"action": "ressusciter",
"params": {
"cible": "hasard"
}
}
}
]
}
]
}pack-outil-compagnon-http.jsonUn programme local en HTTP
Deux cadeaux qui pilotent un plugin officiel en mode http (Tomb Raider I-III Remastered) ; l'action de soin répond aussi à un cadeau TikTok précis (giftId 5655, la Rose).
{
"schema": 3,
"rules": [
{
"id": "trousse",
"label": "Trousse de soins",
"on": [
{
"type": "gift",
"giftSlug": "ix_slot_01",
"coinCost": 20,
"group": "Coup de main"
},
{
"type": "platform",
"platform": "tiktok",
"event": "gift",
"giftId": "5655"
}
],
"do": [
{
"kind": "action",
"id": "trousse",
"effect": {
"type": "jeu",
"jeu": "tomb-raider-remastered",
"action": "small_medkit",
"params": {}
}
}
]
},
{
"id": "trex",
"label": "T-Rex",
"on": {
"type": "gift",
"giftSlug": "ix_slot_02",
"coinCost": 490,
"group": "Pagaille"
},
"do": [
{
"kind": "action",
"id": "annonce",
"effect": {
"type": "chat",
"text": "{sender.name} lâche un T-Rex sur Lara !",
"cooldownMs": 5000
}
},
{
"kind": "action",
"id": "trex",
"effect": {
"type": "jeu",
"jeu": "tomb-raider-remastered",
"action": "trex",
"params": {}
}
}
]
}
]
} Générer un pack avec une IA
- Donne à l'IA le fichier
guide-ia.mddu kit et l'adresse de cette page, puis décris le pack : ce que les spectateurs déclenchent, et ce qui se passe. - Si le pack joue des sons, ajoute la liste des sons : dans Connect, « Sons », « Exporter la liste… » (ou « Liste des sons… » dans le Lab).
- L'IA rend un fichier JSON. Dans le Lab : onglet « Écouteurs », « JSON », « Importer un fichier… », puis « Vérifier mon pack ».
- Règle ensuite dans le Lab ce qu'une IA ne peut pas connaître : l'icône d'un cadeau personnalisé, les overlays du streamer, les connexions.
Référence
Référence
Formats et protocoles, champ par champ. Les validateurs sont fermés : une clé inconnue fait refuser le fichier entier. La liste exhaustive des déclencheurs, de leurs variables et de l'échelle des prix est dans guide-ia.md, généré depuis Connect.
Format d'un plugin de jeu
Un libellé est une chaîne, ou un objet { "fr": "…", "en": "…" }.
| Clé | Règle |
|---|---|
id | Identifiant du plugin : ^[a-z0-9][a-z0-9-]{1,39}$. Unique dans le Store, et identique à celui que le mod annonce. |
nom | Nom du jeu, 60 caractères au plus. |
protocole | Toujours "jeu". |
description | Facultative, 400 caractères au plus. Indique ce qu'il faut installer ou lancer. |
jeu.steamAppId | Facultatif : le numéro de la page Steam du jeu (store.steampowered.com/app/4244510/… donne 4244510). Sans lui, Connect ne peut pas installer de mod. |
mode | mod, rcon, telnet, rcon-evrima, ws ou http. |
mod | Mode mod seulement : moteur (godot, unity, lua, autre), dossier, version, prerequis. Le bloc fichier (adresse, empreinte, taille) est rempli par Hou.la à l'envoi de l'archive. |
oauth | Mode http seulement, facultatif : autorisation (page de connexion, https), jeton (adresse d'échange et de renouvellement, https), scopes (1 à 20 droits), clientId (l'application que tu as enregistrée chez l'éditeur), port (facultatif, de 1024 à 65535 : l'adresse de rappel devient http://127.0.0.1:<port>/callback, à déclarer chez l'éditeur ; sans lui, un port libre). Aucun secret client : la connexion utilise PKCE. |
actions[].id | Identifiant de l'action : a-z, 0-9, _ et -, 40 caractères au plus. |
actions[].libelle | Nom de l'action, 80 caractères au plus. |
actions[].description | Facultative, 300 caractères au plus. |
actions[].effet | aide, gene ou neutre (par défaut). Le Lab l'affiche à côté de l'action. |
actions[].params | 8 au plus. entier : min, max, defaut. choix : valeurs (60 au plus) et defaut ; une valeur peut porter une valeur, substituée dans le modèle à la place de son identifiant (sauf en mode mod, où le mod reçoit l'identifiant). |
actions[].commandes | Modes rcon, telnet et rcon-evrima : 1 à 5 commandes, une ligne chacune, 500 caractères au plus. |
actions[].message | Mode ws : le message, 2 000 caractères au plus. |
actions[].method / path / json | Mode http : method (GET, POST, PUT), path ajouté à l'URL de base de la connexion (commence par /, sans ..), json facultatif (un objet, 2 000 caractères au plus). |
Variables d'un modèle
{param.<réglage>} : la valeur d'un réglage de l'action. {player} et les autres champs non secrets de la connexion ; un mot de passe ou un jeton n'est jamais inséré dans un modèle. Les données de l'événement : {sender.name} (le pseudo du spectateur), {gift.name}, {gift.quantity}… Chaque valeur est échappée selon sa destination (adresse, JSON, texte).
Format d'un pack (schéma 3)
Un pack est un objet JSON. Squelette minimal :
{ "schema": 3, "rules": [ { "id": "…", "label": "…", "on": [ … ], "do": [ … ] } ] }Clés du pack
| Clé | Règle |
|---|---|
schema | Toujours 3. |
rules | Les interactions, 300 au plus. |
chatbot / profiles / maxPlayers | Facultatives : réponses automatiques du chat, configurations clavier ou manette, nombre de joueurs simultanés d'un pack manette. |
slug | Facultatif : le Lab y met l'identifiant du pack. |
Clés d'une interaction (rules[i])
| Clé | Règle |
|---|---|
id | Identifiant unique dans le pack, 60 caractères au plus. Garde-le stable d'une version à l'autre : les réglages des streamers y sont attachés. |
label | Nom de l'interaction, 120 caractères au plus. Pour un cadeau personnalisé, c'est aussi le nom affiché sur sa tuile. |
on | Un déclencheur, ou une liste de 8 au plus : n'importe lequel lance la chaîne. |
do | La chaîne de blocs, jouée dans l'ordre. |
followersOnly / moderatorsOnly | true : réservée aux abonnés, ou aux modérateurs. |
enchainement | Si l'interaction se déclenche pendant qu'elle tourne : suite (par défaut, les demandes attendent leur tour), ensemble ou ignorer. |
profile | L'identifiant d'une configuration de profiles (clavier, manette). Absent : toutes. |
Déclencheurs (on)
Chaque déclencheur a un type et ses propres réglages (seuil, palier, cadeau précis…). Les variables qu'il remplit et ses réglages exacts sont listés dans guide-ia.md.
| Famille | type | Description |
|---|---|---|
| Cadeaux et commandes | giftcommandpriority | Un cadeau Hou.la (du catalogue, ou personnalisé ix_slot_01, ix_slot_02… avec son prix et son rayon), une commande de chat déclarée par le pack (gratuite ou payante), un message prioritaire payé. |
| Audience | followjoinviewerheartsshareshare_total | Follow, arrivée d'un spectateur, tous les N spectateurs, likes et partages (un palier, une liste de paliers, ou tous les N). |
| Chat | comment | Un message : début (startsWith), mot contenu (contains) ou tous les N messages, sur les chats choisis (Hou.la, TikTok, YouTube, Kick). |
| Live | pollgoalwheelhotkey | Sondage, objectif de cadeaux, roue du streamer, raccourci clavier du streamer. |
| Overlays | chronomatchobjectif | Fin d'un chrono, fin d'un match, objectif atteint, sur un overlay du streamer. |
| Boutique | productauctionpurchasepromo | Produit épinglé, fin d'enchère, achat payé, code promo ajouté au live. |
| Autres plateformes | platform | Un événement de TikTok, YouTube ou Kick : platform, event, et selon l'événement min, giftId ou des paliers. Les événements Twitch sont acceptés par le format, sans être reçus pour l'instant. |
Blocs d'une chaîne (do)
Chaque bloc a un kind et un id unique dans son interaction (lettres, chiffres, -, _, 24 caractères au plus).
kind | Champs principaux | Description |
|---|---|---|
action | effect, holdMs, do | Joue une action (effect). holdMs : temps d'attente après l'action, 10 s au plus. |
delay | ms, do | Attend ms millisecondes, 10 s au plus. |
if | when, do, else | Si la condition when est vraie, joue do, sinon else. |
switch | conditions, branches, else | 1 à 20 conditions, une branche par condition : seule la première condition vraie part. else est obligatoire. |
random | branches, weights | 2 à 10 branches, une seule tirée au sort. weights : un poids de 1 à 1000 par branche. |
parallel | branches | 2 à 4 branches, jouées en même temps. Pas de parallel dans un parallel. |
repeat | times, branches, gapMs, cases | Répète une branche times fois (50 au plus), avec une pause gapMs. cases choisit le nombre de tours selon une condition. Pas de repeat dans un repeat. |
counter | name, op, value | Un compteur de la session : inc, dec ou set. Se lit dans une condition : counter:<nom>. 8 compteurs par pack. |
var | name, value | Pose {var.<nom>} pour la suite de la chaîne. Se lit dans une condition : var:<nom>. 8 par interaction. |
Conditions
{ "all": [ … ] } (toutes vraies) ou { "any": [ … ] } (une suffit), 3 clauses au plus. Une clause compare left à une valeur écrite (right, jamais une variable) avec ==, !=, <, <=, >, >= ou contains. left : le montant (coins), la quantité, le message, le palier de likes (hearts.palier), le montant d'une autre plateforme (platform.amount), un tirage de 0 à 99 (random), un compteur, une variable…
{ "all": [ { "left": "coins", "op": ">=", "right": 100 } ] }Variables dans les textes
Dans un texte (chat.text, tts.text, rcon.command…), {sender.name}, {gift.name}, {hearts.palier}, {commande.texte}… sont remplacées au moment de jouer. Seules les variables remplies par un déclencheur de l'interaction ont une valeur ; une variable inconnue fait refuser le pack. Une variable n'est jamais admise dans un identifiant (son, overlay, réglage de jeu, scène).
Bornes
48 blocs par interaction et 1200 par pack, 6 niveaux d'imbrication, 30 s de durée calculée par chaîne, 50 tours pour un repeat, 10 s par attente.
Protocole du pont (mode mod)
Un WebSocket local, des messages JSON texte. Versions acceptées : 1 et 2.
| Appairage | ~/.houla/pont-jeu.json : v, port (30212), jeton (48 caractères hexadécimaux, nouveau à chaque démarrage de Connect). Fichier effacé à la fermeture de Connect. |
|---|---|
| Adresse | ws://127.0.0.1:<port>/pont/v1 |
| En-têtes | X-Houla-Pont: <jeton> obligatoire. Aucun en-tête Origin. Host : 127.0.0.1 ou localhost. |
| Délais | bonjour dans les 5 s après la connexion ; fait dans les 5 s après une action. Connect envoie un ping WebSocket toutes les 15 s : un client qui ne répond pas est déconnecté. |
Messages
Codes de refus (raison)
raison | Signification |
|---|---|
pas_en_partie | Aucune partie en cours (menu, chargement). |
mode_interdit | Le mode de jeu en cours refuse les actions des spectateurs. |
pas_maintenant | L'action n'a pas pu être jouée à ce moment. |
sans_effet | Rien à faire (vie déjà pleine, personne à ranimer). |
action_inconnue | Le mod installé ne connaît pas cette action (version trop ancienne). |
params | Réglages refusés par le mod. |
erreur | Erreur dans le jeu, précisée dans detail. |
pas_hote | Seul l'hôte de la partie peut recevoir les actions. |
delai | Posé par Connect : aucune réponse fait en 5 secondes. |
deconnecte | Posé par Connect : le mod s'est déconnecté avant de répondre. |
Codes de fermeture
| Code | Signification |
|---|---|
4001 | Aucun bonjour reçu dans les 5 secondes. |
4002 | Version de protocole non prise en charge. |
4003 | Identifiant de jeu invalide. |
4004 | Remplacée par une nouvelle connexion du même jeu. |
1001 | Connect se ferme. |
Garanties du serveur
- Il n'écoute que sur
127.0.0.1. - Le jeton est exigé et comparé en temps constant ; absent ou faux, la connexion est refusée (401).
- Tout en-tête
Originest refusé (403) : une page web ouverte sur le PC ne peut pas se faire passer pour un jeu. - L'en-tête
Hostdoit nommer127.0.0.1oulocalhost, ce qui bloque le DNS rebinding. - Un message fait 64 Ko au plus. Une action ne part que si un plugin la déclare, avec des réglages validés.
Types d'actions (effect)
Le champ type d'un effect choisit l'exécuteur. Les champs exacts et leurs valeurs admises sont dans guide-ia.md.
type | Champs principaux | Description |
|---|---|---|
jeuAction de jeu | jeuactionparams | Une action d'un plugin de jeu, avec ses réglages écrits en clair (jamais une variable). |
soundSon | soundIdlocalFilevolumeinterruptmaxMs | Un son de la bibliothèque Hou.la (soundId), ou un fichier du PC désigné par un rôle que le streamer relie (localFile, jamais un chemin). Volume, coupure du son précédent, durée maximale. |
chatChat | textcooldownMs | Écrit dans le chat Hou.la du live, 300 caractères au plus, avec au moins 2 secondes entre deux messages (cooldownMs). |
ttsSynthèse vocale | textlangvoiceratevolumevoiceModehoulaVoicevoiceSetpitchvoiceEffect | Lit un texte à voix haute, 300 caractères au plus : une voix du système (voice), ou une voix Hou.la (voiceMode : houla avec houlaVoice, random, ou viewer pour une voix fixe par spectateur, qu'il choisit avec !voix). voiceSet limite les voix permises, pitch (-8 à 8 demi-tons) et voiceEffect (robot, radio, echo) s'ajoutent. Les voix Hou.la (16 voix, 31 langues) sont calculées sur le PC du streamer ; sans elles, la voix du système lit. |
overlayOverlay | overlayIdcommandevaleurtexte | Pilote un overlay de la bibliothèque du streamer : afficher, masquer, ajouter, retirer, fixer, demarrer, arreter. |
keyboardClavier | keysgapMs | Touches, combinaisons (shift+c), suites (up,up,down), maintien (space:400). Le jeu doit être au premier plan. Désactivé par défaut. |
gamepadManette virtuelle | buttonbuttonssequencestepsanalog | Une manette Xbox virtuelle : boutons, sticks, gâchettes, séquences. Windows seulement (pilote ViGEmBus). Désactivée par défaut. |
obsOBS Studio | requestparams | Une requête OBS WebSocket v5 prise dans une liste fermée : scène, sources, filtres, micro, médias, transitions, enregistrement, replay, caméra virtuelle. Démarrer ou couper le stream est exclu. |
streamlabsStreamlabs Desktop | commandscenesource | Une commande d'une liste fermée : scènes, sources, son, enregistrement, replay. Démarrer le stream est exclu. |
voicemodVoicemod | commandvoicesounddurationMs | Change de voix, joue un son de la soundboard. Voicemod doit être lancé (Windows). Aucune variable. |
rconRCON | commandconnector | Une commande à un serveur de jeu (Minecraft, Rust, ARK…). Les verbes destructeurs (stop, ban, op, whitelist…) sont refusés. |
httpHTTP | methodurlpathconnectorjson | Une requête GET, POST ou PUT, 5 secondes au plus. Par une connexion : son adresse, même locale, son jeton, et la réponse est lue ({reponse.…}). Par une adresse écrite dans le pack : jamais ce PC ni le réseau local, ni jeton, ni redirection, et la réponse n'est pas lue. |
wsWebSocket | messageurlconnector | Un message texte, par une connexion ou par une adresse écrite dans le pack (mêmes règles que HTTP pour ce PC et le réseau local). |
mqttMQTT | topicpayloadqosretainconnector | Un message sur un topic, vers le courtier de la connexion du streamer. |
oscOSC | addressargshostport | Un message OSC en UDP, par défaut vers 127.0.0.1:9000. UDP ne confirme pas la réception : réussi signifie envoyé. |
Plugins de jeu officiels
Livrés avec Connect, au même format que les plugins de la communauté. Utilise-les dans tes packs, ou lis-les comme exemples.
| Jeu | id | Mode | Actions |
|---|---|---|---|
| Pratfall | pratfall | mod | 16 actions |
| Minecraft | minecraft | rcon | 8 actions |
| 7 Days to Die | 7-days-to-die | telnet | 8 actions |
| ARK: Survival Evolved | ark-survival-evolved | rcon | 6 actions |
| Counter-Strike 2 | counter-strike-2 | rcon | 9 actions |
| Factorio | factorio | rcon | 4 actions |
| Left 4 Dead 2 | left-4-dead-2 | rcon | 6 actions |
| Project Zomboid | project-zomboid | rcon | 10 actions |
| Rust | rust | rcon | 5 actions |
| Squad | squad | rcon | 4 actions |
| Team Fortress 2 | team-fortress-2 | rcon | 7 actions |
| The Isle | the-isle | rcon-evrima | 9 actions |
| Tomb Raider I Remastered (TRR Director) | tomb-raider-remastered | http | 31 actions |
Extraits
Un plugin officiel par mode représenté : l'en-tête et les deux premières actions, tels que Connect les charge.
pratfallPratfall · mod · 16 actions
{
"id": "pratfall",
"nom": "Pratfall",
"protocole": "jeu",
"mode": "mod",
"jeu": {
"steamAppId": 4244510
},
"actions": [
{
"id": "bombes",
"libelle": {
"fr": "Pluie de bombes",
"en": "Bomb rain",
"it": "Pioggia di bombe",
"es": "Lluvia de bombas",
"pt": "Chuva de bombas"
},
"effet": "gene",
"params": [
{
"id": "nombre",
"type": "entier",
"libelle": {
"fr": "Nombre de bombes",
"en": "Number of bombs",
"it": "Numero di bombe",
"es": "Número de bombas",
"pt": "Número de bombas"
},
"min": 1,
"max": 5,
"defaut": 3
},
{
"id": "cible",
"type": "choix",
"libelle": {
"fr": "Qui",
"en": "Who",
"it": "Chi",
"es": "Quién",
"pt": "Quem"
},
"defaut": "moi",
"valeurs": [
{
"id": "moi",
"libelle": {
"fr": "Toi",
"en": "You",
"it": "Tu",
"es": "Tú",
"pt": "Você"
}
},
{
"id": "hasard",
"libelle": {
"fr": "Un joueur au hasard",
"en": "A random player",
"it": "Un giocatore a caso",
"es": "Un jugador al azar",
"pt": "Um jogador aleatório"
}
}
]
}
]
},
{
"id": "mine",
"libelle": {
"fr": "Mine foreuse",
"en": "Digging mine",
"it": "Mina scavatrice",
"es": "Mina excavadora",
"pt": "Mina escavadora"
},
"effet": "gene",
"params": [
{
"id": "cible",
"type": "choix",
"libelle": {
"fr": "Qui",
"en": "Who",
"it": "Chi",
"es": "Quién",
"pt": "Quem"
},
"defaut": "moi",
"valeurs": [
{
"id": "moi",
"libelle": {
"fr": "Toi",
"en": "You",
"it": "Tu",
"es": "Tú",
"pt": "Você"
}
},
{
"id": "hasard",
"libelle": {
"fr": "Un joueur au hasard",
"en": "A random player",
"it": "Un giocatore a caso",
"es": "Un jugador al azar",
"pt": "Um jogador aleatório"
}
}
]
}
]
}
]
}counter-strike-2Counter-Strike 2 · rcon · 9 actions
{
"id": "counter-strike-2",
"nom": "Counter-Strike 2",
"protocole": "jeu",
"mode": "rcon",
"jeu": {
"steamAppId": 730,
"alias": [
"CS2",
"Counter Strike 2",
"CS"
],
"moteur": "source"
},
"actions": [
{
"id": "gravite",
"libelle": {
"fr": "Changer la gravité (triche)",
"en": "Change gravity (cheat)",
"it": "Cambia la gravità (trucco)",
"es": "Cambiar la gravedad (truco)",
"pt": "Mudar a gravidade (trapaça)"
},
"effet": "neutre",
"params": [
{
"id": "gravite",
"type": "entier",
"libelle": {
"fr": "Gravité (800 = normale)",
"en": "Gravity (800 = normal)",
"it": "Gravità (800 = normale)",
"es": "Gravedad (800 = normal)",
"pt": "Gravidade (800 = normal)"
},
"min": 100,
"max": 2000,
"defaut": 200
}
],
"commandes": [
"sv_gravity {param.gravite}"
]
},
{
"id": "bunny_hop",
"libelle": {
"fr": "Bunny hop",
"en": "Bunny hop",
"it": "Bunny hop",
"es": "Bunny hop",
"pt": "Bunny hop"
},
"effet": "aide",
"params": [
{
"id": "etat",
"type": "choix",
"libelle": {
"fr": "État",
"en": "State",
"it": "Stato",
"es": "Estado",
"pt": "Estado"
},
"defaut": "oui",
"valeurs": [
{
"id": "oui",
"libelle": {
"fr": "Activé",
"en": "On",
"it": "Attivo",
"es": "Activado",
"pt": "Ativado"
},
"valeur": "1"
},
{
"id": "non",
"libelle": {
"fr": "Désactivé",
"en": "Off",
"it": "Disattivo",
"es": "Desactivado",
"pt": "Desativado"
},
"valeur": "0"
}
]
}
],
"commandes": [
"sv_enablebunnyhopping {param.etat}",
"sv_autobunnyhopping {param.etat}"
]
}
]
}tomb-raider-remasteredTomb Raider I Remastered (TRR Director) · http · 31 actions
{
"id": "tomb-raider-remastered",
"nom": "Tomb Raider I Remastered (TRR Director)",
"protocole": "jeu",
"mode": "http",
"jeu": {
"steamAppId": 2478970,
"alias": [
"TRR",
"Tomb Raider Remastered",
"Tomb Raider 1",
"Tomb Raider I",
"Tomb Raider 1 Remastered",
"TRR Director"
],
"moteur": "autre"
},
"actions": [
{
"id": "small_medkit",
"libelle": {
"fr": "Soins : petite trousse de soins",
"en": "Health: small medipack",
"it": "Cure: medikit piccolo",
"es": "Curación: botiquín pequeño",
"pt": "Cura: kit médico pequeno"
},
"effet": "aide",
"method": "POST",
"path": "/small_medkit",
"json": {
"provider": "houla",
"viewer": "{sender.name}"
}
},
{
"id": "large_medkit",
"libelle": {
"fr": "Soins : grande trousse de soins",
"en": "Health: large medipack",
"it": "Cure: medikit grande",
"es": "Curación: botiquín grande",
"pt": "Cura: kit médico grande"
},
"effet": "aide",
"method": "POST",
"path": "/large_medkit",
"json": {
"provider": "houla",
"viewer": "{sender.name}"
}
}
]
}Sécurité
Limites et sécurité
Règles appliquées à tout pack, plugin et mod. Hou.la les contrôle à la publication, puis Connect avant chaque action.
Interdit à un pack, un plugin ou un mod
- Lancer un exécutable ou un script. Aucun pack, plugin ou mod ne peut lancer un programme : aucun champ ne désigne un fichier à exécuter. Pour piloter un programme externe, utilise le mode
httpouwsvers une API locale exposée par ce programme. - Viser ce PC ou le réseau local par une adresse écrite dans le pack (
127.0.0.1,192.168.…,localhost, ou un nom qui y mène). Seule une connexion saisie par le streamer, ou un hôte qu'il a approuvé, y accède. - Envoyer un jeton ou un mot de passe ailleurs que vers la connexion du streamer, ou suivre une redirection depuis une adresse écrite dans le pack.
- Insérer un champ secret d'une connexion (mot de passe, jeton, webhook) dans une commande ou une requête.
- Jouer une commande RCON destructrice :
stop,restart,op,ban,kick,whitelist,save-off,gamemode… - Utiliser le clavier, la manette ou le pilote bas niveau sans que le streamer les ait activés : ils sont désactivés par défaut.
- Démarrer ou couper le stream depuis OBS ou Streamlabs.
- Envoyer à un jeu une action ou un réglage que son plugin ne déclare pas, ou mettre une variable venue du chat dans un réglage.
- Porter une clé inconnue : chaque objet est reconstruit depuis une liste fermée, et une clé en trop fait refuser le fichier.
- Faire exécuter un mod par Connect. Connect vérifie l'empreinte de l'archive, contrôle son contenu (aucun chemin hors du dossier, aucun lien symbolique) et l'extrait dans le dossier du jeu ; c'est le jeu qui charge le mod.
Limites actuelles
Attention
- Les événements Twitch sont acceptés par le format de pack, mais Connect ne les reçoit pas encore.
- Un plugin de type « logiciel ou objet connecté » publié depuis l'assistant s'installe depuis le Store, mais ne se configure pas encore dans Connect. Pour piloter un programme, utilise un plugin de jeu en mode
httpouws. - Les chaînes autres que Hou.la ne sont écoutées que pendant un live Hou.la.
- Aucun kit de mod n'est en téléchargement public, et il n'existe pas de kit pour Unreal Engine.
- Une action OSC ne confirme pas la réception (UDP).
Publication
Publication
Tout compte Hou.la peut publier un plugin ou un pack. La publication pour tous passe par une relecture.
- Plugin ajouté avec « Ajouter mon jeu » : utilisable tout de suite sur ton PC, dans tes packs, invisible des autres. Rendu public avec le premier pack public qui l'utilise : relu par Hou.la avant d'entrer dans le Store ; chaque modification repasse en relecture. Un plugin avec un mod est toujours relu par une personne.
- Un pack public attend ses plugins : publier dans le Store un pack qui utilise un plugin de jeu privé envoie ce plugin en relecture. Le pack est relu de son côté, et n'est proposé dans le Store qu'une fois tous ses plugins approuvés. Un plugin refusé ou un mod sans archive bloque l'envoi (
PLUGIN_JEU_A_CORRIGER), avec le motif. - Un pack public est relu : l'analyse automatique peut seulement refuser, une personne approuve. Un pack approuvé est signé, et Connect vérifie sa signature.
- Installer un pack installe ses plugins : la liste se calcule depuis ses actions de jeu, sans rien à déclarer. Le mod d'un plugin s'installe avec lui.
- Commission : le créateur d'un pack peut fixer la sienne, de 0 à 15 %.
Ressources
Kit et téléchargements
Les fichiers de cette documentation. Les exemples sont générés depuis Connect et vérifiés par le validateur de Hou.la.
guide-ia.md: la référence complète du format de pack : variables de chaque déclencheur, échelle des prix, bornes, et actions de chaque plugin de jeu officiel. Le même fichier que le bouton « Guide pour une IA… » du Lab.packs/: les cinq packs d'exemple des guides.plugins/: un plugin de jeu par mode.visuels/gabarit-banniere-1600x900.jpg,visuels/prompt-banniere.md: le gabarit de la bannière d'un pack (1600 x 900) et le prompt associé, pour une IA d'images. Icônes de cadeaux : 512 x 512, PNG transparent.

FAQ
Questions fréquentes
Dans quel langage écrire un plugin ?
Un plugin est un fichier JSON, sans code. Le seul code est celui de ton mod (mode mod) ou de ton programme (modes http et ws), dans le langage de ton choix.
Comment tester sans être en live ?
Dans le Lab, « Tester » joue un écouteur tout de suite, comme en direct. La notification du test indique ce qu'a fait chaque action : jouée, sautée ou en échec, avec la cause.
Un pack peut-il appeler une adresse locale, comme 127.0.0.1 ?
Seulement par une connexion saisie par le streamer. Une adresse locale écrite dans le pack est refusée.
Comment mon programme authentifie-t-il les requêtes de Connect ?
Par le jeton de la connexion : Connect l'envoie dans Authorization: Bearer <jeton>, ou dans l'en-tête nommé par la connexion (par exemple X-API-Key). Le jeton reste sur le PC du streamer et n'apparaît jamais dans le pack.
Le numéro Steam est-il obligatoire ?
Seulement en mode mod : sans lui, Connect ne peut pas installer de mod. Il est facultatif dans les autres modes.
Que se passe-t-il si le jeu n'est pas lancé (mode mod) ?
L'action échoue et le journal le dit. Si le mod manque, Connect l'installe à la première action qui vise le jeu, puis demande de relancer le jeu.
Puis-je modifier un plugin ou un pack publié ?
Oui. La nouvelle version repasse en relecture avant d'être proposée dans le Store.
Quelle est la différence entre un plugin de jeu en mode http et une action http dans un pack ?
Le plugin déclare des actions nommées et bornées, réutilisables par tous les packs, avec une connexion par jeu. Une action http dans un pack est une requête libre, écrite dans le pack, soumise aux règles d'adresse décrites dans « Limites et sécurité ».
Support développeur
Pose tes questions techniques sur le Discord de Hou.la.