← Назад к ресурсам

Custom Item Data

A useful mod for Minecraft servers or framework for mod developers to store easily data on item.

Modrinth library storage utility

Описание

# Custom Item Data ![Version](https://img.shields.io/badge/Minecraft-1.20.6-green) ![Loader](https://img.shields.io/badge/Loader-Fabric-blue) ![Status](https://img.shields.io/badge/Status-In_Development-orange) A **Fabric library mod** for Minecraft **1.20.6** that lets you store persistent, typed, schema-validated data on any `ItemStack`. ### Two ways to use it: - As a **mod developer**, call the `ItemDataAPI` from your own mod to read/write values on items. - As a **server admin**, use the `/itemdata` commands in-game to define and edit item data without writing code. --- ## Features - Store typed data (`int`, `float`, `string`, `boolean` + your own types for framework) on any `ItemStack`. - Schema-based validation — the framework rejects values that don't match the registered type. - Automatic persistence (survives save/load, gives/clones, etc.). - Stacks with different data **don't stack together**. - In-game admin commands (`/itemdata […]`) persisted in the world save. - Pluggable custom types via a simple `CustomDataType` interface. --- ## Installation (end users) 1. Install **Fabric Loader** for Minecraft 1.20.6: https://fabricmc.net/use/installer/ 2. Download **Fabric API** for 1.20.6 and drop the `.jar` into your `mods/` folder. 3. Drop `customitemdata-1.0.0.jar` into the same `mods/` folder. 4. Launch the game (or server). You should see `[ItemDataFramework] Initialisé avec succès.` in the log. That's it — the mod is now active. By itself it does nothing visible; it becomes useful when another mod calls its API, or when an OP uses the `/itemdata` commands. --- ## Admin commands tutorial All commands require OP level 2. They operate on the item **currently held in the main hand**. ```text /itemdata define /itemdata define [default] /itemdata remove /itemdata set /itemdata get /itemdata list /itemrename [name] ``` Example session — give a stick a "points" counter: ```text /itemdata define nb_point int /itemdata set nb_point 42 /itemdata get nb_point → 42 /itemdata list → nb_point (int) ``` Schemas defined this way are saved in the world save, so they persist across restarts. --- ## Framework tutorial (for mod developers) This section walks you through using ItemDataFramework from your own Fabric mod — from zero to a working example in four steps. ### Step 1 — Add the dependency Until the mod is published to a Maven repository, the simplest approach is to drop its `.jar` into your project and reference it locally. **Option A — local jar** (fastest): 1. Build this mod: `./gradlew build` — the jar lands in `build/libs/customitemdata-1.0.0.jar`. 2. Copy that jar into a `libs/` folder in your own mod project. 3. In your `build.gradle`: ```gradle dependencies { modImplementation files("libs/customitemdata-1.0.0.jar") // or: modImplementation fileTree(dir: "libs", include: ["*.jar"]) } ``` 4. In your `fabric.mod.json`, declare it as a dependency so it loads first: ```json "depends": { "fabricloader": ">=0.18.4", "minecraft": "~1.20.6", "fabric-api": "*", "customitemdata": "*" } ``` ### Step 2 — Register a schema A **schema** tells the framework which parameters an item type is allowed to carry, and of what type. Register your schemas once, in your mod's `onInitialize()`, *after* Minecraft's registries are ready. ```java package com.example.mymod; import fr.hdi.customitemdata.schema.SchemaEntry; import fr.hdi.customitemdata.schema.SchemaManager; import net.fabricmc.api.ModInitializer; public class MyMod implements ModInitializer { @Override public void onInitialize() { SchemaManager schemas = SchemaManager.getInstance(); // A simple integer counter on sticks, visible in the tooltip, // defaulting to "0" and required (not nullable). schemas.registerSchema("minecraft:stick", new SchemaEntry.Builder("nb_point", "int") .visible(true) .nullable(false) .defaultValue("0") .build()); // A string "owner" field on diamond swords. schemas.registerSchema("minecraft:diamond_sword", new SchemaEntry.Builder("owner", "string") .visible(true) .build()); } } ``` Available primitive types: `int`, `float`, `string`, `boolean`. > Schemas registered via the API are re-applied every time the server starts — they are **not** persisted to the world. Only schemas created via `/itemdata define` are persisted. ### Step 3 — Read and write with `ItemDataAPI` Once a schema exists, use `ItemDataAPI` anywhere you have an `ItemStack`. The API validates against the schema automatically; if validation fails, `set*` returns `false` and the stack is left unchanged. ```java import fr.hdi.customitemdata.api.ItemDataAPI; import net.minecraft.item.ItemStack; ItemStack stack = player.getMainHandStack(); ItemDataAPI.setCustomName(stack, "My Item"); // Write ItemDataAPI.setInt(stack, "nb_point", 42); ItemDataAPI.setString(stack, "owner", "Steve"); // Read (with default fallback) int points = ItemDataAPI.getInt(stack, "nb_point", 0); String owner = ItemDataAPI.getString(stack, "owner", "unknown"); // Read as Optional (absent if the key isn't present) ItemDataAPI.getInt(stack, "nb_point").ifPresent(value -> ...); // Check / delete boolean has = ItemDataAPI.has(stack, "nb_point"); ItemDataAPI.remove(stack, "nb_point"); ``` Under the hood, every value is serialized as a `String` inside a single `ItemDataComponent` attached to the stack. Two stacks with different data **will not merge**, because that's how vanilla Data Components work. ### Step 4 — Create a custom data type When `int` / `float` / `string` / `boolean` aren't enough, implement `CustomDataType` to plug in your own type. You need to provide: - a unique string ID (`namespace:name`), - an `encode` that turns your object into a string, - a `decode` that parses it back, - a `Codec` (used for Minecraft data integration, e.g. datapacks). ```java package com.example.mymod; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import fr.hdi.customitemdata.api.CustomDataType; public final class PointDataType implements CustomDataType { public static final String TYPE_ID = "mymod:point"; public static final PointDataType INSTANCE = new PointDataType(); private PointDataType() {} @Override public String getId() { return TYPE_ID; } @Override public Codec getCodec() { return Point.CODEC; } @Override public String encode(Point value) { return value.x() + "," + value.y(); } @Override public Point decode(String stored) { String[] parts = stored.split(",", 2); return new Point(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); } // Nicer tooltip display: "(3, 7)" instead of "Point[x=3, y=7]" @Override public String display(String stored) { Point p = decode(stored); return "(" + p.x() + ", " + p.y() + ")"; } public record Point(int x, int y) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( Codec.INT.fieldOf("x").forGetter(Point::x), Codec.INT.fieldOf("y").forGetter(Point::y) ).apply(i, Point::new)); } } ``` Register it **before** any schema that uses it: ```java SchemaManager schemas = SchemaManager.getInstance(); schemas.registerCustomType(PointDataType.INSTANCE); schemas.registerSchema("minecraft:compass", new SchemaEntry.Builder("target", PointDataType.TYPE_ID).build()); ``` Then read/write through the generic `get`/`set` overloads: ```java ItemDataAPI.set(stack, "target", new PointDataType.Point(3, 7), PointDataType.INSTANCE); Optional target = ItemDataAPI.get(stack, "target", PointDataType.INSTANCE); ``` ### Full working example A minimal mod that defines two parameters on a stick, sets them on the first tick, and reads them back: ```java package com.example.mymod; import fr.hdi.customitemdata.api.ItemDataAPI; import fr.hdi.customitemdata.schema.SchemaEntry; import fr.hdi.customitemdata.schema.SchemaManager; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.ActionResult; import net.minecraft.util.TypedActionResult; public class MyMod implements ModInitializer { @Override public void onInitialize() { SchemaManager schemas = SchemaManager.getInstance(); schemas.registerSchema("minecraft:stick", new SchemaEntry.Builder("nb_point", "int") .nullable(false).defaultValue("0").build()); schemas.registerSchema("minecraft:stick", new SchemaEntry.Builder("owner", "string").build()); // Right-click a stick to increment its counter. UseItemCallback.EVENT.register((player, world, hand) -> { ItemStack stack = player.getStackInHand(hand); if (stack.getItem() != Items.STICK) { return TypedActionResult.pass(stack); } int current = ItemDataAPI.getInt(stack, "nb_point", 0); ItemDataAPI.setInt(stack, "nb_point", current + 1); ItemDataAPI.setString(stack, "owner", player.getName().getString()); player.sendMessage( net.minecraft.text.Text.literal("Points: " + (current + 1)), true); return TypedActionResult.success(stack); }); } } ``` --- ## Reference ### `ItemDataAPI` (static methods) | Method | Purpose | |------------------------------------------------------------|------------------------------------------| | `getRaw(stack, key)` / `setRaw(stack, key, value)` | Raw string access | | `getInt` / `setInt` | Integer values | | `getFloat` / `setFloat` | Float values | | `getString` / `setString` | String values | | `getBoolean` / `setBoolean` | Boolean values | | `get(stack, key, customType)` / `set(stack, key, v, t)` | Custom typed values | | `has(stack, key)` / `hasData(stack)` | Presence checks | | `remove(stack, key)` | Delete a key (removes component if empty)| | `getComponent(stack)` | Access the raw `ItemDataComponent` | ### `SchemaEntry.Builder` | Method | Default | Description | |----------------------|---------|------------------------------------------------| | `visible(boolean)` | `true` | Show in item tooltip | | `nullable(boolean)` | `true` | Allow the parameter to be absent | | `defaultValue(String)`| `null` | Default encoded value (e.g. `"0"`, `"false"`) | | `fromCommand(boolean)`| `false`| Mark as command-sourced (triggers persistence) | ### `SchemaManager` (singleton via `getInstance()`) | Method | Purpose | |--------------------------------------------------|--------------------------------------| | `registerSchema(itemId, entry)` | API-side schema registration | | `registerCustomType(customDataType)` | Register a `CustomDataType` | | `getEntry(itemId, paramName)` | Look up a single entry | | `getEntries(itemId)` | Look up all entries for an item | | `hasSchema(itemId)` | Does the item have any schema? | | `isKnownType(typeId)` | Is the type ID primitive or custom? | | `validateValue(entry, value)` | Validate a string against an entry | ---