Hou.la Connect · Développeurs

Relie ton jeu, ton outil ou ton matériel à Hou.la Connect

Ce que Connect sait piloter, comment écrire le plugin d'un jeu, le format d'un pack, et ce qu'un plugin ne peut pas faire. Chaque exemple de cette page passe le validateur de Hou.la.

← Hou.la Connect, l'app

Le principe

Trois pièces, trois rôles

Un spectateur agit ; Connect, sur le PC du streamer, décide quoi faire et le fait. Un pack et un plugin sont des fichiers JSON : Connect n'exécute jamais le code de leur auteur.

Le pack : quand, alors

Il dit quand agir (un cadeau, un like, une commande de chat, sur Hou.la, TikTok, YouTube ou Kick) et quoi faire (un son, une scène OBS, une action de jeu…). N'importe quel créateur l'écrit dans le Lab de Connect.

Le plugin : ce qu'un jeu sait faire

Il décrit un jeu, ses actions et leurs réglages, et comment le joindre : un mod, RCON, WebSocket ou HTTP. Un par jeu, partagé par tous les packs de ce jeu.

La connexion : l'adresse chez le streamer

L'adresse, le port, le mot de passe ou le jeton, saisis par le streamer dans « Mes plugins ». Ils restent sur son PC : un pack ne contient jamais d'identifiants, il demande une connexion par son rôle.

1 Un spectateur cadeau, like, commande 2Hou.la Connect le pack choisit l'action 3 Le plugin du jeu mod · rcon · ws · http 4 Le jeu l'action se joue événement action + réglages commande, message, requête 1 Un spectateur cadeau, like, commande événement 2Hou.la Connect le pack choisit l'action action + réglages 3 Le plugin du jeu mod · rcon · ws · http commande, message, requête 4 Le jeu l'action se joue

Tout se passe sur le PC du streamer. Connect revérifie chaque réglage avant de l'envoyer.

Le trajet d'une action, de l'événement au jeu.

Les actions

Ce que Connect sait piloter

Quinze sortes d'actions, chacune jouée par un exécuteur de Connect. Un pack n'en invente pas : il choisit dans cette liste, et un type inconnu fait refuser le pack.

