Skip to main content

Datapack Extension Guide

SmartKeyPrompts Data Pack Guide

This page is under construction...

Note: Currently, only the Forge version supports data-driven key prompts.

SmartKeyPrompts lets you define custom key prompt logic using data packs, so modpack creators and players without KubeJS expertise can still create dynamic key hints tailored to gameplay.

The datapack use the MVEL for logical judgment and action execution, providing powerful expression support.

The built-in functions of this document are only available for version 1.0.4.2 and above, please see built-in datapack or source code as an example


Introduction to MVEL Expression Language

MVEL (MVFLEX Expression Language) is a powerful expression language that supports rich syntax features:

Basic Syntax Features

  • Ternary Expression: condition ? value1 : value2
  • Logical Operators: && (and), || (or), ! (not)
  • Comparison Operators: ==, !=, >, <, >=, <=
  • String Operations: + (concatenation), .startsWith(), .contains(), etc.
  • Null Check: value != null, value == null
  • Function Calls: Supports calling registered static methods

Example Expressions

// Ternary expression
keyUse == 'key.keyboard.unknown' ? 'key.mouse.right' : keyUse

// Logical combination
isModLoaded('tacz') && mainHandItem() == 'tacz:modern_kinetic_gun'

// String matching
vehicleType() != null && vehicleType().startsWith('immersive_aircraft:')

// Complex condition
mainHandItem() != 'minecraft:sugar' && mainHandItem() != 'diligentstalker:stalker_master'

For more detailed syntax, please refer to:

Data Pack Structure

➤ File Location

Data files must be placed under the following path:

data/[namespace]/smartkeyprompts/key_prompts/[filename].json

Example:

data/smartkeyprompts/smartkeyprompts/key_prompts/tacz.json

JSON Format

{
"modid": "your_mod_id",
"vars": {
"myVariable": "MVEL Expression"
},
"entries": [
{
"when": {
"someCondition": "expectedValueOrExpression"
},
"then": [
"Action Expression"
]
}
]
}

Field Descriptions

modid

  • Type: string
  • Purpose: Identifies which mod this key prompt supports. Usually this is the mod ID you're adapting prompts for.

vars

  • Type: object
  • Purpose: Reusable variables, evaluated at runtime using MVEL expressions. Useful for simplifying logic and improving performance.

entries

  • Type: array
  • Purpose: Defines a list of conditional key prompts using "when → then" logic. Evaluated in order—first matching entry executes its actions.

when

  • Type: object
  • Purpose: Conditions that must all match simultaneously for the then block to run.
  • Supports:
    • Exact match: e.g. "mainHandItem": "minecraft:diamond_sword"
    • Wildcard match: e.g. "vehicleType": "immersive_aircraft:*"
    • Expression match: e.g. "isCreativeMode": "true"

then

  • Type: string[]
  • Purpose: A list of actions (function calls) to perform when when conditions are met.

Built-in Functions(You can submit issues for the author to add)

Basic Variable Functions

  • player() — Gets the current player object
  • mainHandItem() — Gets the ID of the item in the main hand
  • vehicleType() — Gets the current vehicle type
  • targetedEntity() — Gets the type of the targeted entity

Player State Checks

  • isInVehicle() — Checks if the player is in a vehicle
  • isSwimming() — Checks if the player is swimming
  • isFlying() — Checks if the player is flying
  • isCreativeMode() — Checks if the player is in Creative mode
  • hasItem(itemId) — Checks if the player has the specified item
  • getKeyByDesc(desc) — Gets the key name by its description
  • isKeyPressedOfDesc(key) — Checks if the specified key is pressed

Entity and Environment Checks

  • isTargetedEntityType(entityType) — Checks if the targeted entity is of the specified type
  • hasTargetEntity() — Checks if there is a targeted entity
  • isCameraPlayer() — Checks if the camera entity is the player
  • isScreenOpen() — Checks if a game screen is currently open
  • hasMainHandNBT(nbtPath) — Checks if the main hand item contains the specified NBT path
  • checkMainHandNBT(nbtPath, expectedValue) — Checks if the NBT path of the main hand item matches the expected value
  • getMainHandNBTValue(nbtPath) — Gets the value at the NBT path of the main hand item
  • matchMainHandSNBT(snbt) — Checks if the main hand item's NBT matches the given SNBT string
  • getMainHandSNBT() — Gets the full SNBT of the main hand item
  • hasTargetEntityNBT(nbtPath) — Checks if the targeted entity contains the specified NBT path
  • checkTargetEntityNBT(nbtPath, expectedValue) — Checks if the NBT path of the targeted entity matches the expected value
  • getTargetEntityNBTValue(nbtPath) — Gets the value at the NBT path of the targeted entity
  • getTargetEntitySNBT() — Gets the full SNBT of the targeted entity
  • hasTargetBlockEntityNBT(nbtPath) — Checks if the targeted block entity contains the specified NBT path
  • checkTargetBlockEntityNBT(nbtPath, expectedValue) — Checks if the NBT path of the targeted block entity matches the expected value

Mod and System

  • isModLoaded(modid) — Checks if the specified mod is loaded
  • getCurrentTime() — Gets the current timestamp

Display Actions

  • show(modid, desc) — Displays a keybind prompt
  • custom(modid, key, desc) — Displays a custom keybind prompt
  • alias(modid, key, desc) — Displays a keybind alias prompt

