

LLM-controlled Risk of Rain 2 via BepInEx mod + Python brain
Early development — expect bugs, rough edges, and missing features.
Setup | Protocol Reference | Architecture | Youtube Showcase
Rainflayer connects a large language model to Risk of Rain 2. A BepInEx mod exposes game state (enemies, inventory, objectives, interactables) over a local TCP socket. A Python brain queries that state every few seconds, feeds it to an LLM, and sends back strategic commands that the mod executes at 50 Hz — movement, aiming, skills, camera, all without simulating mouse or keyboard input.
Inspired by Mindcraft and Mineflayer.
Mods like PlayerBots and ImprovedSurvivorAI use RoR2's native AISkillDriver system — static, hand-tuned rule sets baked into the mod at compile time. They work well as NPC teammates, but their behavior cannot be changed at runtime. You cannot tell them to play more aggressively, follow you to the teleporter, wait around and farm chests, or adapt to what's happening in the run.
Rainflayer exposes a reasoning layer. The LLM reads live game state every few seconds and issues commands that dynamically reshape behavior mid-run. Directives like play defensively or go open that chest are interpreted in context and translated into actual inputs. PlayerBots gives you a reliable NPC teammate, while Rainflayer is an experiment in giving an AI agency in a game.
Rainflayer runs entirely on consumer hardware using API calls — no local GPU or training involved. The goal is to explore what's achievable with foundation models and game-level APIs today.
This is not a drop-in replacement for a human co-op partner. It's experimental and proof-of-concept — if you want a reliable RoR2 teammate, the RoR2 community has no shortage of players. This exists because nothing quite like it exists yet, and because the broader vision of an AI that can play any game (not just RoR2) is worth exploring.
A common concern with AI mods is RAM overhead. Here's what Rainflayer's expectage usage looks like:
| Component | RAM usage |
|---|---|
| BepInEx mod (C# plugin) | < 5 MB |
| Python brain | ~50–100 MB |
| Local GPU | Not required |
All LLM inference runs remotely via the Novita.ai API, the Python brain is a lightweight script that makes HTTP calls. Only internet connection is required.
[!NOTE] Rainflayer takes full control of your character. Set
EnableAIControl = falsein the mod config when you want to play normally without removing the mod.
┌─ Python Brain (Strategic, ~0.25 Hz) ────────────────┐
│ Llama 4 Maverick 17B via Novita.ai │
│ Reads game state → LLM → commands │
└────────────────────── ↕ TCP :7777 ──────────────────┘
┌─ BepInEx Mod (Tactical, 50 Hz) ─────────────────────┐
│ Intercepts InputBank + CameraRigController │
│ Entity detection, pathfinding, skill execution │
└──────────────────── Risk of Rain 2 ─────────────────┘
While the brain plays, you can type natural-language goals to influence its decisions in real time:
directive> go activate the teleporter
directive> play more aggressively
directive> clear
directive> status
This is just the minimum usage case - connecting to your own custom LLMs / AI projects is intended as the broader use case.
Via mod manager (recommended): Install with r2modman or Thunderstore Mod Manager from the Thunderstore page.
Manually: Download the latest release, and drop Rainflayer.dll into your BepInEx/plugins/ folder. Repeat for dependencies.
Launch RoR2 once to generate the config file, then close it.
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
Create a .env file in the repo root:
NOVITA_API_KEY=your_novita_api_key_here
Load into a solo run in RoR2, then in a separate terminal:
source venv/bin/activate
python -m brain.main
The brain waits up to 2 minutes for the mod to connect. Once connected, it starts making decisions every 4 seconds.
============================================================
rainflayer — RoR2 LLM Brain Server
Llama 4 Maverick 17B via Novita.ai
============================================================
Game connected at 12:34:56
Brain is running. Type a directive to influence its decisions.
directive>
Generated at BepInEx/config/justindwang.rainflayer.cfg after first run:
[Rainflayer]
EnableAIControl = true
DebugMode = false
python -m brain.main --interval 4.0 # decision interval in seconds (default: 4.0)
python -m brain.main --timeout 120 # seconds to wait for game connection
Every few seconds the brain queries the mod for the current game state — health, gold, nearby enemies, interactables, teleporter charge — and sends it to an LLM (Llama 4 Maverick 17B). The LLM returns a short list of commands that the mod executes immediately.
The commands the AI can send:
| Command | Args | Effect |
|---|---|---|
FIND_AND_INTERACT |
chest |
Navigate to nearest chest and open it |
FIND_AND_INTERACT |
shrine |
Navigate to nearest shrine and use it |
FIND_AND_INTERACT |
teleporter |
Navigate to teleporter and activate it |
FIND_AND_INTERACT |
pillar |
Navigate to nearest uncharged moon battery pillar and interact (moon2 only) |
FIND_AND_INTERACT |
jump_pad |
Navigate to the jump pad leading to the Mythrix arena (moon2 only) |
FIND_AND_INTERACT |
ship |
Navigate to the rescue ship to escape the moon after defeating Mythrix (moon2 only) |
GOTO |
CANCEL |
Cancel current navigation and clear all island/return-path tracking |
BUY_SHOP_ITEM |
Item Name |
Navigate to nearest shop and buy named item |
FIND_AND_INTERACT handles the full navigation → combat → interaction loop. If combat interrupts it (enemy within 10m), the mod pauses and resumes after the threat clears. pillar, jump_pad, and ship are long-running journeys (60–120s) — issue once and wait.
Commencement (moon2) has a distinct set of objectives: charge four battery pillars → use the jump pad → defeat Mythrix → escape to the rescue ship. The general flow is:
FIND_AND_INTERACT:pillar — navigates across the main platform and through the appropriate passage to the nearest uncharged pillar, activates it, and stands inside the zone until it's fully charged. After charging, use MODE:defend_zone to hold position while the charge ticks up.FIND_AND_INTERACT:jump_pad — navigates to the mass island, crosses the chain bridge, and reaches the jump pad teleporting into the Mythrix arena.MODE:combat and STRATEGY:aggressive.FIND_AND_INTERACT:ship — after defeating Mythrix, navigates through the arena escape orb, follows the blood passage back to the main platform, and reaches the rescue ship.There are also GOTO:<island> commands (blood, soul, mass, design) for recovering from bugged island state — these are not general navigation commands and should only be used when explicitly needed to reset position.
MODE:defend_zone locks the AI inside the nearest active holdout zone and keeps it there:
| Scenario | Zone type |
|---|---|
| Teleporter charging | Teleporter's holdout zone |
| Moon2 pillar charging | Battery pillar's holdout zone |
| Post-Mythrix escape sequence | Extraction zone near the rescue ship |
When MODE:defend_zone is set, the AI navigates into the zone if outside it, then switches to free combat/roam behavior clamped inside the zone radius. All roam waypoints and strafe movement are constrained so the AI stays within the zone boundary. The mode self-cancels if the zone becomes inactive (e.g. teleporter fully charged, pillar done).
If no active zone is found when the command is issued, the mod sends action_failed with reason no_active_zone and the brain falls back to MODE:combat.
| Command | Args | Effect |
|---|---|---|
STRATEGY |
aggressive |
Close range (15m), strafing, high DPS |
STRATEGY |
defensive |
Long range (25m), heavy kiting |
STRATEGY |
balanced |
Medium range (20m), moderate skills |
STRATEGY |
support |
Follow allies (does not engage enemies) |
MODE |
roam |
Wander and auto-engage enemies |
MODE |
combat |
Stay focused on nearby enemies |
MODE |
follow |
Follow nearest ally |
MODE |
wait |
Stop all movement |
MODE |
defend_zone |
Stay inside the nearest active holdout zone (teleporter, pillar, or escape zone) |
You don't need to use these commands directly — just type natural-language goals into the directive prompt and the AI figures out what to do.
This list is not exhaustive — there are likely bugs that haven't been discovered or documented yet. If you run into something, feel free to open an issue.
clear or roam will usually unstick itThe same socket interface used to control the player character can be extended to control enemies. A Soul model issuing directives to teamIndex: Monster instead of the player would give the LLM agency over an enemy character — navigating, targeting the player, and using skills via the same command layer.
A more interesting variant: put the AI on an opposing survivor — same character type as the player but on the enemy team. RoR2 supports custom TeamIndex assignments (see PVP Mod and Refightilization for prior art on team reassignment). This would create an AI-controlled adversary that fights and moves like a player, opening the door to AI vs. human PvP scenarios within a co-op game.
AISkillDriver tuningSee docs/ARCHITECTURE.md for a technical deep dive:
See https://github.com/justindwang/rainflayer/tree/docs/docs/PROTOCOL.md for the full socket protocol reference.
MIT