Skip to content

Variables & Types

Declaring Variables

Use let for mutable variables and const for constants:

rs
let health: int = 20;
let name: string = "Steve";
const MAX_PLAYERS: int = 16;

const values cannot be reassigned:

rs
const PI: fixed = 3.14;
PI = 3.15; // Error: cannot reassign constant

Negative literals work in const declarations:

rs
const MIN_SCORE: int = -50;
const DEPTH: int = -64;      // e.g. Minecraft bedrock level

Types

RedScript has a small set of primitive types plus a few Minecraft-facing value categories that show up constantly in real datapacks:

TypeDescriptionExample
intInteger (scoreboard, 32-bit)42, -7, 0
fixedFixed-point decimal, stored at scale ×100001.0, 1.5
doubleIEEE 754 double (NBT-backed)x as double
stringText"hello", "Steve"
boolBooleantrue, false
selectorMinecraft entity/player selector@s, @a[tag=runner]
T[]Array of one element typeint[], string[]
Option<T>Value that may be absentSome(42), None

v2.5.0 note: float has been renamed to fixed. Any existing code using float will trigger a deprecation warning and should be migrated to fixed. The double type is new.

Integers

rs
let score: int = 0;
let negative: int = -10;
score = score + 1;

Fixed-point (fixed)

fixed represents decimal values on scoreboards as integers scaled by ×10000. This is the language-level fixed-point representation for fractional math in datapacks.

rs
let speed: fixed = 1.5;   // stored as int 15000
let half: fixed  = 0.5;   // stored as int 5000
let one: fixed   = 1.0;   // stored as int 10000

Key rules:

  • Decimal literals are scaled for storage: 1.010000, 1.515000, 0.00
  • fixed arithmetic is scale-aware: a * b and a / b are lowered by the compiler with ×10000 compensation.
  • Raw integer literals are int; use as fixed to convert from int or double.
rs
let x: int = 5;
let xf: fixed = x as fixed;  // 5 * 10000 = 50000

let d: double = 3.14 as double;
let df: fixed = d as fixed;   // floor(3.14 * 10000) = 31400

Note: stdlib/math.mcrs still exposes legacy scale-specific helpers such as sin_fixed, cos_fixed, sqrt_fixed, mulfix, and divfix, which are ×1000 integer helpers. These are not replacements for language fixed operators.

Double (double)

double stores an IEEE 754 double-precision value in NBT storage (rs:d). It is used when integer or fixed-point precision is insufficient — e.g. for high-precision trig, physics simulation, or values that span a very wide range.

rs
let pi: double = 3 as double;   // start from int 3
// ... use math_hp functions to refine

Conversion:

rs
let n: int    = 42;
let d: double = n as double;    // 42.0

let f: fixed  = 1.5;
let d2: double = f as double;   // 1.5 (divided by 10000 automatically)

let d3: double = /* some double */;
let back: fixed = d3 as fixed;  // floor(d3 * 10000)
let back_int: int = d3 as int;  // floor(d3)

NBT storage: double values live in rs:d __dp0 (and __dp1, __dp2, …). Direct assignment uses data modify storage. Use functions from stdlib/math_hp.mcrs for arithmetic.

Explicit as casting

Starting in v2.5.0, numeric type conversions require an explicit as cast. Implicit coercion no longer works between int, fixed, and double.

rs
// ✅ Correct — explicit cast
let n: int   = 5;
let f: fixed = n as fixed;   // 50000

// ❌ Error — implicit coercion removed
let f2: fixed = n;           // compiler error: expected fixed, got int

Strings

Use canonical f-strings (f"...{expr}...") for interpolation:

rs
let player: string = "Alex";
let msg: string = f"Hello, {player}!";
say(msg); // Hello, Alex!

Booleans

rs
let alive: bool = true;
let creative: bool = false;

Selectors

selector is the type used by commands that target entities or players. It is not stored like an int; it compiles into Minecraft selector syntax and execute context.

rs
let nearest: selector = @p;
give(nearest, "minecraft:apple", 1);

foreach (player in @a[tag=runner]) {
    actionbar(player, "Keep running!");
}

Use @s when the current execute context matters, especially inside trigger/event handlers and foreach loops.

Arrays

Arrays hold multiple values of the same type:

rs
let scores: int[] = [10, 20, 30];
let names: string[] = ["Alice", "Bob"];

Access elements by index:

rs
let first: int = scores[0]; // 10

Arrays are homogeneous: every element must have the same type. Use for value in array to iterate values, or for i in 0..array.len when you need indexes.

Optional Values

Option<T> represents a value that might be missing. Use Some(value) when the value exists and None when it does not.

rs
let maybe_score: Option<int> = Some(10);
let missing_score: Option<int> = None;

if let Some(score) = maybe_score {
    say(f"Score: {score}");
}

if let Some(x) = option { ... } is the supported unwrap pattern. Do not assume Rust-style helpers like .unwrap() or .unwrap_or() exist.

Type Inference

RedScript can infer the type when the value is obvious:

rs
let health = 20;        // inferred as int
let name = "Steve";     // inferred as string
let alive = true;       // inferred as bool
let speed = 1.5;        // inferred as fixed

This also works for const — the type annotation is optional:

rs
const MAX_PLAYERS = 16;     // inferred as int
const PREFIX = "[Game]";    // inferred as string
const RATE = 0.5;           // inferred as fixed

Explicit types are recommended for clarity, but optional.

Global Variables

Variables declared at the top level are global and accessible from any function:

rs
let score: int = 0;

@throttle(ticks=20)
fn update() {
    score = score + 1;
    actionbar(@a, f"Score: {score}");
}

fn reset() {
    score = 0;
}

Global variables are stored as Minecraft scoreboard objectives.

Next Steps

Released under the MIT License.