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"
- Exact match: e.g.
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 objectmainHandItem()
— Gets the ID of the item in the main handvehicleType()
— Gets the current vehicle typetargetedEntity()
— Gets the type of the targeted entity
Player State Checks
isInVehicle()
— Checks if the player is in a vehicleisSwimming()
— Checks if the player is swimmingisFlying()
— Checks if the player is flyingisCreativeMode()
— Checks if the player is in Creative modehasItem(itemId)
— Checks if the player has the specified item
Keybind Related
getKeyByDesc(desc)
— Gets the key name by its descriptionisKeyPressedOfDesc(key)
— Checks if the specified key is pressed
Entity and Environment Checks
isTargetedEntityType(entityType)
— Checks if the targeted entity is of the specified typehasTargetEntity()
— Checks if there is a targeted entityisCameraPlayer()
— Checks if the camera entity is the playerisScreenOpen()
— Checks if a game screen is currently open
Item NBT Related
hasMainHandNBT(nbtPath)
— Checks if the main hand item contains the specified NBT pathcheckMainHandNBT(nbtPath, expectedValue)
— Checks if the NBT path of the main hand item matches the expected valuegetMainHandNBTValue(nbtPath)
— Gets the value at the NBT path of the main hand itemmatchMainHandSNBT(snbt)
— Checks if the main hand item's NBT matches the given SNBT stringgetMainHandSNBT()
— Gets the full SNBT of the main hand item
Target Entity NBT Related
hasTargetEntityNBT(nbtPath)
— Checks if the targeted entity contains the specified NBT pathcheckTargetEntityNBT(nbtPath, expectedValue)
— Checks if the NBT path of the targeted entity matches the expected valuegetTargetEntityNBTValue(nbtPath)
— Gets the value at the NBT path of the targeted entitygetTargetEntitySNBT()
— Gets the full SNBT of the targeted entity
Target Block Entity NBT Related
hasTargetBlockEntityNBT(nbtPath)
— Checks if the targeted block entity contains the specified NBT pathcheckTargetBlockEntityNBT(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 loadedgetCurrentTime()
— Gets the current timestamp
Display Actions
show(modid, desc)
— Displays a keybind promptcustom(modid, key, desc)
— Displays a custom keybind promptalias(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
Expression | Description |
---|---|
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!