AI-assisted Authoring
CVR Lua scripts run in a sandboxed MoonSharp interpreter inside the CVR client — not in standard Lua, not in LuaJIT, not in any environment most LLMs were trained on. AI assistants will happily suggest os.execute, io.open, socket.http, full Lua 5.4 features, and other things that simply do not exist in this runtime. This page tells you how to keep them in lane.
What the AI must understand before it writes a line
Section titled “What the AI must understand before it writes a line”Drop this paragraph into the system prompt (Claude project instructions, Gemini Gem context, ChatGPT custom instructions, Ollama Modelfile SYSTEM). It’s the smallest unit of context the model needs to stop hallucinating standard-Lua features.
You are helping write a Lua script for ChilloutVR (CVR), running under MoonSharpinside the CVR client. Constraints you MUST respect:
1. MoonSharp Lua 5.2-equivalent with some 5.3 features. NO os.execute, NO io.*, NO socket.*, NO package.loadlib, NO C bindings, NO debug library, NO require() for any module not in the allow-list below.2. Allow-listed require() modules: 'UnityEngine', 'UnityEngine.UI', 'UnityEngine.AI', 'TextMeshPro', 'CVR', 'CVR.CCK', 'CVR.Network', 'RCC', 'StaticOverrides', 'System'. The Lua engine refuses to load anything else.3. Pre-registered globals (no require needed): the boundObjects / boundComponents / boundGameObjects tables, players (PlayerAPI), instances (InstancesAPI), spawnable (SpawnableAPI), world (WorldAPI), avatar (AvatarAPI), debug, print.4. The script is client-side. Every player runs their own copy locally. Do not assume shared state — synchronize via the CVR.Network module.5. Lifecycle entry points: Start(), Update(), Receive(name, ...), plus CVR game events (OnPlayerJoin, OnPlayerLeave, OnAvatarReady, etc.).6. The runtime enforces a four-axis access gate: Environment x Object x Owner x Scope. Any binding you call may throw if you call it from the wrong context.7. Persistence is via PublicBin / PrivateBin objects. There is no filesystem.8. Authoritative documentation lives at https://unofficial-cvr-documentation.shinter.dev/lua/ — fetch and read pages from there before answering API questions.
When you are unsure whether a function exists, say so and refuse to fabricate.URLs to feed the AI (or have it fetch)
Section titled “URLs to feed the AI (or have it fetch)”Pages that pay back their context budget when the AI can read them:
/lua/api/bindings/— every type Lua can see, organized byrequire()module. The single most important reference./lua/api/context-and-permissions/— the four-axis access model. Without this the AI will assume a binding is available and produce code that throws at runtime./lua/api/events/— actual game-event names and signatures./lua/api/globals/— the pre-registered table layout the AI keeps confusing with_G./lua/api/lua-behaviour/— what aCVRLuaClientBehaviouractually exposes./lua/api/storage/— PublicBin / PrivateBin semantics. AI assistants will reach forio.openif they don’t know about these./lua/security/— high-level threat model. Worth pasting if the AI tries to “encrypt” a hard-coded password.- For specific worked patterns:
/lua/recipes/and/lua/examples/.
If your tool supports tool-use / browsing (Claude with WebFetch, Gemini with grounding, ChatGPT with browsing), instruct it to fetch from these URLs rather than rely on training data.
Tool-specific notes
Section titled “Tool-specific notes”Claude
Section titled “Claude”- Drop the system prompt above into a Project’s custom instructions or use it as the first message. Claude follows constraint lists well.
- Enable the WebFetch tool if you have it; cite the URLs verbatim and ask Claude to open them before generating.
- Claude tends to over-comment and produce docstrings. Tell it “no comments unless the WHY is non-obvious.”
Gemini
Section titled “Gemini”- Use Gems (custom system instructions) for the constraint block. Gemini drifts back toward standard Lua faster than Claude does, so re-grounding mid-conversation helps.
- Grounding sources work well — paste the docs URL list as a “browsing context.”
- Watch for
printbeing conflated withconsole.logstyle — Gemini sometimes inventsLogger.Log.
ChatGPT (GPT-4o / o1 / o3 / 4.5)
Section titled “ChatGPT (GPT-4o / o1 / o3 / 4.5)”- Use Custom Instructions or a Project for the constraint block.
- The reasoning models (o1, o3) work well when you ask them to enumerate the bindings they plan to use before writing.
- 4o is fine for short scripts; longer scripts benefit from a planning pass.
Ollama / local models
Section titled “Ollama / local models”- Put the constraint block in the Modelfile
SYSTEMdirective. Local models drift more, so the smaller and more declarative your system prompt, the better. - Avoid models below ~8 B parameters for non-trivial scripts; they will quietly invent APIs.
- A two-step pipeline works well locally: first a “plan” pass (which bindings, which lifecycle methods) then a “write” pass.
A user-message prompt template
Section titled “A user-message prompt template”Once the system prompt is set, structure your individual requests like this:
Goal: <one sentence — "make a button that teleports the local player to a spot when clicked">
Required behaviour:- <bullet 1>- <bullet 2>
Constraints:- Must run as a CVRLuaClientBehaviour on the world.- Must use only the bindings on /lua/api/bindings/.- No external HTTP unless the world has the http permission and I confirm.
Output:- A single Lua file ready to drop into the script slot.- Brief comment on how each lifecycle hook is used.
Before writing, list the modules you'll require() and the events you'll hook,and ask me to confirm.The “list modules first, confirm, then write” step reliably catches hallucinations before they end up in code.
Common pitfalls AI assistants fall into
Section titled “Common pitfalls AI assistants fall into”require('socket')/require('http')— neither exists. Network goes throughCVR.Network.io.open/os.date— neither. Persistence isPublicBin/PrivateBin. For time, useos.time()(one of the fewossurvivors) or Unity’sTime.realtimeSinceStartupvia the UnityEngine module.debug.getinfo— thedebuglibrary is heavily redacted; treat as unavailable.- Coroutines for async — MoonSharp supports coroutines but the script lifecycle is single-threaded and event-driven. Tell the AI: prefer
Update()ticks tocoroutine.wrap/yield. - Standard Lua metatables on bound types — most Unity / CVR bound types are
userdataand don’t acceptsetmetatable. The AI will sometimes try to monkey-patch them; reject those suggestions. - Network events crossing trust boundaries — when the AI writes “send a Move command and the receiver moves the player,” it usually forgets that the sender is forgeable. Always reread its output through the Security Guidance lens.
Verification checklist before you ship
Section titled “Verification checklist before you ship”After the AI hands you a script, before you paste it into the CCK, walk this list:
- Every
require(...)is in the allow-list. Anything else fails to load. - Every method call has a matching entry in the bindings reference. Fast spot-check: paste the output into your AI tool with the bindings page in context and ask it to flag any call it can’t trace.
- Lifecycle methods only. No top-level code beyond declarations and the entry-point definitions.
- No hard-coded secrets. AI assistants love to suggest passwords-as-constants. Re-read Security.
- All cross-player state goes through
CVR.Networkwith sender validation in the receiver. - The script doesn’t touch any persistent storage you don’t intend. PrivateBin writes survive across sessions; double-check before shipping a public-world script that writes per-player state.
What to do when the AI is stuck
Section titled “What to do when the AI is stuck”- Show it the relevant decompile path. The bindings page cites them, e.g.
_LUAINSTANCE_ScriptedCamera.cs. Real, named source files anchor the AI to ground truth. - Ask it to write the test before the script. If it can’t articulate “how would I know this works in-world,” the script isn’t ready.
- Switch tools. If Claude refuses or drifts, Gemini sometimes has a different blind spot; the failure modes don’t perfectly overlap.
See also
Section titled “See also”- Getting Started — manual authoring path.
- API Reference — index of everything the runtime exposes.
- Context & Permissions — the four-axis gate model.
- Security Guidance — threat model.