Action type Ce que ça fait, et ses limites
Action de jeu jeuUne action d'un plugin de jeu, avec ses réglages écrits en clair (jamais une variable). Voir « Créer le plugin d'un jeu ».
Clavier keyboardTouches, combinaisons (shift+c), suites (up,up,down), maintien (space:400). Le jeu doit être au premier plan. Désactivé par défaut : le streamer l'allume dans « Mes plugins ».
Manette virtuelle gamepadUne manette Xbox virtuelle : boutons, sticks, gâchettes, séquences. Windows seulement (pilote ViGEmBus). Désactivée par défaut.
OBS Studio obsUne requête d'OBS WebSocket v5 prise dans une liste fermée : scène, sources, filtres, micro, médias, transitions, enregistrement, replay, caméra virtuelle. Jamais démarrer ni couper le stream.
Streamlabs Desktop streamlabsUne commande d'une liste fermée : scènes, sources, son, enregistrement, replay. Jamais démarrer le stream.
Overlay overlayPiloter un overlay du streamer : afficher, masquer, ajouter, retirer, fixer, démarrer, arrêter. L'overlay est choisi par le streamer dans sa bibliothèque.
Son soundUn son de la bibliothèque Hou.la, par son identifiant, ou un fichier du PC désigné par un rôle que le streamer relie (jamais un chemin). Volume, coupure du son précédent, durée maximale.
Voix de synthèse ttsLire un texte à voix haute avec une voix du système, 300 caractères au plus.
Voicemod voicemodChanger de voix, jouer un son de la soundboard. Voicemod doit être lancé (Windows).
Chat chatÉcrire dans le chat Hou.la du live, avec au moins 2 secondes entre deux messages.
RCON rconUne commande à un serveur de jeu (Minecraft, Rust, ARK…). Les verbes destructeurs (stop, ban, op, whitelist…) sont refusés.
HTTP httpUne requête GET, POST ou PUT. Par une connexion du streamer : son adresse, même locale (http://127.0.0.1:…), 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. 5 secondes au plus.
WebSocket wsUn message texte, par une connexion du streamer ou une adresse écrite dans le pack (mêmes règles que HTTP pour ce PC et le réseau local).
MQTT mqttUn message sur un topic, vers le courtier de la connexion du streamer (domotique, Zigbee2MQTT, Tasmota…).
OSC oscUn message OSC en UDP (VRChat, TouchDesigner, Resolume), par défaut vers 127.0.0.1:9000. UDP ne confirme rien : réussi veut dire envoyé.

Les plugins de jeu officiels

Livrés avec Connect, au même format que ceux de la communauté. Sers-t'en dans tes packs tels quels, ou lis-les comme exemples.

Jeu id Mode Actions
Pratfallpratfallmod 16 actions
Minecraftminecraftrcon 8 actions
ARK: Survival Evolvedark-survival-evolvedrcon 6 actions
Counter-Strike 2counter-strike-2rcon 9 actions
Factoriofactoriorcon 4 actions
Left 4 Dead 2left-4-dead-2rcon 6 actions
Project Zomboidproject-zomboidrcon 10 actions
Rustrustrcon 5 actions
Squadsquadrcon 4 actions
Team Fortress 2team-fortress-2rcon 7 actions
Tomb Raider I-III Remasteredtomb-raider-remasteredhttp 30 actions

Et un logiciel, un objet connecté ?

Connect a des plugins officiels génériques (HTTP, WebSocket, MQTT, OSC, RCON, OBS, Streamlabs) et des plugins prêts à l'emploi (Home Assistant, Elgato Key Light, Discord, Telegram, webhook) : un pack y envoie ses requêtes par la connexion du streamer. « + Créer un plugin », « Un logiciel ou un objet connecté » publie un descripteur (protocole, champs, commandes), mais installé depuis le Store, ce type de plugin ne se configure pas encore dans Connect. Pour piloter ton programme aujourd'hui, le plus direct est un plugin de jeu en mode http ou ws, qui fonctionne de bout en bout.

Plugin de jeu

Créer le plugin de ton jeu

Un plugin de jeu est une fiche JSON : le jeu, ses actions, leurs réglages, et le mode de liaison. Tu n'écris aucun code côté Connect ; le seul code éventuel est le mod de ton jeu.

Quatre façons de joindre un jeu

Un mod dans le jeu

"mode": "mod"

Pour un jeu sans serveur que tu peux moder. Ton mod se relie tout seul à Connect par le pont local. Hou.la héberge l'archive du mod, et 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. Chaque action envoie 1 à 5 commandes, jouées dans l'ordre. Le streamer saisit l'hôte, le port, le mot de passe et son pseudo en jeu.

Plugins officiels dans ce mode : 9

Un WebSocket local

"mode": "ws"

Pour un jeu ou un outil qui écoute déjà un WebSocket. Chaque action envoie un message (2 000 caractères au plus). Le streamer saisit l'adresse.

Plugins officiels dans ce mode : 0

Une API HTTP locale

"mode": "http"

Pour un jeu ou un outil qui expose une API HTTP. Chaque action est une requête GET, POST ou PUT sur un chemin, avec un corps JSON facultatif. Le streamer saisit l'adresse de base et, si besoin, un jeton.

Plugins officiels dans ce mode : 1

Pas à pas, dans Connect

  1. Ouvre l'assistant

    « Mes plugins », « + Créer un plugin », « Un jeu ». Ou depuis un pack : dans le Lab, bloc « Dans le jeu », « Mon jeu n'est pas dans la liste », « Créer le plugin de mon jeu ». Si ton mod tourne déjà, ton jeu est proposé « relié en développement » et l'assistant reprend les actions qu'il annonce.

  2. Le jeu

    Nom, identifiant (minuscules, chiffres et tirets, 2 à 40 caractères, le même que dans ton mod), numéro Steam et description. Le numéro Steam est facultatif, mais sans lui Connect ne trouve pas le dossier où installer un mod.

  3. La liaison

    Un mod, RCON, WebSocket ou HTTP. Tu n'écris pas les champs de connexion : ils viennent du mode, et le streamer retrouve le formulaire qu'il connaît.

  4. Les actions

    Pour chacune : un nom (en français, en anglais si tu veux), une description, un effet (aide, gêne ou neutre) et jusqu'à 8 réglages (un entier borné, ou un choix dans une liste). 60 actions au plus. En RCON, WebSocket et HTTP, écris le modèle de la commande, du message ou de la requête.

  5. 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), envoyée à Hou.la qui l'héberge et en calcule l'empreinte.

  6. Teste, puis publie

    « Pour moi, pour tester » : le plugin s'installe tout de suite sur ton PC, pour tes packs privés. Dans un pack, « Tester » joue l'action pour de vrai, sans être en live. Puis « Pour tout le monde » : une personne de l'équipe Hou.la le relit avant le Store. L'onglet JSON de l'assistant importe et exporte le fichier.

