PLAYER, WALLET, AND CONTROL · STEP 9

Displayed Stats vs Enforced Effects: Trace Every Number to a Consumer

A number becomes gameplay only when reachable production code reads it and changes an outcome. NiceChunk takes all ten skill levels from PlayerSkills, but their consumers differ: Precision, Exploration, Smelting, and Forging reach contracts; Swiftness changes local controls; several other cards currently describe planned effects. PlayerProfile's six initialized attributes are stored defaults rather than the source of these levels. This guide names the actual consumer and boundary for every current claim.

34 min read
Generated NiceChunk workshop where the canonical villagers route stored values through separate browser, contract, and Guardian mechanisms.
The scene is a consumer-tracing metaphor. A displayed tile affects gameplay only when a reachable production path reads it; the illustration itself proves no consumer or rule.
PLAYER QUESTION Which Profile and skill numbers change real outcomes, which are presentation only, and who enforces each active effect?
Learning path
Player, wallet, and control · Step 9 of 15
Audience
Players and integrators comparing visible stats with enforced behavior
Production entry reviewed
/play/index.html → /play/main.js → play/* plus Chunk, Smelting, Backpack, Skills, and Guardian consumers
Reviewed
25 July 2026 · current source, focused browser tests, Rust tests, and Devnet transaction probes
Stored value
Bytes written into an account or browser object. Storage alone does not make an effect active.
Displayed value
Text, a progress bar, or a preview calculated for the interface. Display alone does not enforce settlement.
Authoritative level
One of ten cached u8 levels in PlayerSkills, recomputed from XP and SkillRuleTable cumulative thresholds.
Local consumer
Reachable browser code that changes controls, prediction, animation, or another client-only result.
Contract consumer
A Solana program that validates PlayerSkills identity and uses a level in accepted state calculation.
Prediction
A reversible local estimate shown before the transaction settles. It is not a committed outcome.
Settlement
The state transition accepted by the owning program after account, authority, and rule checks.
Planned effect
A displayed design parameter with no current production consumer changing the claimed outcome.
Guardian state
Realtime pose, chat, region, and building-discovery data. Guardian does not own PlayerSkills or redefine contract effects.
Program-scoped identity
Program owner, PDA seeds, bump, wallet owner, and GlobalConfig must match the expected domain.

Key points

PlayerSkills is the level authority

All ten Profile skill cards use chain-decoded PlayerSkills levels. Local behavior does not create a competing effective level.

A shared source does not imply a shared consumer

The same level can drive a browser preview, contract calculation, both, or neither.

Contracts revalidate skill identity

Chunk, Smelting, and Backpack derive PlayerSkills from owner and GlobalConfig and reject substituted data.

Prediction and settlement remain separate

Mining and Smelting can preview from the same level, but the owning program recomputes the accepted result.

Swiftness is chain-backed but locally consumed

Its level is public on chain; its multiplier changes browser controls rather than Guardian or contract movement state.

Unconsumed labels are design intent

Burden capacity, Craftsmanship tier, Stamina fatigue, Strength handling, and Appraisal visibility have no current enforcement loop.

TRACE THE READ PATH

A stored or displayed number is not an effect until production code consumes it

Use four questions for every stat: where is it stored, where is it decoded, which reachable function reads it, and what concrete outcome changes? If the chain stops at a card or description, the value is presentation. If a browser reads it only for preview, it is not contract settlement.

PlayerSkills solves the source-of-level question: all ten XP totals and levels come from one owner-scoped PDA and one SkillRuleTable. It does not automatically make every designed effect active. Each system still needs an explicit consumer.

Contract consumers also need identity checks. Chunk, Smelting, and Backpack derive expected PlayerSkills, validate Skills owner, layout, wallet owner, GlobalConfig, and level range, then use only the relevant level.

Storage Where bytes live

PlayerProfile, PlayerSkills, rule table, Backpack, or browser state.

Decoder How identity and layout are checked

Wrong program owner or PDA must fail closed.

Consumer Which production function reads the value

A description or test helper is not enough.

Outcome What changes

Movement speed, resource volume, drop weighting, material output, grade, or durability.

Generated NiceChunk workshop where a value tile reaches a machine only through a visible working linkage.
The linkage illustrates the consumer test: storage and display are insufficient without reachable code that changes an outcome.

PLAYERPROFILE DEFAULTS

Six PlayerProfile attributes are stored defaults, not the ten-skill source

PlayerProfile version 7 initializes health 100, energy 100, stamina 100, mining power 1, build power 1, and defense 0. Those bytes remain schema fields, but Profile skill cards do not decode them.

Profile derives overview level from the average of ten PlayerSkills levels and six visible stat cards from skill definitions. Chunk mining output, Smelting, Forging, and movement do not read the six defaults.

Do not equate PlayerProfile stamina with the Stamina skill, or mining power with Precision Gathering. Similar labels are separate until a consumer explicitly connects them.

Health / Energy / Stamina 100 / 100 / 100

Initialized in PlayerProfile and not skill-level inputs.

Mining / Build / Defense 1 / 1 / 0

Stored defaults with no current effect loop identified here.

Ten skill levels PlayerSkills bytes 156–165

Source for Profile cards and active consumers.

Overview Level 1 + floor(sum(skillLevels) / 10)

A browser presentation derived from chain levels.

Generated NiceChunk archive with six sealed profile tablets beside a separate ten-slot skill ledger.
The separated tablets illustrate distinct schemas. Similar names do not create a data dependency.

CHAIN LEVELS, BROWSER PRESENTATION

The browser derives cards and progress from chain levels, XP, and thresholds

Profile builds one skill state from PlayerSkills XP, PlayerSkills levels, and SkillRuleTable thresholds. It normalizes those values, calculates tier progress from the cumulative table, and uses definitions to format card text.

The six overview cards show Gathering, Carry, extra Smelting output, Speed, Strength, and Crafting. These are presentation formulas over authoritative levels. Enforcement status depends on the consumer matrix, not on accurate arithmetic.

A missing PlayerSkills account produces zero levels until synchronization creates it. A malformed present account is rejected. There is no local XP fallback or maximum merge.

XP source PlayerSkills.xp[10]

Ten u64 totals decoded from one owner PDA.

Level source PlayerSkills.levels[10]

Ten u8 values recomputed by Skills.

Tier source SkillRuleTable.thresholds[10][10]

Strictly increasing cumulative values.

Card formulas play/play-profile-skills.js

Presentation parameters do not replace contract formulas.

Generated NiceChunk profile desk where one public skill ledger and one rule tablet feed multiple display cards.
The cards are presentation consumers of chain state. Their existence does not prove every described effect is implemented.

CURRENT CONSUMER MATRIX

Four contract effects, one local movement effect, and five planned effect loops

Precision Gathering is consumed by browser mining preview and Chunk's authoritative gathered-volume calculation. Exploration is consumed by Chunk's deterministic extra-drop weighting. Smelting is consumed by preview and Smelting's physical output calculation. Forging is consumed by Backpack's durability bonus after material-derived grade and item level establish the base.

Swiftness is consumed by local movement controls: walking speed is 14.8 times the skill multiplier and running remains exactly twice walking. Guardian receives pose updates but does not authorize that multiplier.

Burden has an enforced XP earning rule based on pre-mine Backpack mass, but displayed safe-carry kilograms do not cap inventory or movement. Craftsmanship tier, Stamina fatigue reduction, Strength one-hand mass, and Appraisal traits are displayed parameters without consumers.

Precision / Exploration Chunk contract

Authoritative gathered volume and extra-drop weighting.

Smelting / Forging Game contracts

Authoritative material output and verified forge outcome.

Swiftness browser controls

Chain level, local speed consumer.

Other five partial or planned

XP may be authoritative while displayed effect lacks enforcement.

Generated NiceChunk workshop with ten skill tokens routed to contract machines, local controls, or unfinished stations.
The routing illustrates current consumer categories. It does not imply unfinished stations are active.

PRECISION AND EXPLORATION

Mining previews locally, then Chunk recomputes authoritative volume and extra drops

Mining reads the current Precision effect to build a pending reward preview. This improves responsiveness but remains reversible until chain confirmation.

Chunk independently validates PlayerSkills and reads Precision and Exploration levels. Precision selects gathered volume. Exploration changes deterministic extra-drop weighting under ResourceDropTable. The browser cannot submit an accepted yield or rarity result as authority.

After confirmation, local state removes only committed blocks and Backpack refresh supplies stored resources. Failure restores pending state.

Preview input chain-derived Precision level

Used for immediate local presentation.

Settlement input validated PlayerSkills PDA

Chunk reads levels from chain bytes.

Precision outcome resource volume

Contract-selected from authoritative level.

Exploration outcome extra-drop weighting

Applied inside deterministic selection.

Generated NiceChunk mining scene with a translucent local estimate beside a sealed contract measurement station.
The stations distinguish prediction from settlement. Only the program-owned result writes authoritative state.

GAME CONTRACT CONSUMERS

Smelting and Forging read PlayerSkills but compute accepted output inside their programs

Smelting validates owner-derived PlayerSkills and reads Smelting level. It applies 10,000 + 500 × level basis points, capped at 15,000, to each non-merge output's physical volume and integer stack quantity. The browser previews with the same level, but inputs, fuel, output quantity, volume, and capacity are revalidated on chain. Material-merge recipes preserve all consumed quantity and volume instead of applying the skill multiplier.

Verified Forging validates PlayerSkills and reads Forging level. Backpack first combines material grade, item level, weak-material caps, quality, and raw durability into a capped base, then applies exactly +5% durability per Forging level, capped at level 10. The Profile card describes that final level factor, not the complete material calculation.

Both programs fail closed on substituted identity. A missing account is level zero; a present malformed account is rejected.

Smelting 0% to +50% extra output

Exactly +5 percentage points per level, enforced on non-merge output volume and integer stack quantity.

Forging material base plus level durability bonus

Level multiplies the capped material-derived base by 1 + 0.05 × min(level, 10).

Missing PlayerSkills level 0

Allows base behavior before account creation.

Malformed PlayerSkills reject

Wrong identity, layout, GlobalConfig, or level fails.

Generated NiceChunk foundry where one verified skill ledger feeds separate smelting and forging calculation stations.
The stations share a level source but apply different program formulas and account checks.

CHAIN-BACKED LEVEL, LOCAL CONSUMER

Swiftness changes browser movement speed, while running stays exactly two times walking

The level source is PlayerSkills, including +1 XP for proven mining coordinates at least 160 blocks apart. The consumer remains local browser controls.

playerMovementSpeeds multiplies base walking speed 14.8 by 1 + 0.03 × level, capped at 1.3, then defines running as walking × 2. applyPlayerMovementSpeeds writes both values to controls.

Guardian packets report movement pose for realtime presence. Guardian does not receive PlayerSkills, calculate the multiplier, or settle movement distance. This is a local effect backed by a chain-authoritative level.

Base walking speed 14.8

Multiplied by Swiftness effect.

Per level +3%

Level 10 gives a 1.3 multiplier.

Running walking × 2

Uses the same skill-adjusted base.

Guardian role realtime pose transport

Not a level or speed authority.

Generated NiceChunk path where a public training marker informs local walking and running controls while a Guardian relay observes pose.
The relay is not Swiftness authority. The scene does not prove movement settlement or anti-cheat.

CURRENT BOUNDARIES

Guardian does not fill missing stat consumers, and several effect loops remain unfinished

Guardian handles region connection, nearby pose, chat, and verified building discovery. Its movement protocol does not carry ten skill levels, PlayerProfile defaults, Backpack mass, Smelting multiplier, or Forging formula.

No current path enforces displayed Burden capacity, Craftsmanship process tier, Stamina fatigue multiplier, Strength one-hand limit, or Appraisal trait count. These can remain product targets, but UI and documentation must not present them as enforced.

Completion requires an explicit consumer, PlayerSkills identity validation at the trust boundary, and tests proving changed outcome. Do not infer enforcement from a card, source coefficient, or authoritative XP balance.

Guardian carries pose, chat, region, building discovery

Realtime service data with separate checks.

Guardian does not carry skill or Profile stat authority

It cannot make a planned card enforced.

Currently planned carry, tier, fatigue, hand limit, traits

No changed production outcome found.

Activation requirement consumer + identity validation + outcome tests

All three are needed before claiming enforcement.

Generated NiceChunk Guardian outpost with active pose relays and separate unfinished gameplay stations.
The outpost illustrates service boundaries. Guardian connectivity does not substitute for missing consumers.

READ THE IMPLEMENTATION

Ten equations and thirteen literal excerpts separate storage, presentation, local effects, and settlement

Each claim identifies a concrete reader and changed outcome. Excerpts are continuous ranges from current source, and missing consumers are stated instead of inferred.

The consumer test

Effect(v, a) exists only if reachable production code reads v and changes outcome(a)

Storage, decoding, and display are not gameplay enforcement.

PlayerSkills level source

level[s] = PlayerSkills.levels[index(s)]

All ten Profile cards share this source.

Profile overview level

ProfileLevel = max(1, 1 + floor(sum(ten skill levels) / 10))

This is a browser summary, not another ledger.

Precision preview

previewVolume = floor(1,000 cm³ × PrecisionBps / 10,000)

Chunk independently recomputes the accepted resource volume on chain.

Smelting output multiplier

SmeltingBps = min(15,000, 10,000 + 500 × level)

Implemented in preview and contract settlement.

Swiftness walking speed

walk = 14.8 × min(1.3, 1 + 0.03 × level)

The browser applies this control effect.

Running speed

run = walk × 2

Swiftness changes the shared base, not sprint ratio.

Forging material grade

grade = min(10, round((3×materialGrade + itemLevelGrade)/4), materialGrade + 1, weakGradeCap)

Forging level does not change grade in the current settlement formula.

Forging durability factors

base = min(effectiveDurability × (90 + 5×grade) × (100 + itemLevel/2) / 10,000, 1.05×rawDurability); durability = floor(base × (100 + 5×min(level,10)) / 100)

The current contract applies the displayed five-percent-per-level factor after the material-derived base and cap.

Missing-consumer boundary

Displayed parameter + authoritative level ≠ enforced effect

This applies to several planned effects.

Production Play loads play/main.js

HTML play/index.html
    <script type="module" src="./main.js"></script>

Consumer claims start from the current production entry.

  1. Line 1

    Load the modular game entry for /play/.

PlayerProfile declares six defaults

Rust programs/nicechunk_player/src/state.rs
pub const DEFAULT_HEALTH: u16 = 100;
pub const DEFAULT_ENERGY: u16 = 100;
pub const DEFAULT_STAMINA: u16 = 100;
pub const DEFAULT_MINING_POWER: u16 = 1;
pub const DEFAULT_BUILD_POWER: u16 = 1;
pub const DEFAULT_DEFENSE: u16 = 0;

These constants describe Profile initialization, not skill levels.

  1. Constants

    Define health, energy, stamina, mining power, build power, and defense.

Profile accepts chain skill state

JavaScript play/play-profile-ui.js
    const skillState = buildProfileSkillState({
      chainXp: chain.playerSkillXp || chain.skillXp || null,
      chainLevels: chain.playerSkillLevels || chain.skillLevels || null,
      chainThresholds: chain.playerSkillThresholds || chain.skillThresholds || null,
    });

UI passes PlayerSkills XP, levels, and SkillRuleTable thresholds.

  1. Inputs

    Select chain values from Player snapshot.

  2. Boundary

    No local behavior profile participates.

Overview derives from authoritative levels

JavaScript play/play-profile-ui.js
  function renderOverview(profile, chain, skillState) {
    const [px, , pz] = getPlayerPosition?.() ?? [0, 0, 0];
    const chainBackpack = chain.chainBackpack || {};
    const levels = effectiveSkillLevels(skillState);
    const totalSkillLevels = Object.values(levels).reduce((sum, value) => sum + value, 0);
    const level = Math.max(1, 1 + Math.floor(totalSkillLevels / Math.max(1, PLAYER_SKILL_DEFINITIONS.length)));
    const reputation = Math.max(0,
      Number(profile.confirmedMines || 0) * 5
      + Number(profile.confirmedPlacements || 0) * 3
      + Number(profile.materialsSmelted || 0) * 4,
    );
    const chunkX = Math.floor(px / 16);
    const chunkZ = Math.floor(pz / 16);
    const regionX = Math.floor(chunkX / 100);
    const regionZ = Math.floor(chunkZ / 100);
    const backpackValue = chainBackpack.backpackAddress
      ? `${chainBackpack.syncedSlots || 0} / ${chainBackpack.capacity || 0}`
      : ui("main.profile.noBackpack", "No backpack");
    const balance = chain.walletAddress && Number.isFinite(Number(chain.walletBalanceSol))
      ? `${formatSol(chain.walletBalanceSol)} SOL`
      : ui("main.profile.balanceUnavailable", "0 SOL");

    setText(elements.profileIdentityBalance, balance);
    setText(elements.profileLevelValue, String(level));
    setText(elements.profileReputationValue, reputation.toLocaleString());
    setText(elements.profileRegionValue, `NiceChunk / ${regionX}, ${regionZ}`);
    setText(elements.profileBackpackValue, backpackValue);
    renderStats(skillState);
  }

Overview level is a presentation formula over PlayerSkills levels.

  1. Level

    Average ten chain levels for visible Level.

  2. Identity

    Render wallet, region, and Backpack separately.

Runtime effects require explicit chain levels

JavaScript play/play-skill-effects.js
export function createProfileSkillEffects({
  chainXp = null,
  chainLevels = null,
} = {}) {
  const state = buildProfileSkillState({ chainXp, chainLevels });
  const level = (skillId) => {
    const skill = SKILL_BY_ID[skillId];
    return skill ? profileSkillStateLevel(state, skill) : 0;
  };
  const levels = Object.fromEntries(PLAYER_SKILL_DEFINITIONS.map((skill) => [skill.id, level(skill.id)]));
  const effects = Object.fromEntries(PLAYER_SKILL_DEFINITIONS.map((skill) => [
    skill.effect.key,
    profileSkillEffectValue(skill, levels[skill.id]),
  ]));
  return {
    levels,
    ...effects,
  };
}

Runtime does not infer a level from local XP or behavior.

  1. State

    Build normalized state from supplied chain values.

  2. Effects

    Evaluate each definition at authoritative level.

Chunk validates Precision and Exploration

Rust programs/nicechunk_chunk/src/lib.rs
    let precision_level = player_skill_level(
        player_skills,
        global_config.key,
        &context.owner,
        PRECISION_GATHERING_SKILL_INDEX,
    )?;
    let exploration_level = player_skill_level(
        player_skills,
        global_config.key,
        &context.owner,
        EXPLORATION_SKILL_INDEX,
    )?;
    Ok((
        PlayerProgressState::precision_gathering_volume_mm3_from_level(precision_level),
        exploration_level,
    ))
}

fn player_skill_level(
    player_skills: &AccountInfo,
    global_config: &Pubkey,
    owner: &Pubkey,
    skill_index: usize,
) -> Result<u8, solana_program::program_error::ProgramError> {
    let (expected, _) = Pubkey::find_program_address(
        &[PLAYER_SKILLS_SEED, global_config.as_ref(), owner.as_ref()],
        &NICECHUNK_SKILLS_PROGRAM_ID,
    );
    require_key_eq(
        player_skills.key,
        &expected,
        NicechunkChunkError::InvalidPlayerSkillsPda,
    )?;
    if player_skills.owner == &system_program::ID && player_skills.data_len() == 0 {
        return Ok(0);
    }
    require_key_eq(
        player_skills.owner,
        &NICECHUNK_SKILLS_PROGRAM_ID,
        NicechunkChunkError::InvalidPlayerSkillsOwner,
    )?;
    let data = player_skills.try_borrow_data()?;
    if data.len() != PLAYER_SKILLS_LEN
        || data[0..8] != PLAYER_SKILLS_MAGIC
        || u16::from_le_bytes([data[8], data[9]]) != PLAYER_SKILLS_VERSION
        || data[11] != 1
        || &data[PLAYER_SKILLS_OWNER_OFFSET..PLAYER_SKILLS_OWNER_OFFSET + 32] != owner.as_ref()
        || &data[PLAYER_SKILLS_GLOBAL_CONFIG_OFFSET..PLAYER_SKILLS_GLOBAL_CONFIG_OFFSET + 32]
            != global_config.as_ref()
    {
        return Err(NicechunkChunkError::InvalidPlayerSkillsData.into());
    }
    let level = data
        .get(PLAYER_SKILLS_LEVELS_OFFSET + skill_index)
        .copied()
        .ok_or(NicechunkChunkError::InvalidPlayerSkillsData)?;
    if level > MAX_SKILL_LEVEL {
        return Err(NicechunkChunkError::InvalidPlayerSkillsData.into());
    }
    Ok(level)
}

Chunk derives and validates PlayerSkills before mining effects.

  1. Load

    Read two skill indexes.

  2. Identity

    Check PDA, owner, layout, wallet, GlobalConfig, and range.

Browser mining creates only a preview

JavaScript play/mining-controller.js
    const resourceId = Number.isFinite(hit.resourceId) ? hit.resourceId : def.resourceId;
    const txId = `local-pending-${txSerial++}`;
    const skillEffects = getSkillEffects?.() ?? {};
    const yieldBps = Math.max(1, Math.min(10000, Math.trunc(skillEffects.precisionGatheringBps || 5000)));
    const volumeMilliLiters = Math.max(1, Math.floor(1000 * yieldBps / 10000));
    const planBlocks = currentPlanBlocks(swing, hit);
    const plannedRewardBlocks = swing.miningPlan?.rewardBlocks?.length ? swing.miningPlan.rewardBlocks : planBlocks;
    const rewardGroups = rewardGroupsForBlocks(uniqueMiningBlocks([hit, ...plannedRewardBlocks]), {
      yieldBps,
      volumeMilliLiters,
    });

Pending reward groups use Precision for responsiveness.

  1. Effect

    Read Precision basis points.

  2. Preview

    Build local volume and rewards that remain pending.

Smelting consumes authoritative level

Rust programs/nicechunk_smelting/src/lib.rs
    let smelting_level = player_skill_level(player_skills, global_config.key, owner.key)?;
    let skill_output_bps = PlayerProgressState::smelting_output_bps_from_level(smelting_level);

    let validated_inputs = {
        let backpack_data = backpack.try_borrow_data()?;
        BackpackAccountView::validate_recipe_inputs(
            &backpack_data,
            owner.key,
            indexes,
            fuel_indexes,
            &recipe,
            multiplier,
        )?
    };

    let recipe_input_volume_mm3 = recipe_input_volume_mm3(&recipe);
    let mut output_volumes_mm3 = [0_u32; state::RECIPE_MAX_OUTPUTS];
    let mut output_quantities = [0_u32; state::RECIPE_MAX_OUTPUTS];
    for output_index in 0..recipe.output_count as usize {
        let output = &recipe.outputs[output_index];
        output_volumes_mm3[output_index] = smelting_recipe_output_volume_mm3(
            &recipe,
            output,
            validated_inputs.input_volume_mm3,
            recipe_input_volume_mm3,
            multiplier,
            skill_output_bps,
        )?;
        output_quantities[output_index] = smelting_output_quantity(
            &recipe,
            output,
            validated_inputs.consumed_input_units,
            multiplier,
            skill_output_bps,
        )?;
    }

The program reads the authoritative PlayerSkills level and validates every resulting output volume and quantity before it consumes any Backpack input.

  1. Level

    Decode Smelting level.

  2. Output

    Apply the level-derived basis points while precomputing checked output volume and quantity arrays.

Smelting is zero to fifty percent extra

Rust programs/nicechunk_smelting/src/state.rs
    pub fn smelting_output_bps_from_level(level: u8) -> u16 {
        let level = u16::from(level.min(10));
        SMELTING_SKILL_BASE_OUTPUT_BPS
            .saturating_add(level.saturating_mul(SMELTING_SKILL_OUTPUT_BPS_PER_LEVEL))
            .min(SMELTING_SKILL_MAX_OUTPUT_BPS)
    }

Contract formula matches current browser Smelting metric.

  1. Clamp

    Limit level to ten.

  2. Basis points

    Add 500 per level and cap at 15,000.

Forging builds a material base, then applies the level bonus

Rust programs/nicechunk_backpack/src/state.rs
    let material_level =
        material_item_level_from_durability(total_effective_durability, total_volume);
    let item_level = material_level.max(1);
    let item_level_grade = 1_u8.saturating_add((item_level.saturating_sub(1) / 10).min(9));
    let blended_grade = ((material_grade as u16 * 3 + item_level_grade as u16 + 2) / 4) as u8;
    let grade = blended_grade
        .max(1)
        .min(10)
        .min(material_grade.saturating_add(1).min(10))
        .min(weak_grade_cap);

    let material_factor = 90_u64.saturating_add(grade as u64 * 5);
    let level_factor = 100_u64.saturating_add(item_level as u64 / 2);
    let base_candidate = total_effective_durability
        .saturating_mul(material_factor)
        .saturating_mul(level_factor)
        / 10_000;
    let base_material_cap = total_raw_durability.saturating_mul(105) / 100;
    let base_durability = base_candidate.max(1).min(base_material_cap.max(1));
    let durability_bonus_percent = 100_u64.saturating_add(forging_level.min(10) as u64 * 5);
    let durability_max = base_durability
        .saturating_mul(durability_bonus_percent)
        .saturating_div(100)
        .max(1)
        .min(u32::MAX as u64) as u32;

    ForgeOutcome {
        grade,
        item_level,
        durability_max,
        quality_bps,
        volume_mm3: total_volume.max(1).min(u32::MAX as u64) as u32,
        mass_grams: 0,
        gained_xp: 1,
    }

Backpack derives grade and base durability from materials, applies exactly five percent per verified Forging level up to level ten, and awards one XP for every completed forge.

  1. Grade

    Blend material and item-level grade, then apply material-derived caps.

  2. Durability

    Cap the material-derived base, then apply the five-percent-per-level multiplier.

  3. XP

    Award one XP for the completed forging action.

Swiftness changes controls and keeps two-times sprint

JavaScript play/play-movement-speed.js
export const PLAYER_MOVEMENT_CONFIG = Object.freeze({
  baseSpeed: 14.8,
  sprintMultiplier: 2,
});

export function playerMovementSpeeds(movementSpeedMultiplier = 1) {
  const parsed = Number(movementSpeedMultiplier);
  const skillMultiplier = Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
  const walking = PLAYER_MOVEMENT_CONFIG.baseSpeed * skillMultiplier;
  return {
    walking,
    running: walking * PLAYER_MOVEMENT_CONFIG.sprintMultiplier,
  };
}

export function applyPlayerMovementSpeeds(controls, skillEffects = {}) {
  const speeds = playerMovementSpeeds(skillEffects.movementSpeedMultiplier);
  if (controls) {
    controls.speed = speeds.walking;
    controls.sprintMultiplier = PLAYER_MOVEMENT_CONFIG.sprintMultiplier;
  }
  return speeds;
}

The chain-backed level reaches controls.speed and sprintMultiplier.

  1. Config

    Set base 14.8 and sprint 2.

  2. Calculation

    Apply skill to walking and derive running.

  3. Consumer

    Write values to controls.

Guardian MOVE contains pose, not skills

JavaScript play/play-guardian-client.js
  encodeMove({ x, y, z, yaw, pitch }) {
    const chunk = this.worldToChunk(x, z);
    const local = this.globalToLocalChunk(chunk.x, chunk.z);
    const offsetX = x - chunk.x * this.chunkSize;
    const offsetZ = z - chunk.z * this.chunkSize;
    const bytes = new Uint8Array(MOVE_SIZE);
    const view = new DataView(bytes.buffer);
    view.setUint8(0, MSG_MOVE);
    view.setUint8(1, clampByte(local.x));
    view.setUint8(2, clampByte(local.z));
    view.setUint16(3, clampU16(Math.round(offsetX * this.positionPrecision)), true);
    view.setUint16(5, clampU16(Math.round(y * this.positionPrecision)), true);
    view.setUint16(7, clampU16(Math.round(offsetZ * this.positionPrecision)), true);
    view.setUint8(9, encodeYaw(yaw));
    view.setInt8(10, encodePitch(pitch));
    view.setUint16(11, ++this.clientTick & 0xffff, true);
    return bytes;
  }

Realtime movement payload has pose fields but no PlayerSkills or stat table.

  1. Position

    Encode chunk-local x, y, and z.

  2. Orientation

    Encode yaw and pitch.

  3. Ordering

    Include a client tick, not skill state.

PlayerSkills presentation has no XP merge

JavaScript play/play-profile-skills.js
export function buildProfileSkillState({
  chainXp = null,
  chainLevels = null,
  chainThresholds = null,
} = {}) {
  const levels = normalizeSkillLevels(chainLevels);
  const xpBySkill = normalizeSkillXp(chainXp);
  const thresholdsBySkill = normalizeSkillThresholds(chainThresholds);
  return {
    levels,
    xpBySkill,
    thresholdsBySkill,
    resolvedLevels: { ...levels },
    source: "chain",
  };
}

Final state normalizes only chain-supplied values.

  1. Inputs

    Accept chain XP, levels, and thresholds.

  2. Result

    Return one chain-sourced state.

IMPLEMENTATION EVIDENCE

Where these claims come from

Each claim is intentionally scoped to a concrete implementation path. These references are for verification, not decoration.

play/index.html

Loads current modular production game.

programs/nicechunk_player/src/state.rs

Defines six PlayerProfile defaults and version-7 layout.

play/play-profile-ui.js

Builds overview and cards from PlayerSkills.

play/play-profile-skills.js

Defines presentation parameters and chain-only state.

play/play-skill-effects.js

Creates runtime effects from explicit levels.

play/play-movement-speed.js

Applies Swiftness and two-times running.

play/mining-controller.js

Uses Precision for reversible preview.

src/chain/nicechunkChain.js

Validates PlayerSkills and SkillRuleTable.

programs/nicechunk_chunk/src/lib.rs

Consumes Precision and Exploration.

programs/nicechunk_chunk/src/state.rs

Defines gathered volume and Exploration logic.

programs/nicechunk_smelting/src/lib.rs

Consumes the authoritative Smelting level during settlement.

programs/nicechunk_smelting/src/state.rs

Defines the enforced Smelting output multiplier.

programs/nicechunk_backpack/src/lib.rs

Validates and reads Forging level.

programs/nicechunk_backpack/src/state.rs

Defines the multi-factor verified Forging outcome.

programs/nicechunk_skills/src/state.rs

Stores ten authoritative XP totals and levels.

play/play-guardian-client.js

Defines Guardian pose payload without skill state.

Guardian/src/protocol.h

Defines Guardian protocol independently from Skills.

play/tests/play-skill-movement.test.mjs

Checks authoritative movement and two-times running.

play/tests/smelting-skill-output.test.mjs

Checks Smelting preview levels.

tests/nicechunk_skills.ts

Checks current Skills account and instruction structure.