Sure, if you know how to develop mods, you can register it yourself. Please refer to the MVEL events(zh-cn).


Action Expressions

ExpressionDescription
show(modid, 'translation.key')Show a default translated key prompt
custom(modid, key, 'translation.key')Show a custom prompt for a specific key
custom(modid, key1 + '+' + key2, 'desc')Show a combined key prompt

Usage Examples

Example 1: Simple Mod Support

{
"modid": "tacz_skp",
"vars": {
"modLoaded": "isModLoaded('tacz')",
"hasTaczGun": "mainHandItem() == 'tacz:modern_kinetic_gun'",
"isNotInVehicle": "!isInVehicle()"
},
"entries": [
{
"when": {
"modLoaded": "true",
"hasTaczGun": "true",
"isNotInVehicle": "true"
},
"then": [
"show('tacz_skp', 'key.tacz.shoot.desc')",
"show('tacz_skp', 'key.tacz.zoom.desc')",
"show('tacz_skp', 'key.tacz.reload.desc')"
]
}
]
}

Example 2: Wildcard Matching

{
"modid": "immersive_aircraft_skp",
"vars": {
"modid": "immersive_aircraft",
"modLoaded": "isModLoaded('immersive_aircraft')",
"vehicleType": "vehicleType",
"keyInventory": "getKeyInventory()"
},
"entries": [
{
"when": {
"modLoaded": "true",
"vehicleType": "immersive_aircraft:*"
},
"then": [
"custom(modid, keyInventory, 'immersive_aircraft.slot.upgrade')",
"show(modid, 'key.immersive_aircraft.dismount')"
]
}
]
}

Example 3: Combined Keys + NBT Checks

{
"modid": "diligentstalker",
"vars": {
"modLoaded": "isModLoaded('diligentstalker')",
"keyUse": "getKeyByDesc('key.use')",
"keyShift": "getKeyByDesc('key.sneak')",
"mainHandItem": "mainHandItem()",
"targetedEntity": "targetedEntity()",
"isCameraPlayer": "isCameraPlayer()",
"isTargetedDrone": "isTargetedEntityType('diligentstalker:drone_stalker')",
"screenOpen": "isScreenOpen()",
"notHoldingSugarOrMaster": "mainHandItem() != 'minecraft:sugar' && mainHandItem() != 'diligentstalker:stalker_master'",
"hasTargetEntity": "hasTargetEntity()",
"stalkerMasterHasBind": "hasMainHandNBT('StalkerId')"
},
"entries": [
{
"when": {
"modLoaded": "true",
"mainHandItem": "diligentstalker:stalker_master",
"stalkerMasterHasBind": "true",
"screenOpen": "false",
"isCameraPlayer": "true"
},
"then": [
"custom('diligentstalker', keyUse, 'key.diligentstalker.remote_connect')"
]
},
{
"when": {
"modLoaded": "true",
"isTargetedDrone": "true",
"mainHandItem": "minecraft:sugar",
"screenOpen": "false"
},
"then": [
"custom('diligentstalker', keyShift + '+' + keyUse, 'key.diligentstalker.add_fuel')"
]
},
{
"when": {
"modLoaded": "true",
"isTargetedDrone": "true",
"screenOpen": "false",
"notHoldingSugarOrMaster": "true"
},
"then": [
"custom('diligentstalker', keyShift + '+' + keyUse, 'key.diligentstalker.container')"
]
}
]
}

Example 4: NBT

{
"modid": "minecraft_skp_nbt",
"vars": {
"isDiamondSword": "mainHandItem() == 'minecraft:diamond_sword'",
"hasSharpness": "hasMainHandNBT('Enchantments[{id:\"minecraft:sharpness\"}]')",
"sharpnessLevel5": "checkMainHandNBT('Enchantments[{id:\"minecraft:sharpness\"}].lvl', '5s')",
"displayName": "getMainHandNBTValue('display.Name')",
"isNamedExcalibur": "displayName != null && displayName.contains('Excalibur')",

"hasCustomModelData99": "checkMainHandNBT('CustomModelData', 99)",
"hasExcaliburName": "hasMainHandNBT('display.Name') && getMainHandNBTValue('display.Name').contains('Excalibur')",

"modLoaded": "isModLoaded('minecraft')"
},
"entries": [
{
"when": {
"modLoaded": "true",
"isDiamondSword": "true",
"hasSharpness": "true",
"sharpnessLevel5": "true",
"isNamedExcalibur": "true"
},
"then": [
"show('minecraft_skp_nbt', 'key.sneak')"
]
},
{
"when": {
"modLoaded": "true",
"isDiamondSword": "true",
"hasCustomModelData99": "true",
"hasExcaliburName": "true"
},
"then": [
"show('minecraft_skp_nbt', 'key.use')"
]
}
]
}

Best Practices

Naming Conventions

  • Use modid_skp format to indicate key prompt support (e.g. tacz_skp)
  • Use key.modid.action format for translation keys

Performance Tips

  • Prefer exact matches over expressions when possible
  • Extract complex logic into vars
  • Put common conditions earlier for faster matching

Maintainability

  • Split files by mod
  • Group entries by function
  • Keep variable names meaningful and concise

Robustness

  • Always check if a mod is loaded (isModLoaded)
  • Handle missing/nullable data carefully
  • Consider different gameplay states (e.g. in vehicle, screen open)

Need help? Open an issue, consult mod sources, or reach out to the community!