Le format, champ par champ

Le validateur est fermé : une clé inconnue fait refuser le plugin. Un libellé peut être une simple chaîne, ou un objet { "fr": "…", "en": "…" }.

Clé Règle
idIdentifiant du plugin : ^[a-z0-9][a-z0-9-]{1,39}$. Unique dans le Store, et le même que celui que ton mod annonce.
nomNom du jeu, 60 caractères au plus.
protocoleToujours "jeu".
descriptionFacultative, 400 caractères au plus. Dis ce qu'il faut installer ou lancer.
jeu.steamAppIdFacultatif : 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.
modemod, rcon, ws ou http.
modMode mod seulement : moteur (godot, unity, lua, autre), dossier, version, prerequis. Le bloc fichier (adresse, empreinte, taille) est rempli par Hou.la quand l'assistant envoie l'archive.
actions[].idIdentifiant de l'action : a-z, 0-9, _ et -, 40 caractères au plus.
actions[].libelleNom de l'action, 80 caractères au plus.
actions[].descriptionFacultative, 300 caractères au plus.
actions[].effetaide, gene ou neutre (par défaut). Le Lab l'écrit à côté de l'action.
actions[].params8 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 (pas en mode mod, où le mod reçoit l'identifiant).
actions[].commandesMode rcon : 1 à 5 commandes, une ligne chacune, 500 caractères au plus.
actions[].messageMode ws : le message, 2 000 caractères au plus.
actions[].method / path / jsonMode http : method (GET, POST, PUT), path ajouté à l'adresse de la connexion (commence par /, sans ..), json facultatif (un objet, 2 000 caractères au plus).

Ce qu'un modèle peut contenir

{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'entre jamais dans une commande. 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).

Un exemple par mode

Chacun passe le vrai validateur de Hou.la. Importe-le dans l'assistant (onglet JSON) pour partir de là.

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."
    }
  }
}
Télécharger ce fichier
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 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} !"
      ]
    }
  ]
}
Télécharger ce fichier
plugin-mode-ws.jsonUn WebSocket local

Le message est du texte, ici du JSON. Pour un choix, la valeur (goblin) remplace le réglage, pas son identifiant.

{
  "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}\"}"
    }
  ]
}
Télécharger ce fichier
plugin-mode-http.jsonUne API HTTP locale

L'architecture d'un outil compagnon : un chemin par action, un corps JSON facultatif.

{
  "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}"
      }
    }
  ]
}
Télécharger ce fichier

Trois plugins officiels, en extrait

