World access and player control
Lesson 7.8
This lesson reads the world in bulk (ray casts, many blocks at once, properties, light, containers), listens for block changes, and controls players (movement modifiers, model parts, locked slots, forced input, food). Everything works on vanilla blocks and the vanilla player.
Look along a line
cl_raycast_opts o = CL_RAYCAST_OPTS_INIT;
o.blocks = CL_RAY_BLOCKS_OUTLINE; /* what the crosshair picks; also COLLIDER, VISUAL, NONE */
o.fluids = CL_RAY_FLUIDS_ANY;
o.entities = true;
o.ignore = player;
cl_raycast_hit hit;
if (cl_level_raycast(level, eye, far_point, &o, &hit) == CL_OK && hit.type == CL_HIT_BLOCK) {
/* hit.block_pos, hit.state, hit.face, hit.distance */
}
Many blocks at once
cl_block_pos lo = {x - 8, y - 4, z - 8}, hi = {x + 8, y + 4, z + 8};
size_t n = 0;
cl_level_get_blocks(level, lo, hi, NULL, 0, &n); /* how many (17 * 9 * 17) */
cl_block_state* states = malloc(n * sizeof *states);
cl_level_get_blocks(level, lo, hi, states, n, &n); /* x fastest, then z, then y */
A buffer smaller than the box gets nothing. WASM reads at most 262,144 states per call, so read big areas in slices. cl_level_snapshot(level, lo, hi) keeps a copy you can read later (cl_snapshot_get, cl_snapshot_copy) and must release (cl_snapshot_release).
Properties and block facts
char text[32];
size_t len = 0;
if (cl_block_state_property(state, CL_STR("open"), text, sizeof text, &len) == CL_OK && strcmp(text, "false") == 0) {
cl_level_set_block_property(level, pos, CL_STR("open"), CL_STR("true"), CL_SET_ALL);
}
cl_block_info info;
cl_block_get_info(state, &info); /* hardness, solid_render, light_emission, map_color, ... */
cl_block_sound_type(state, text, sizeof text, &len); /* "stone", "wood", "glass", ... */
Light, height and containers
cl_light_info li;
cl_level_light(level, pos, &li); /* li.block, li.sky, li.combined */
int32_t ground = 0;
cl_level_heightmap(level, x, z, CL_HEIGHTMAP_MOTION_BLOCKING, &ground);
int32_t size = 0;
if (cl_level_container_size(level, chest, &size) == CL_OK) {
cl_item_stack s = cl_stack_create_by_name(CL_STR("minecraft:diamond"), 3);
cl_level_container_set(level, chest, 0, s);
cl_stack_release(s);
}
Block changes
level.block_changed (cl_ev_block_changed: level, pos, old_state, new_state, client) fires for every changed block; on the server at the end of the level’s tick. Subscribe like any event and filter by the new block.
Players
cl_movement_modifier m = CL_MOVEMENT_MODIFIER_INIT;
m.id = CL_STR("broken_leg");
m.speed_mult = 0.6f;
m.can_sprint = false;
cl_player_add_movement_modifier(player, &m);
cl_player_set_part_visible(player, CL_PART_LEFT_ARM, false);
cl_player_set_hand_disabled(player, CL_HAND_OFF, true);
cl_player_lock_slot(player, 0, true, false);
cl_input_override in = CL_INPUT_OVERRIDE_INIT; /* every key -1: the player's own */
in.crouch = 1;
cl_player_set_input_override(player, &in);
cl_player_set_food(player, 6);
cl_entity_set_air(player, 100);
cl_player_set_natural_regen(player, false);
On a client, cl_camera_get(&cam) tells where the camera is and whether it is in first person.
Exercise
Count the diamond ores in a 16-block cube around a player with one cl_level_get_blocks call.
Show a solution
static int count_diamonds(cl_level level, cl_block_pos c) {
cl_block_pos lo = {c.x - 8, c.y - 8, c.z - 8}, hi = {c.x + 7, c.y + 7, c.z + 7};
static cl_block_state buf[16 * 16 * 16];
size_t n = 0;
if (cl_level_get_blocks(level, lo, hi, buf, 16 * 16 * 16, &n) != CL_OK) return 0;
int found = 0;
for (size_t i = 0; i < n; ++i)
if (cl_block_state_is(buf[i], CL_STR("minecraft:diamond_ore"))) ++found;
return found;
}
Recap: cl_level_raycast, cl_level_get_blocks, snapshots, state properties, cl_block_get_info, light, heightmaps and containers read the world; level.block_changed tells you what changed; cl_player_* movement, part, hand, slot, input and food functions control players. Guides: World Access, Player Control.