From 65ac7058866846f15ca212c809fc1f51e61e9456 Mon Sep 17 00:00:00 2001 From: Technofied <40795318+Technofied@users.noreply.github.com> Date: Mon, 22 Jun 2026 11:31:52 +0800 Subject: [PATCH 1/3] feat: chestshop integration --- build.gradle | 5 + .../vault/VaultStoragePlugin.java | 10 ++ .../vault/api/service/ChestShopService.java | 30 ++++ .../internal/security/VaultCapturePolicy.java | 129 ++++++++++++------ .../internal/service/ChestShopServiceImp.java | 66 +++++++++ .../service/NoOpChestShopService.java | 33 +++++ .../internal/service/VaultCaptureService.java | 45 +++++- src/main/resources/plugin.yml | 1 + 8 files changed, 280 insertions(+), 39 deletions(-) create mode 100644 src/main/java/net/democracycraft/vault/api/service/ChestShopService.java create mode 100644 src/main/java/net/democracycraft/vault/internal/service/ChestShopServiceImp.java create mode 100644 src/main/java/net/democracycraft/vault/internal/service/NoOpChestShopService.java diff --git a/build.gradle b/build.gradle index 4de0193..a5e89f7 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,10 @@ repositories { name = "opencollab" url = "https://repo.opencollab.dev/main/" } + maven { + name = "minebench" + url = "https://repo.minebench.de/" + } } dependencies { @@ -44,6 +48,7 @@ dependencies { compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.14") compileOnly("net.essentialsx:EssentialsX:2.21.2") compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT") + compileOnly("com.acrobot.chestshop:chestshop:3.12.2") } tasks { diff --git a/src/main/java/net/democracycraft/vault/VaultStoragePlugin.java b/src/main/java/net/democracycraft/vault/VaultStoragePlugin.java index 3af2372..8cb9c08 100644 --- a/src/main/java/net/democracycraft/vault/VaultStoragePlugin.java +++ b/src/main/java/net/democracycraft/vault/VaultStoragePlugin.java @@ -53,6 +53,7 @@ public final class VaultStoragePlugin extends JavaPlugin { private BoltService boltService; private MailService mailService; private Essentials essentials; + private ChestShopService chestShopService; private DemocracyLibApi democracyLibApi; private BedrockUniqueIdentifierRetriever bedrockUniqueIdentifierRetriever; @@ -130,6 +131,10 @@ public void onEnable() { this.essentials = ess; } + // ChestShop soft dependency: real impl when present, no-op otherwise. + var chestShop = getServer().getPluginManager().getPlugin("ChestShop"); + this.chestShopService = chestShop != null ? new ChestShopServiceImp() : new NoOpChestShopService(); + // Domain services this.captureService = new VaultCaptureService(); this.placementService = new VaultPlacementService(); @@ -209,4 +214,9 @@ public Essentials getEssentials() { return essentials; } + /** ChestShop integration; returns a no-op implementation when ChestShop is not installed. */ + public ChestShopService getChestShopService() { + return chestShopService; + } + } diff --git a/src/main/java/net/democracycraft/vault/api/service/ChestShopService.java b/src/main/java/net/democracycraft/vault/api/service/ChestShopService.java new file mode 100644 index 0000000..92b60a6 --- /dev/null +++ b/src/main/java/net/democracycraft/vault/api/service/ChestShopService.java @@ -0,0 +1,30 @@ +package net.democracycraft.vault.api.service; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Service for integrating with the optional ChestShop plugin. + * Resolves to a no-op implementation when ChestShop is not installed. + */ +public interface ChestShopService extends Service { + + /** @return true if ChestShop is installed and this integration is active. */ + boolean isAvailable(); + + /** @return true if the block is a valid ChestShop sign. */ + boolean isShopSign(@Nullable Block block); + + /** @return the shop sign blocks attached to the container, or an empty list. */ + @NotNull List findShopSigns(@Nullable Block containerBlock); + + /** + * Notifies ChestShop and removes the shop sign block. Must run on the main thread. + * @return true if the block was a shop sign and was removed + */ + boolean removeShopSign(@Nullable Player actor, @Nullable Block signBlock); +} diff --git a/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java b/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java index c721cd5..38c71cb 100644 --- a/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java +++ b/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java @@ -148,12 +148,73 @@ private static boolean computeHangableRestricted(Player actor, Block block, UUID private static Decision evaluateBlockPolicy(Player actor, Block block, UUID originalOwner, boolean hangableRestricted) { boolean hasOverride = VaultPermission.ACTION_PLACE_OVERRIDE.has(actor); boolean actorIsContainerOwner = originalOwner != null && originalOwner.equals(actor.getUniqueId()); + + RegionParticipation participation = computeRegionParticipation(actor, block, originalOwner); + boolean actorParticipantAny = participation.actorParticipantAny(); + boolean containerOwnerParticipantAny = participation.containerOwnerParticipantAny(); + boolean inAnyRegion = participation.inAnyRegion(); + List perRegionDebug = participation.perRegionDebug(); + + boolean disallowedOwnerSelf = actorParticipantAny && actorIsContainerOwner; + boolean baseAllowed; + + if (actorParticipantAny) { + // Actor is a participant of at least one relevant (non-parent) region + // Can vault if: block has owner, actor is not the owner, and owner is not participant of any relevant region + baseAllowed = (originalOwner != null) && !actorIsContainerOwner && !containerOwnerParticipantAny; + } else { + // Actor is not a participant of any relevant region + // Can only vault their own blocks + baseAllowed = actorIsContainerOwner; + } + + + boolean allowed = (originalOwner != null) + && !disallowedOwnerSelf + && !hangableRestricted + && ((inAnyRegion && baseAllowed) || hasOverride); + + Decision.Reason reason; + if (allowed) { + reason = Decision.Reason.ALLOWED; + } else if (hangableRestricted) { + reason = Decision.Reason.ENTITIES_REQUIRE_ADMIN; + } else if (!inAnyRegion) { + reason = Decision.Reason.NOT_IN_REGION; + } else if (disallowedOwnerSelf) { + reason = Decision.Reason.OWNER_SELF_IN_REGION; + } else if (actorParticipantAny && containerOwnerParticipantAny) { + reason = Decision.Reason.CONTAINER_OWNER_IN_OVERLAP; + } else if (originalOwner == null) { + reason = Decision.Reason.UNPROTECTED_NO_OVERRIDE; + } else { + reason = Decision.Reason.NOT_INVOLVED_NOT_OWNER; + } + + return new Decision( + allowed, + hasOverride, + actorParticipantAny, + containerOwnerParticipantAny, + actorIsContainerOwner, + disallowedOwnerSelf, + baseAllowed, + originalOwner, + reason, + perRegionDebug + ); + } + + private record RegionParticipation(boolean inAnyRegion, boolean actorParticipantAny, + boolean containerOwnerParticipantAny, List perRegionDebug) {} + + /** Scans WorldGuard regions at the block and computes actor/owner participation flags. */ + private static RegionParticipation computeRegionParticipation(Player actor, Block block, UUID originalOwner) { WorldGuardService wgs = VaultStoragePlugin.getInstance().getWorldGuardService(); boolean actorParticipantAny = false; boolean containerOwnerParticipantAny = false; boolean inAnyRegion = false; - List perRegionDebug = List.of(); if (wgs != null) { @@ -196,10 +257,8 @@ private static Decision evaluateBlockPolicy(Player actor, Block block, UUID orig final BoundingBox currentBox = box; boolean isParent = false; - boolean isChild = false; try { isParent = regions.stream().anyMatch(r -> r != region && safeContains(currentBox, r)); - isChild = regions.stream().anyMatch(r -> r != region && safeContains(r.boundingBox(), region)); } catch (Throwable t) { VaultStoragePlugin.getInstance().getLogger().warning("[VaultCapturePolicy] Error evaluating parent/child for region " + region.id() + ": " + t.getClass().getSimpleName() + " - " + t.getMessage()); @@ -220,53 +279,47 @@ private static Decision evaluateBlockPolicy(Player actor, Block block, UUID orig perRegionDebug = Collections.unmodifiableList(debugList); } - boolean disallowedOwnerSelf = actorParticipantAny && actorIsContainerOwner; - boolean baseAllowed; + return new RegionParticipation(inAnyRegion, actorParticipantAny, containerOwnerParticipantAny, perRegionDebug); + } - if (actorParticipantAny) { - // Actor is a participant of at least one relevant (non-parent) region - // Can vault if: block has owner, actor is not the owner, and owner is not participant of any relevant region - baseAllowed = (originalOwner != null) && !actorIsContainerOwner && !containerOwnerParticipantAny; - } else { - // Actor is not a participant of any relevant region - // Can only vault their own blocks - baseAllowed = actorIsContainerOwner; + /** + * Evaluate whether the actor may remove a ChestShop sign on the given container. + * Same region participation rule as {@link #evaluate(Player, Block)}, but does not require a Bolt owner. + */ + public static Decision evaluateChestShopSignRemoval(Player actor, Block containerBlock) { + BoltService bolt = VaultStoragePlugin.getInstance().getBoltService(); + UUID originalOwner = null; + if (bolt != null) { + try { + originalOwner = bolt.getOwner(containerBlock); + } catch (Throwable t) { + VaultStoragePlugin.getInstance().getLogger().warning("[VaultCapturePolicy] Error obtaining Bolt owner for block in " + + formatBlock(containerBlock) + ": " + t.getClass().getSimpleName() + " - " + t.getMessage()); + } } + boolean hasOverride = VaultPermission.ACTION_PLACE_OVERRIDE.has(actor); + boolean actorIsContainerOwner = originalOwner != null && originalOwner.equals(actor.getUniqueId()); + RegionParticipation participation = computeRegionParticipation(actor, containerBlock, originalOwner); - boolean allowed = (originalOwner != null) - && !disallowedOwnerSelf - && !hangableRestricted - && ((inAnyRegion && baseAllowed) || hasOverride); + // Same region gate as capture, but no Bolt-owner requirement. + boolean allowed = (participation.inAnyRegion() && participation.actorParticipantAny()) || hasOverride; - Decision.Reason reason; - if (allowed) { - reason = Decision.Reason.ALLOWED; - } else if (hangableRestricted) { - reason = Decision.Reason.ENTITIES_REQUIRE_ADMIN; - } else if (!inAnyRegion) { - reason = Decision.Reason.NOT_IN_REGION; - } else if (disallowedOwnerSelf) { - reason = Decision.Reason.OWNER_SELF_IN_REGION; - } else if (actorParticipantAny && containerOwnerParticipantAny) { - reason = Decision.Reason.CONTAINER_OWNER_IN_OVERLAP; - } else if (originalOwner == null) { - reason = Decision.Reason.UNPROTECTED_NO_OVERRIDE; - } else { - reason = Decision.Reason.NOT_INVOLVED_NOT_OWNER; - } + Decision.Reason reason = allowed + ? Decision.Reason.ALLOWED + : (!participation.inAnyRegion() ? Decision.Reason.NOT_IN_REGION : Decision.Reason.NOT_INVOLVED_NOT_OWNER); return new Decision( allowed, hasOverride, - actorParticipantAny, - containerOwnerParticipantAny, + participation.actorParticipantAny(), + participation.containerOwnerParticipantAny(), actorIsContainerOwner, - disallowedOwnerSelf, - baseAllowed, + false, + allowed, originalOwner, reason, - perRegionDebug + participation.perRegionDebug() ); } diff --git a/src/main/java/net/democracycraft/vault/internal/service/ChestShopServiceImp.java b/src/main/java/net/democracycraft/vault/internal/service/ChestShopServiceImp.java new file mode 100644 index 0000000..1d2aff7 --- /dev/null +++ b/src/main/java/net/democracycraft/vault/internal/service/ChestShopServiceImp.java @@ -0,0 +1,66 @@ +package net.democracycraft.vault.internal.service; + +import com.Acrobot.ChestShop.Events.ShopDestroyedEvent; +import com.Acrobot.ChestShop.Signs.ChestShopSign; +import com.Acrobot.ChestShop.Utils.uBlock; +import net.democracycraft.vault.api.service.ChestShopService; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Container; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link ChestShopService} backed by the ChestShop API. Only instantiated when ChestShop is installed, + * so that the {@code com.Acrobot} imports are never linked otherwise. + */ +public final class ChestShopServiceImp implements ChestShopService { + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public boolean isShopSign(@Nullable Block block) { + return block != null && ChestShopSign.isValid(block); + } + + @Override + public @NotNull List findShopSigns(@Nullable Block containerBlock) { + if (containerBlock == null) { + return List.of(); + } + List signs = uBlock.findConnectedShopSigns(containerBlock); + if (signs == null || signs.isEmpty()) { + return List.of(); + } + List blocks = new ArrayList<>(signs.size()); + for (Sign sign : signs) { + if (sign != null) { + blocks.add(sign.getBlock()); + } + } + return blocks; + } + + @Override + public boolean removeShopSign(@Nullable Player actor, @Nullable Block signBlock) { + if (signBlock == null || !(signBlock.getState() instanceof Sign sign)) { + return false; + } + if (!ChestShopSign.isValid(sign)) { + return false; + } + Container container = uBlock.findConnectedContainer(signBlock); + Bukkit.getPluginManager().callEvent(new ShopDestroyedEvent(actor, sign, container)); + signBlock.setType(Material.AIR); + return true; + } +} diff --git a/src/main/java/net/democracycraft/vault/internal/service/NoOpChestShopService.java b/src/main/java/net/democracycraft/vault/internal/service/NoOpChestShopService.java new file mode 100644 index 0000000..efcfd4b --- /dev/null +++ b/src/main/java/net/democracycraft/vault/internal/service/NoOpChestShopService.java @@ -0,0 +1,33 @@ +package net.democracycraft.vault.internal.service; + +import net.democracycraft.vault.api.service.ChestShopService; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** No-op {@link ChestShopService} used when ChestShop is not installed. */ +public final class NoOpChestShopService implements ChestShopService { + + @Override + public boolean isAvailable() { + return false; + } + + @Override + public boolean isShopSign(@Nullable Block block) { + return false; + } + + @Override + public @NotNull List findShopSigns(@Nullable Block containerBlock) { + return List.of(); + } + + @Override + public boolean removeShopSign(@Nullable Player actor, @Nullable Block signBlock) { + return false; + } +} diff --git a/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java b/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java index 8086089..4ee29f8 100644 --- a/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java +++ b/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java @@ -4,6 +4,7 @@ import net.democracycraft.vault.api.data.Dto; import net.democracycraft.vault.api.event.PlayerVaultEvent; import net.democracycraft.vault.api.service.BoltService; +import net.democracycraft.vault.api.service.ChestShopService; import net.democracycraft.vault.api.service.VaultService; import net.democracycraft.vault.internal.data.VaultDtoImp; import net.democracycraft.vault.internal.mappable.VaultImp; @@ -282,6 +283,7 @@ public static class SessionTexts implements Dto { public String signNotAllowed = "Sign not allowed: region/block rules."; public String containerNotAllowed = "Container not allowed: region/block rules."; public String signUnlocked = "Sign protection removed."; + public String chestShopSignRemoved = "ChestShop sign removed."; public String emptyCaptureSkipped = "Protection removed."; public String capturedOk = "Vault captured."; public String noBoltOwner = "No Bolt owner found; you will be set as the vault owner."; @@ -405,11 +407,21 @@ public void onInteract(PlayerInteractEvent event) { Block captureBlock = block; boolean clickedSign = isSignBlock(block); + ChestShopService chestShop = VaultStoragePlugin.getInstance().getChestShopService(); + boolean clickedChestShopSign = clickedSign && chestShop.isShopSign(block); // Sequential sign->container policy flow: // 1) Evaluate sign (non-container) policy and optionally unlock sign. // 2) Evaluate container policy on attached container (if present). - if (clickedSign) { + if (clickedChestShopSign) { + // Shop sign is handled through its container; it is removed once the container unlocks. + Block attachedContainer = resolveAttachedContainerBlock(block); + if (attachedContainer == null) { + busy[0] = false; + return; + } + captureBlock = attachedContainer; + } else if (clickedSign) { VaultCapturePolicy.Decision signDecision = VaultCapturePolicy.evaluate(actor, block); UUID signOwner = signDecision.containerOwner(); if (signDecision.allowed()) { @@ -432,6 +444,17 @@ public void onInteract(PlayerInteractEvent event) { VaultCapturePolicy.Decision decision = VaultCapturePolicy.evaluate(actor, captureBlock); UUID originalOwner = decision.containerOwner(); if (!decision.allowed()) { + // A shop sign on an empty, unprotected container can still be removed on region rights alone. + if (chestShop.isAvailable() && isContainerEmpty(captureBlock)) { + List shopSigns = chestShop.findShopSigns(captureBlock); + if (!shopSigns.isEmpty() + && VaultCapturePolicy.evaluateChestShopSignRemoval(actor, captureBlock).allowed()) { + removeAttachedChestShopSigns(actor, shopSigns); + actor.sendMessage(MiniMessageUtil.parseOrPlain(texts.chestShopSignRemoved)); + busy[0] = false; + return; + } + } actor.sendMessage(MiniMessageUtil.parseOrPlain(clickedSign ? texts.containerNotAllowed : texts.notAllowed)); busy[0] = false; return; @@ -442,6 +465,14 @@ public void onInteract(PlayerInteractEvent event) { // Only send "Protection removed" if we actually removed protection. // If protectionRemoved is false, it means we sent a warning (e.g., "Vault the other side first"). if (outcome.protectionRemoved()) { + // Drop any shop sign left on the now-unlocked container. + if (chestShop.isAvailable()) { + List shopSigns = chestShop.findShopSigns(captureBlock); + if (!shopSigns.isEmpty()) { + removeAttachedChestShopSigns(actor, shopSigns); + actor.sendMessage(MiniMessageUtil.parseOrPlain(texts.chestShopSignRemoved)); + } + } actor.sendMessage(MiniMessageUtil.parseOrPlain(texts.emptyCaptureSkipped)); } busy[0] = false; @@ -740,6 +771,18 @@ private static boolean isSignBlock(@NotNull Block block) { return block.getState() instanceof Sign; } + /** Unlocks and removes the given shop signs, notifying ChestShop. Must run on the main thread. */ + private void removeAttachedChestShopSigns(@NotNull Player actor, @NotNull List shopSigns) { + ChestShopService chestShop = VaultStoragePlugin.getInstance().getChestShopService(); + BoltService bolt = VaultStoragePlugin.getInstance().getBoltService(); + for (Block signBlock : shopSigns) { + if (bolt != null) { + try { bolt.removeProtection(signBlock); } catch (Throwable ignored) {} + } + chestShop.removeShopSign(actor, signBlock); + } + } + private static Block resolveAttachedContainerBlock(@NotNull Block signBlock) { if (!isSignBlock(signBlock)) return null; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4e5fb3a..fc8145b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,6 +4,7 @@ main: net.democracycraft.vault.VaultStoragePlugin api-version: '1.21' authors: [ Alepando ] depend: [ Bolt , WorldGuard, WorldEdit, Essentials ] +softdepend: [ ChestShop ] commands: vault: From 551c044a79573dd864b4a3e8a25482bcfa6a0267 Mon Sep 17 00:00:00 2001 From: Technofied <40795318+Technofied@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:34:28 +0800 Subject: [PATCH 2/3] refactor: decision chain to be switch not chained if else statement --- gradlew | 0 .../internal/security/VaultCapturePolicy.java | 25 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java b/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java index 38c71cb..8e4191a 100644 --- a/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java +++ b/src/main/java/net/democracycraft/vault/internal/security/VaultCapturePolicy.java @@ -174,22 +174,15 @@ private static Decision evaluateBlockPolicy(Player actor, Block block, UUID orig && !hangableRestricted && ((inAnyRegion && baseAllowed) || hasOverride); - Decision.Reason reason; - if (allowed) { - reason = Decision.Reason.ALLOWED; - } else if (hangableRestricted) { - reason = Decision.Reason.ENTITIES_REQUIRE_ADMIN; - } else if (!inAnyRegion) { - reason = Decision.Reason.NOT_IN_REGION; - } else if (disallowedOwnerSelf) { - reason = Decision.Reason.OWNER_SELF_IN_REGION; - } else if (actorParticipantAny && containerOwnerParticipantAny) { - reason = Decision.Reason.CONTAINER_OWNER_IN_OVERLAP; - } else if (originalOwner == null) { - reason = Decision.Reason.UNPROTECTED_NO_OVERRIDE; - } else { - reason = Decision.Reason.NOT_INVOLVED_NOT_OWNER; - } + Decision.Reason reason = switch (Boolean.TRUE) { + case Boolean b when allowed -> Decision.Reason.ALLOWED; + case Boolean b when hangableRestricted -> Decision.Reason.ENTITIES_REQUIRE_ADMIN; + case Boolean b when !inAnyRegion -> Decision.Reason.NOT_IN_REGION; + case Boolean b when disallowedOwnerSelf -> Decision.Reason.OWNER_SELF_IN_REGION; + case Boolean b when actorParticipantAny && containerOwnerParticipantAny -> Decision.Reason.CONTAINER_OWNER_IN_OVERLAP; + case Boolean b when originalOwner == null -> Decision.Reason.UNPROTECTED_NO_OVERRIDE; + default -> Decision.Reason.NOT_INVOLVED_NOT_OWNER; + }; return new Decision( allowed, From 57a9869d4529c8fdd4462f2dd816bc5a042d4c07 Mon Sep 17 00:00:00 2001 From: Technofied <40795318+Technofied@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:26:06 +0800 Subject: [PATCH 3/3] fix: chestshop vaultable status in action bar --- .../vault/internal/service/VaultCaptureService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java b/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java index 4ee29f8..776b36a 100644 --- a/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java +++ b/src/main/java/net/democracycraft/vault/internal/service/VaultCaptureService.java @@ -692,6 +692,11 @@ public void onInteractEntity(PlayerInteractEntityEvent event) { actor.sendActionBar(MiniMessageUtil.parseOrPlain(texts.actionBarIdle)); return; } + ChestShopService chestShop = VaultStoragePlugin.getInstance().getChestShopService(); + if (chestShop.isAvailable() && chestShop.isShopSign(target)) { + Block container = resolveAttachedContainerBlock(target); + if (container != null) target = container; + } var boltSvc = VaultStoragePlugin.getInstance().getBoltService(); UUID owner = boltSvc != null ? boltSvc.getOwner(target) : null; String ownerName = ownerDisplayNameAsync(owner);