Language Basics
For the purposes of these docs, we refer to CSL and the runtime/platform around it interchangeably
This page is the “data modeling” side of CSL: primitives, structs/classes, and the patterns you’ll use to represent game state cleanly.
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: b8 (alias: bool), b16, b32, b64
health: int = 100;
speed: float = 6.5;
is_dead: bool = false;
string is used constantly for IDs, names, and formatting.
item_id := "sword";
log_info("picked up %", {item_id});
v2, v3, v4 are built-in math/data types:
Enums are great for readable “mode/state” fields:
Structs (value types)
Use structs for “plain data” you want to copy around cheaply (config, small state blobs, descriptors).
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(...).
You can inherit from other structs/classes:
Use inheritance when there’s a real “is-a” relationship. For most gameplay data, composition (fields) is simpler and easier to maintain.
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:
Use expr.(T):
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:
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.
Keep server-authoritative data authoritative. If it affects gameplay, don’t compute it only on the client.
For player-specific patterns, see Adding Player Logic. For the ECS side of the world, see Entities and Components.