For the complete documentation index, see llms.txt. This page is also available as Markdown.

Language Basics

CSL primitives, enums, structs, classes, references, and polymorphism.

This page is the “data modeling” side of CSL: primitives, structs/classes, and the patterns you’ll use to represent game state cleanly.

If you’re brand new, start with Getting Started with CSL. This page assumes you already know how to declare variables and write proc/method.

Primitive types

Numbers and bools

  • Signed integers: s8, s16, s32, s64 (alias: int == s64)

  • Unsigned integers: u8, u16, u32, u64 (alias: uint == u64)

  • Floats: f32 (alias: float), f64

  • Booleans: bool

health: int = 100;
speed: float = 6.5;
is_dead: bool = false;

CSL widens numbers when that is safe: smaller signed integers can become wider signed integers, smaller unsigned integers can become wider unsigned integers, unsigned integers can become wider signed integers when the target can hold the full range, and f32 can become f64.

Strings

string is used constantly for IDs, names, and formatting.

item_id := "sword";
log_info("picked up %", {item_id});

Vectors

v2, v3, v4 are built-in math/data types:

Enums

Enums are great for readable “mode/state” fields:

Enum values are commonly referenced with the shorthand .VALUE when the compiler can infer the enum type.

Structs (value types)

Use structs for “plain data” you want to copy around cheaply (config, small state blobs, descriptors).

Structs can inherit from other structs. Compound literals include inherited fields first:

When to use a struct

  • A descriptor/config object (*_Desc)

  • Small, self-contained state you want to copy by value

  • Data you want to embed inside another type

When not to use a struct

  • Large mutable state shared across multiple systems (use a class)

  • “Identity” objects (players, items, long-lived objects) (use a class)

Classes (reference types)

Classes are reference types. Create instances with new(...).

Inheritance

You can inherit from other structs/classes:

Derived classes inherit default field values. They can override inherited defaults with field = value in the class body:

Arrays, slices, and dynamic arrays

You’ll see three common shapes:

  • Fixed arrays: [N]T (compile-time size)

  • Slices / managed arrays: []T (a view of an array)

  • Dynamic arrays: [..]T (resizable list)

Example:

For a full guide, see Arrays and Collections.

Passing by reference (ref)

If a procedure needs to modify a value, use ref:

This is especially useful for “layout-style” APIs that progressively cut/shrink a rect:

Casting

Use expr.(T) or cast(T)expr:

When the target type is already known, expr.() and cast expr infer the target type.

Type polymorphism (generic-style procs)

CSL supports polymorphic procedures using $ to deduce types at the callsite:

Types as values (typeid)

typeid is a value that represents a type at runtime and/or for polymorphic calls:

value.#type returns the runtime type of a class value. Use it before downcasting from a base class when the exact derived type matters.

Practical modeling guidelines

  • Per-player state lives on Player. If it should differ between players, it shouldn’t be global.

  • Prefer small structs for descriptors and store runtime state in classes/components.

  • Run gameplay in the shared predicted path. Do not compute gameplay state only under is_local() or guard it with Game.is_server().

For player-specific patterns, see Adding Player Logic. For the ECS side of the world, see Entities and Components.

Last updated