Pratfall (mod), Counter-Strike 2 (RCON) et Tomb Raider I-III Remastered (HTTP) : 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"
      },
      "effet": "gene",
      "params": [
        {
          "id": "nombre",
          "type": "entier",
          "libelle": {
            "fr": "Nombre de bombes",
            "en": "Number of bombs"
          },
          "min": 1,
          "max": 5,
          "defaut": 3
        },
        {
          "id": "cible",
          "type": "choix",
          "libelle": {
            "fr": "Qui",
            "en": "Who"
          },
          "defaut": "moi",
          "valeurs": [
            {
              "id": "moi",
              "libelle": {
                "fr": "Toi",
                "en": "You"
              }
            },
            {
              "id": "hasard",
              "libelle": {
                "fr": "Un joueur au hasard",
                "en": "A random player"
              }
            }
          ]
        }
      ]
    },
    {
      "id": "mine",
      "libelle": {
        "fr": "Mine foreuse",
        "en": "Digging mine"
      },
      "effet": "gene",
      "params": [
        {
          "id": "cible",
          "type": "choix",
          "libelle": {
            "fr": "Qui",
            "en": "Who"
          },
          "defaut": "moi",
          "valeurs": [
            {
              "id": "moi",
              "libelle": {
                "fr": "Toi",
                "en": "You"
              }
            },
            {
              "id": "hasard",
              "libelle": {
                "fr": "Un joueur au hasard",
                "en": "A random player"
              }
            }
          ]
        }
      ]
    }
  ]
}
counter-strike-2Counter-Strike 2 · rcon · 9 actions
{
  "id": "counter-strike-2",
  "nom": "Counter-Strike 2",
  "protocole": "jeu",
  "mode": "rcon",
  "jeu": {
    "steamAppId": 730
  },
  "actions": [
    {
      "id": "gravite",
      "libelle": {
        "fr": "Changer la gravité (triche)",
        "en": "Change gravity (cheat)"
      },
      "effet": "neutre",
      "params": [
        {
          "id": "gravite",
          "type": "entier",
          "libelle": {
            "fr": "Gravité (800 = normale)",
            "en": "Gravity (800 = normal)"
          },
          "min": 100,
          "max": 2000,
          "defaut": 200
        }
      ],
      "commandes": [
        "sv_gravity {param.gravite}"
      ]
    },
    {
      "id": "bunny_hop",
      "libelle": {
        "fr": "Bunny hop",
        "en": "Bunny hop"
      },
      "effet": "aide",
      "params": [
        {
          "id": "etat",
          "type": "choix",
          "libelle": {
            "fr": "État",
            "en": "State"
          },
          "defaut": "oui",
          "valeurs": [
            {
              "id": "oui",
              "libelle": {
                "fr": "Activé",
                "en": "On"
              },
              "valeur": "1"
            },
            {
              "id": "non",
              "libelle": {
                "fr": "Désactivé",
                "en": "Off"
              },
              "valeur": "0"
            }
          ]
        }
      ],
      "commandes": [
        "sv_enablebunnyhopping {param.etat}",
        "sv_autobunnyhopping {param.etat}"
      ]
    }
  ]
}
tomb-raider-remasteredTomb Raider I-III Remastered · http · 30 actions
{
  "id": "tomb-raider-remastered",
  "nom": "Tomb Raider I-III Remastered",
  "protocole": "jeu",
  "mode": "http",
  "jeu": {
    "steamAppId": 2478970
  },
  "actions": [
    {
      "id": "small_medkit",
      "libelle": {
        "fr": "Soins : petite trousse de soins",
        "en": "Health: small medipack"
      },
      "effet": "aide",
      "method": "POST",
      "path": "/small_medkit"
    },
    {
      "id": "large_medkit",
      "libelle": {
        "fr": "Soins : grande trousse de soins",
        "en": "Health: large medipack"
      },
      "effet": "aide",
      "method": "POST",
      "path": "/large_medkit"
    }
  ]
}

La question qu'on nous pose

Un plugin peut-il embarquer et lancer un programme (.exe) ?

Non. Ni un pack, ni un plugin, ni un mod ne font lancer un programme par Connect. La bonne architecture existe pourtant, et elle marche aujourd'hui : ton outil reste un programme que le joueur lance, il expose une API locale, et ton plugin lui parle en HTTP ou en WebSocket.

Ce que Connect ne fait pas

Connect ne lance aucun programme fourni par un pack ou un plugin : aucun de leurs champs ne désigne un fichier à exécuter, et une clé inconnue fait refuser le fichier. Les seuls programmes qu'il démarre sont ses propres composants, livrés avec lui (le pilote de la manette virtuelle, par exemple).

