From 88783da09d137fee1c2b66042840d69296cb9ea4 Mon Sep 17 00:00:00 2001 From: Brendan Callanan <30944704+bennycallanan@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:18:38 +0100 Subject: [PATCH 1/2] feat: add persistent metadata map to claims --- .../huskclaims/api/HuskClaimsAPI.java | 29 +++++++++++++++++++ .../william278/huskclaims/claim/Claim.java | 26 +++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/common/src/main/java/net/william278/huskclaims/api/HuskClaimsAPI.java b/common/src/main/java/net/william278/huskclaims/api/HuskClaimsAPI.java index e89d7d6b..cc822036 100644 --- a/common/src/main/java/net/william278/huskclaims/api/HuskClaimsAPI.java +++ b/common/src/main/java/net/william278/huskclaims/api/HuskClaimsAPI.java @@ -946,6 +946,35 @@ public Optional getTrustLevel(@NotNull Claim claim, @NotNull ClaimWo return claim.getTrustLevel(trustable, plugin); } + /** + * Get a value from a {@link Claim}'s metadata store. + * + * @param claim the claim + * @param key the metadata key + * @return the value, if present + * @since 1.5 + */ + public Optional getClaimMetadata(@NotNull Claim claim, @NotNull String key) { + return Optional.ofNullable(claim.getMetadata().get(key)); + } + + /** + * Set a value in a {@link Claim}'s metadata store, persisting the change asynchronously. + * + * @param claim the claim + * @param claimWorld the claim world that the claim is in + * @param key the metadata key + * @param value the value to set + * @since 1.5 + */ + public void setClaimMetadata(@NotNull Claim claim, @NotNull ClaimWorld claimWorld, + @NotNull String key, @NotNull String value) { + plugin.runAsync(() -> { + claim.getMetadata().put(key, value); + plugin.getDatabase().updateClaimWorld(claimWorld); + }); + } + /** * Get the effective trust level of a {@link Trustable} in a {@link Claim} at a {@link Position}. *

diff --git a/common/src/main/java/net/william278/huskclaims/claim/Claim.java b/common/src/main/java/net/william278/huskclaims/claim/Claim.java index a0fd8745..c74a0bfc 100644 --- a/common/src/main/java/net/william278/huskclaims/claim/Claim.java +++ b/common/src/main/java/net/william278/huskclaims/claim/Claim.java @@ -159,6 +159,13 @@ public class Claim implements Highlightable { @SerializedName("creation_time") private String creationTime; + /** + * Generic key-value metadata store for third-party plugins to attach persistent data to a claim. + */ + @Expose + @SerializedName("metadata") + private Map metadata; + protected Claim(@Nullable UUID owner, @NotNull Region region, @NotNull ConcurrentMap users, @NotNull ConcurrentMap groups, @NotNull ConcurrentMap tags, @NotNull ConcurrentMap bannedUsers, @NotNull Set children, boolean inheritParent, @@ -174,6 +181,7 @@ protected Claim(@Nullable UUID owner, @NotNull Region region, @NotNull Concurren this.inheritParent = inheritParent; this.creationTime = OffsetDateTime.now().toString(); this.privateClaim = privateClaim; + this.metadata = Maps.newConcurrentMap(); children.forEach(child -> child.setParent(this)); } @@ -564,6 +572,24 @@ public Optional getCreationTime() { return Optional.ofNullable(creationTime).map(OffsetDateTime::parse); } + /** + * Get the mutable generic metadata store for this claim. + *

+ * Third-party plugins may use this to attach persistent key-value data that travels with the claim through + * resizes, transfers, deletions and cross-server synchronisation. Mutating the returned map and then persisting + * the {@link ClaimWorld} (e.g. via the API) will save the changes. + * + * @return the mutable metadata map + * @since 1.5 + */ + @NotNull + public Map getMetadata() { + if (metadata == null) { + metadata = Maps.newConcurrentMap(); + } + return metadata; + } + @NotNull public Optional getChildClaimAt(@NotNull BlockPosition position) { return getChildren().stream().filter(claim -> claim.getRegion().contains(position)).findFirst(); From b0f6b3d7b339fc6de043f3f3fdef071040e5ccc6 Mon Sep 17 00:00:00 2001 From: Brendan Callanan <30944704+bennycallanan@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:44:45 +0100 Subject: [PATCH 2/2] docs: added missing section for metadata --- docs/Claims-API.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/Claims-API.md b/docs/Claims-API.md index 2019fbd5..d9e41f81 100644 --- a/docs/Claims-API.md +++ b/docs/Claims-API.md @@ -1,4 +1,4 @@ -HuskClaims provides API for getting, creating resizing, & deleting [[claims]], child claims, and admin claims, and managing [[claim blocks]]. +HuskClaims provides API for getting, creating resizing, & deleting [[claims]], child claims, and admin claims, managing [[claim blocks]], and attaching persistent metadata to claims. This page assumes you have read the general [[API]] introduction and that you have both imported HuskClaims into your project and added it as a dependency. @@ -9,6 +9,7 @@ This page assumes you have read the general [[API]] introduction and that you ha * [3. Editing claims](#3-editing-claims) * [3.1 Resizing & deleting claims](#31-resizing--deleting-claims) * [4. Checking & updating a user's claim blocks](#4-checking--updating-a-users-claim-blocks) +* [5. Reading & writing claim metadata](#5-reading--writing-claim-metadata) ## 1. Getting if a location is claimed * On the Bukkit platform, get a `Position` object using `#getPosition(org.bukkit.Location location)` @@ -175,4 +176,31 @@ void giveClaimBlocks(org.bukkit.Player player, long amount) { } } ``` + + +## 5. Reading & writing claim metadata +* Your plugin can attach persistent `String` key-value data to a `Claim` through its metadata store, letting you save custom claim data without your own storage +* Read a value using `#getClaimMetadata(Claim claim, String key)`, which returns an `Optional` +* Write a value using `#setClaimMetadata(Claim claim, ClaimWorld claimWorld, String key, String value)`, which persists the change asynchronously +* Note that metadata is **not** inherited: a child claim does not contain its parent claim's metadata (and vice versa). If you need a parent's data, read it from the parent `Claim` directly using `#getParent()` + +

+Example — Reading & writing claim metadata + +```java +void manageClaimHome(org.bukkit.Location location) { + Position position = huskClaims.getPosition(location); + Optional claimWorld = huskClaims.getClaimWorld(position.getWorld()); + Optional claim = huskClaims.getClaimAt(position); + if (claimWorld.isPresent() && claim.isPresent()) { + Optional home = huskClaims.getClaimMetadata(claim.get(), "myplugin:home"); + if (home.isPresent()) { + System.out.println("This claim's home is at " + home.get()); + } else { + huskClaims.setClaimMetadata(claim.get(), claimWorld.get(), "myplugin:home", position.toString()); + System.out.println("Set this claim's home!"); + } + } +} +```
\ No newline at end of file