Sandbox and limits
A script runs against a whitelist: the Kotlin standard library, java.time, java.math, kotlin.random.Random and the whole nursultan.* API. Every handler invocation carries a 250 ms budget.
on<ClientTickEvent> {
try {
if (world.block(player.position()).liquid()) control.jump()
} catch (error: ScriptStateException) {
log.warn("no world: ${error.message}")
}
}What you get
| Package | Description |
|---|---|
nursultan.** |
the whole script API |
kotlin.* |
every class directly in the kotlin package, subpackages excluded |
kotlin.collections.** |
lists, maps, sets and their operators |
kotlin.sequences.** |
lazy sequences |
kotlin.ranges.** |
ranges and progressions |
kotlin.text.** |
strings, regex, StringBuilder |
kotlin.comparisons.** |
comparator builders |
kotlin.math.** |
math functions and constants |
kotlin.random.** |
Random |
kotlin.enums.** |
entries on enums |
kotlin.annotation.** |
annotation targets and retention |
kotlin.jvm.internal.**, kotlin.jvm.functions.** |
intrinsics and lambda types the compiler emits |
java.util.stream.** |
streams and collectors |
java.util.function.** |
functional interfaces |
java.time.** |
instants, durations, dates |
java.math.** |
BigInteger, BigDecimal |
java.lang (single classes) |
String, StringBuilder, Math, number wrappers, common exceptions |
java.util (single classes) |
collections, Optional, UUID, Random, Locale, BitSet, Objects |
java.util.concurrent.ThreadLocalRandom |
the only class allowed out of java.util.concurrent |
kotlin.reflect.KProperty* |
as a type only, for by delegates |
nursultan.** and kotlin.random.Random are default imports; the kotlin.* rows are Kotlin's own defaults.
What is blocked
| Blocked | Description |
|---|---|
java.io, java.nio |
no file access; only assets and configs touch the disk |
java.net |
no sockets, no http |
java.lang.Thread, java.util.concurrent |
no threads, pools or timers, ThreadLocalRandom aside |
java.lang.Class, java.lang.reflect, kotlin.reflect |
no reflection; only the KProperty types pass |
java.lang.ClassLoader, java.lang.System |
no class loading, no process or environment access |
net.minecraft.**, fun.nursultan.** |
no direct game or client internals |
nursultan.internal.BudgetControl |
the one denied class inside nursultan |
| native methods | a native method in a script class is rejected |
invokedynamic |
only LambdaMetafactory and StringConcatFactory bootstraps pass |
The check runs over the compiled classes before the script executes: a violation fails the load with script class <name> references blocked symbol: <symbol> and nothing runs.
Budgets
| What | Limit |
|---|---|
| one handler, command or callback invocation | 250 ms |
| the top level of the file at load | 5 s |
| throws in a row before the script switches off | 5 |
| error reports per script | one per 3000 ms, the rest counted as (+N suppressed) |
Overrunning throws ScriptTimeoutError, reports it and switches the script off; the deadline is checked every 512th instrumented jump.
One successful invocation resets the throw counter.
Exceptions
| Exception | Extends | Thrown when |
|---|---|---|
ScriptException |
RuntimeException |
bad argument, unknown module/setting/entry reference, invalid id or url |
ScriptStateException |
ScriptException |
no world or player, entity left the world, script already unloaded |
ScriptThreadException |
ScriptException |
an API call that needs the client thread ran off it |
ScriptApiException |
ScriptException |
requireApi(n) with n above ApiVersion.CURRENT |
ScriptTimeoutError |
Error |
the handler budget ran out |
Each takes a String message; ScriptException also has a (message, cause) constructor.
ScriptTimeoutError is an Error, not a ScriptException — catching it changes nothing, the script is reported and switched off anyway.
Resource limits
| Resource | Limit |
|---|---|
| keys per config | 4096, ScriptException on the next new key |
| chars per config value | 65536, ScriptException above it |
| asset file | 8 MiB, bytes() and text() return null above it |
entries per assets.list() |
1024, extra names dropped with one console warning |
| embedded font or PNG | 8 MiB, ignored with one console warning above it |
| live gpu resources per script | 64 meshes, pipelines and render types together, IllegalStateException |
| attributes per vertex format | 16, IllegalArgumentException |
| vertices per mesh | 1 048 576, IllegalStateException |
| indices per mesh | 4 194 304, IllegalStateException |
Mesh buffers are freed while the script is switched off; the resources still count against the 64 until the script unloads.
Last updated