Pourquoi

Un pack s'installe en un clic chez de nombreux streamers. S'il pouvait lancer un programme, chaque pack serait un logiciel à auditer. Un pack et un plugin restent donc de la donnée, relue avant publication ; le code qui tourne chez le streamer est celui qu'il a choisi d'installer lui-même.

Et le mode mod ?

Hou.la héberge l'archive de ton mod. Connect vérifie son empreinte, contrôle son contenu (aucun chemin qui sorte du dossier, aucun lien symbolique) et l'extrait dans le dossier du jeu. Il ne lance rien : c'est le jeu qui charge son mod au démarrage. Un plugin public avec un mod est toujours relu par une personne de l'équipe.

La voie qui marche : un outil compagnon en HTTP

Ce PC, celui du streamer 1Hou.la Connect plugin en mode http connexion : URL de base 2 Ton outil lancé par le joueur http://127.0.0.1:PORT 3 Le jeu piloté par ton outil POST /… réponse, 5 s ton code Hou.la : Connect, ton plugin Toi : l'outil, son téléchargement Le jeu du joueur Ce PC, celui du streamer 1Hou.la Connect plugin en mode http Hou.la : Connect, ton plugin POST /… réponse, 5 s 2 Ton outil http://127.0.0.1:PORT Toi : l'outil, son téléchargement ton code 3 Le jeu piloté par ton outil Le jeu du joueur
Connect parle à ton outil, ton outil parle au jeu. Hou.la ne fournit ni ne lance l'outil.
  1. Ton outil écoute en local

    Il ouvre une API HTTP (ou un WebSocket) sur 127.0.0.1, à un port fixe de ton choix. Une route par action : POST /soin, POST /ennemi… Réponds par un statut 2xx quand l'action a réussi : Connect attend 5 secondes au plus (4 en WebSocket), et tout autre statut s'affiche comme un échec dans le journal du live.

  2. Tu publies un plugin de jeu en mode http

    Une action par route, avec ses réglages. Dans la description, dis où télécharger ton outil et quelle adresse saisir. C'est le rôle de « Mes plugins » : déclarer cette connexion, pas livrer le programme.

  3. Le streamer installe et lance ton outil

    Il le télécharge chez toi, le lance à côté du jeu, puis saisit dans Connect l'« URL de base » (par exemple http://127.0.0.1:7777) et un jeton si ton outil en demande un. Le jeton part dans l'en-tête Authorization: Bearer …, ou dans l'en-tête que la connexion nomme.

  4. Les packs choisissent tes actions

    Dans le Lab, bloc « Dans le jeu » : ton jeu et ses actions apparaissent. Une adresse locale ne passe que par une connexion : un pack ne peut pas écrire http://127.0.0.1 lui-même.

C'est le cas de Tomb Raider I-III Remastered : son plugin officiel (tomb-raider-remastered, 30 actions en POST) parle à l'outil « Director » qui tourne à côté du jeu. Le plugin ne connaît que l'adresse saisie par le streamer ; l'outil fait le travail dans le jeu.

Mode mod

Le pont local : le protocole d'un mod

Ton mod est le client, Connect le serveur : le jeu n'ouvre aucun port, et le mod se reconnecte tout seul quand Connect redémarre. Des messages JSON en texte, sur un WebSocket local.

  1. Lire 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 et le fichier est effacé à la fermeture : relis-le avant chaque tentative.

  2. Se connecter

    ws://127.0.0.1:<port>/pont/v1, avec l'en-tête X-Houla-Pont: <jeton>, et sans en-tête Origin.

  3. Dire bonjour, en 5 secondes

    { "t": "bonjour", "v": 2, "jeu": "mon-jeu", "versionMod": "1.0.0", "actions": [ … ] }. En version 2, actions décrit chaque action (identifiant, libellé, réglages) : l'assistant s'en sert pour préremplir ton plugin. Connect répond { "t": "bienvenue", "v": 2 }.

  4. Recevoir une action

    { "t": "action", "id": "a7", "action": "bombes", "params": { … }, "de": { "pseudo": "Lina", "quantite": 3, "cadeau": "Bombe" } }.

  5. Répondre, en 5 secondes

    { "t": "fait", "id": "a7", "ok": true }, ou "ok": false avec une raison (tableau ci-dessous) et un detail facultatif. Sans réponse en 5 secondes, l'action échoue.

  6. Un jeu que Connect ne connaît pas encore

    Un identifiant valide sans plugin est accepté et marqué « en développement » dans « Mes plugins » : c'est ainsi que tu testes ton mod avant d'avoir publié son plugin. Un second mod du même jeu remplace le premier (le jeu a été relancé).

