API Recipe Collection (Scenario-Based Code)
Belongs to: Developers · Related: API Reference · Events · Integration Hands-On
Turn common third-party needs into complete, ready-to-use code recipes. Entry point com.qinhuai.items.api.QinhItemsAPI, Java uses .INSTANCE.
All methods return
nullfor "non-QI item / nonexistent ID", so always null-check. Confirm QI is enabled before calling.
Recipe 1: Market / Auction House listing and purchase
Need: A player lists the item in hand, and the buyer receives it after purchase. Soulbound items must not be listed.
import com.qinhuai.items.api.QinhItemsAPI;
import org.bukkit.inventory.ItemStack;
// —— Listing: the player puts the item in hand onto the market ——
ItemStack hand = player.getInventory().getItemInMainHand();
// 1) Soulbound items cannot be traded
if (QinhItemsAPI.INSTANCE.isSoulbound(hand)) {
player.sendMessage("§cSoulbound items cannot be listed for trade");
return;
}
// 2) Record ownership (returns null for non-QI items; allow or reject per your logic)
String qiId = QinhItemsAPI.INSTANCE.getItemId(hand);
// 3) Serialize the entire ItemStack directly into storage (NBT fully preserved, player-customized instance data is not lost)
byte[] blob = hand.serializeAsBytes();
// …store into your market database: blob + price + seller…
// —— Purchase: restore the stored ItemStack and give it to the buyer ——
ItemStack listed = ItemStack.deserializeBytes(blob);
buyer.getInventory().addItem(listed);Storage principle: For player consignments, serialize the
ItemStackdirectly (preserving this single item's random values / enhancements / soulbound state); for admin fixed merchandise, store "item ID + amount" and freshly build from the latest template on delivery:javaItemStack reward = QinhItemsAPI.INSTANCE.assembly().build("legendary_sword", 1); if (reward != null) buyer.getInventory().addItem(reward);
Recipe 2: Mail system
Need: Send items to players, prevent mailing of soulbound items.
// Validate before sending
ItemStack toSend = ...;
if (QinhItemsAPI.INSTANCE.isSoulbound(toSend)) {
// Only allow mailing back to the bound owner themselves
java.util.UUID owner = QinhItemsAPI.INSTANCE.getSoulboundOwner(toSend);
if (owner == null || !owner.equals(targetPlayerId)) {
sender.sendMessage("§cSoulbound items can only be mailed back to the bound owner");
return;
}
}
// Store the attachment (serialized ItemStack), deserialize and deliver on receiptRecipe 3: Lottery / drop delivery of admin-configured QI items
Need: Read "QI item ID: weight" from config, deliver after winning.
import com.qinhuai.items.api.QinhItemsAPI;
public ItemStack rollReward(java.util.Map<String, Integer> pool, java.util.Random rng) {
int total = pool.values().stream().mapToInt(Integer::intValue).sum();
int r = rng.nextInt(total);
for (var e : pool.entrySet()) {
r -= e.getValue();
if (r < 0) {
ItemStack item = QinhItemsAPI.INSTANCE.assembly().build(e.getKey(), 1);
if (item == null) {
getLogger().warning("QI item in the prize pool does not exist: " + e.getKey());
return null;
}
return item;
}
}
return null;
}If you want to use QI's built-in random quality / affix generation, see the
RandomItemGeneratorin Random Generation.
Recipe 4: Enhancement / socketing system (write-layer patch)
Need: Add a "+N enhancement" state to gear, protected by the write domain and without breaking attributes.
import com.qinhuai.items.api.QinhItemsAPI
import com.qinhuai.items.layer.QinhLayerPatchPack
// Enhance +1: write a layer patch owned in the LAYER domain
val pack = QinhLayerPatchPack(
id = "forge",
owner = "forge", // Must be a layer-domain owner (forge/gem/enchant/strengthen…)
priority = 50,
variables = mapOf("forge_level" to "1"), // State key; cannot use semantic keys like attack_damage
)
val (updated, result) = QinhItemsAPI.assembly().applyLayerPatch(stack, pack)
when (result) {
com.qinhuai.items.layer.LayerWriteResult.OK -> player.inventory.setItemInMainHand(updated)
else -> player.sendMessage("§cEnhancement failed: $result")
}⚠️ Variable keys in a layer patch cannot use attribute semantic names (
attack_damageand the like are rejected by the semantic red line). To add attributes, go through a Provider. Attributes should be expressed by your enhancement system via an AP source or the item template. For write-domain rules see Layers and Assembly.
Read back a layer value:
val lvl = QinhItemsAPI.layers().int(stack, "forge", "forge_level") ?: 0Recipe 5: Temporary buffs (runtime variable overrides)
Need: Set a temporary UI / buff state on an item without touching the template.
val (updated, result) = QinhItemsAPI.variables().set(
stack, "ui_highlight", "true",
owner = "ui_glow", // runtime-domain owner (buff_/temp_/ui_ prefix, or qi_admin/qi_ui)
)
// Refresh the display
val shown = QinhItemsAPI.variables().refresh(updated ?: stack)Lock to prevent others from changing it:
QinhItemsAPI.variables().lock(stack, "ui_highlight", owner = "ui_glow")For write results see Variables.
Recipe 6: Identify / count QI items on a player
import com.qinhuai.items.api.QinhItemsAPI
import com.qinhuai.items.combat.EquipmentScanner
// Iterate the inventory to count QI items
val counts = HashMap<String, Int>()
for (it in player.inventory.contents) {
if (it == null) continue
val id = QinhItemsAPI.INSTANCE.getItemId(it) ?: continue
counts.merge(id, it.amount, Int::plus)
}
// QI items the player has equipped
val equipped = EquipmentScanner.equippedQinhStacks(player)
// Number of set pieces
val pieces = EquipmentScanner.countSetPieces(player, "warrior_bloodlust")Recipe 7: Listen for item use / custom restrictions
Need: Add a "class restriction" to QI items, forbidding use when not met.
import com.qinhuai.items.event.QinhItemUseCheckEvent;
import org.bukkit.event.EventHandler;
@EventHandler
public void onUseCheck(QinhItemUseCheckEvent e) {
for (String r : e.getRestrictions()) {
if (r.startsWith("class:")) {
String required = r.substring("class:".length());
if (!myClassSystem.hasClass(e.getPlayer(), required)) {
e.setCancelled(true); // Deny use
e.setDenyReason("Class mismatch: requires " + required);
return;
}
}
}
}Configure it on the item like this: options.restrictions: ["class:Warrior"]. For the full event list see Events.
Recipe 8: Register a custom action handler
Need: Let item actions invoke your plugin's logic.
import com.qinhuai.items.api.QinhItemsAPI
import com.qinhuai.items.api.QinhActionHandler
import com.qinhuai.items.api.QinhActionContext
import com.qinhuai.items.api.ActionDispatchResult
// Register in onEnable()
QinhItemsAPI.actions().registerHandler(object : QinhActionHandler {
override val handlerId = "myplugin:teleport_home"
override fun dispatch(ctx: QinhActionContext): ActionDispatchResult {
val home = myHomeService.getHome(ctx.player) ?: run {
ctx.player.sendMessage("§cNo home set")
return ActionDispatchResult.NOT_HANDLED
}
ctx.player.teleport(home)
return ActionDispatchResult.HANDLED
}
})
// Optional: register a payload schema so the GUI can edit it
QinhItemsAPI.actions().registerPayloadSchema("myplugin:teleport_home") {
string(key = "message", label = "Teleport prompt", default = "Headed home")
}Reference it in YAML: - handler: myplugin:teleport_home / payload: "...". See Action Handler Development for details.
Quick reference: which API should I use
| Need | API |
|---|---|
| Build an item | assembly().build(id, amount) |
| Is it a QI item / get ID | isQinhItem / getItemId |
| Get the item template definition | getDefinition(item) |
| Soulbind validation | isSoulbound / getSoulboundOwner |
| Can it be used | canUse(player, item) |
| Get / set / lock variables | variables().get/set/lock |
| Socket / enhance (layer) | assembly().applyLayerPatch |
| Read a layer value | layers().int/string(...) |
| Register a handler | actions().registerHandler |
| Get providers | getProviders(item) |
| Refresh equipment attributes | combat().refreshEquipmentAttributes |
For full signatures see API Reference.
Next steps
- API Reference: all method signatures
- Events: lifecycle hooks you can listen to
- Integration Hands-On: connecting external plugins