Les raisons d'un refus

pas_en_partie Aucune partie en cours (menu, chargement).
mode_interdit Ce mode de jeu refuse les actions des spectateurs.
pas_maintenant L'action n'a pas pu se jouer à ce moment.
sans_effet Rien à faire (vie déjà pleine, personne à ranimer).
action_inconnue Le mod installé ne connaît pas cette action (trop ancien).
params Réglages refusés par le mod.
erreur Erreur dans le jeu, précisée dans le champ detail.
pas_hote Seul l'hôte de la partie peut recevoir les actions.

Ce que le serveur du pont garantit

  • Il n'écoute que sur 127.0.0.1.
  • Le jeton est exigé, et comparé en temps constant.
  • Tout en-tête Origin est refusé : une page web ouverte sur le PC ne peut pas se faire passer pour un jeu.
  • L'en-tête Host doit nommer 127.0.0.1 ou localhost (contre le DNS rebinding).
  • Des messages de 64 Ko au plus, et un battement toutes les 15 secondes : un jeu planté ne reste pas « connecté ».

Les kits de mod

Hou.la maintient trois clients du pont, chacun avec un exemple complet : Godot 4 .NET (C#), Unity avec BepInEx 5 (C#), et Lua avec LuaSocket. Ils ne sont pas encore proposés en téléchargement public ; le protocole ci-dessus suffit pour écrire ton client dans n'importe quel langage. Il n'existe pas de kit pour Unreal Engine.

Packs

Le format d'un pack (schéma 3)

Un pack est un fichier JSON que le Lab de Connect écrit pour toi, et que tu peux aussi écrire à la main ou faire écrire par une IA. La référence complète, générée depuis Connect, est le guide pour une IA du kit.

{ "schema": 3, "rules": [ { "id", "label", "on": [ … ], "do": [ … ] } ] }
  • rules : les interactions. Chacune a un id stable (les réglages des streamers y sont attachés), un label, un ou plusieurs déclencheurs dans on (l'un ou l'autre lance la chaîne, 8 au plus) et une chaîne de blocs dans do.
  • Déclencheurs : cadeau Hou.la, dont les cadeaux personnalisés (ix_slot_01, ix_slot_02… avec leur prix et leur rayon), follow, message du chat, commande déclarée (gratuite ou payante), likes et partages (paliers ou « tous les N »), spectateurs, sondage, roue, raccourci du streamer, et les événements de TikTok, YouTube, Kick et Twitch ("type": "platform").
  • Blocs : action, delay, if, switch (la première condition vraie part), random (tirage pondéré), parallel, repeat (et cases pour choisir le nombre de tours), counter, var.
  • Conditions : { "all": [ … ] } ou { "any": [ … ] }, sur le montant (coins), la quantité, le palier de likes (hearts.palier), le montant d'une autre plateforme (platform.amount), un compteur, une variable…
  • Variables dans les textes : {sender.name}, {gift.name}, {hearts.palier}, {commande.texte}… Seules celles que le déclencheur remplit ont une valeur, et une variable inconnue fait refuser le pack. Jamais de variable dans un identifiant (son, overlay, réglage de jeu).

Bornes : 32 blocs par interaction, 6 niveaux d'imbrication, 30 s de durée calculée par chaîne, 50 tours pour un repeat.

Des packs d'exemple, vérifiés

pack-multi-sources.jsonUn événement, cinq plateformes, une seule action

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 fait apparaître le même creeper devant le streamer (plugin Minecraft). Twitch arrive bientôt dans Connect ; le pack est déjà prêt.

{
  "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"
            }
          }
        }
      ]
    }
  ]
}
Télécharger ce fichier
pack-paliers-et-montants.jsonPaliers de likes et de partages, routage selon le montant

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
              }
            }
          ]
        }
      ]
    }
  ]
}
Télécharger ce fichier
pack-commandes-chat.jsonCommandes de chat

/klaxon est gratuite, 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
          }
        }
      ]
    }
  ]
}
Télécharger ce fichier
pack-cadeaux-personnalises.jsonUn cadeau personnalisé par action de jeu

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 du plugin 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"
            }
          }
        }
      ]
    }
  ]
}
Télécharger ce fichier
pack-outil-compagnon-http.jsonUn outil compagnon en HTTP

Deux cadeaux qui pilotent Tomb Raider I-III Remastered par son plugin officiel en mode http ; la trousse répond aussi à la Rose de TikTok (giftId 5655).

{
  "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": {}
          }
        }
      ]
    }
  ]
}
Télécharger ce fichier

Sécurité

Ce qu'un pack ou un plugin ne peut pas faire

Ces règles sont vérifiées deux fois : par Hou.la à la publication, puis par Connect avant chaque action.

  • Lancer un programme, exécuter un script, ou faire tourner le code de son auteur.
  • 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…
  • Prendre le clavier, la manette ou le pilote bas niveau sans que le streamer les ait allumé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 le reste fait refuser le fichier.

Publier

Privé tout de suite, public après relecture

Tout le monde peut publier. Ce qui part chez les autres streamers est relu avant.

  • Un plugin « pour moi » est utilisable tout de suite sur ton PC, dans tes packs privés. « Pour tout le monde » : relu par une personne de l'équipe avant d'entrer dans le Store ; le modifier le renvoie en relecture.
  • Un pack public a besoin de plugins publics : tant que le plugin de ton jeu est privé ou en relecture, Hou.la refuse de publier le pack (PLUGIN_JEU_PRIVE).
  • Un pack public est relu : l'analyse automatique peut seulement refuser, une personne approuve. Approuvé, il est signé, et Connect vérifie sa signature.
  • Installer un pack installe ses plugins : la liste se calcule depuis ses actions de jeu, il n'y a 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 %.

Kit

Le kit développeur

Tout ce que cette page montre, en fichiers. Les exemples sont générés depuis Connect et vérifiés par le validateur de Hou.la.

Télécharger le kit (287 Ko)

  • guide-ia.md : le format complet d'un pack, avec les variables de chaque déclencheur, l'échelle des prix, les bornes, et les actions de chaque plugin de jeu officiel. C'est le même fichier que le bouton « Guide pour une IA… » du Lab.
  • packs/ : les cinq packs d'exemple de cette page.
  • 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 qui va avec, pour une IA d'images. Les icônes de cadeaux : 512 x 512, PNG transparent.
Gabarit de bannière : à gauche le titre, le sous-titre et l'image du jeu ; à droite, un panneau avec le solde de pièces et une grille de cadeaux (icône, nom, prix) dont la dernière rangée est coupée.
Le gabarit de la bannière qui présente un pack.

Faire écrire un pack par une IA

  1. Donne-lui guide-ia.md, et l'adresse de cette page.
  2. Si ton pack joue des sons, ajoute la liste des sons : dans Connect, « Sons », « Exporter la liste… » (ou « Liste des sons… » dans le Lab).
  3. Elle rend un fichier JSON. Dans le Lab : onglet « Écouteurs », « JSON », « Importer un fichier… », puis « Vérifier mon pack ».
  4. Règle ensuite ce qu'une IA ne peut pas deviner : l'icône d'un cadeau personnalisé, les overlays du streamer, les connexions.

Prêt à relier ton jeu ?

Installe Hou.la Connect, ouvre « Mes plugins » et crée ton premier plugin. Une question : la communauté Hou.la est sur Discord.