From 59ffee153b3df2b3d7544637971f78c1e01e956f Mon Sep 17 00:00:00 2001 From: Fff Date: Wed, 9 Sep 2026 09:13:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 2 + README.md | 1 + .../lib/v2/cube/client/CubeSelection.java | 37 ++++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.en.md b/README.en.md index 53b34f98..6b8d3539 100644 --- a/README.en.md +++ b/README.en.md @@ -36,6 +36,8 @@ Opt a namespace into cube-based outlines and precise client picking with rotated and reversed cubes, parent models, weighted variants, multipart models and resource reloads. Optional providers describe moving BER parts using shared geometry and per-frame transforms. Geometry, background outline work, caches and rendered line counts have explicit limits. +Reloadable blacklists can use `CubeSelection.registerTargetExclusion(id, predicate)` and +`unregisterTargetExclusion(id)` without discarding baked geometry. ### Config Module diff --git a/README.md b/README.md index d0762483..5f3f19ae 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ AnvilLib 采用模块化设计,包含以下功能模块: 提供精确拾取和斜棱线高亮。 支持旋转、反向描边壳、父模型、随机变体、multipart 和资源重载; 可选提供器支持 BER 活动部件。几何共享、BVH 拾取和后台轮廓任务均有数量及内存预算。 +支持按 ID 注册、替换和注销运行时目标排除规则,方便接入可重载黑名单,且不丢弃模型缓存。 ### Config 模块 diff --git a/module.cube/src/main/java/dev/anvilcraft/lib/v2/cube/client/CubeSelection.java b/module.cube/src/main/java/dev/anvilcraft/lib/v2/cube/client/CubeSelection.java index 6c7d6df4..88e9ead4 100644 --- a/module.cube/src/main/java/dev/anvilcraft/lib/v2/cube/client/CubeSelection.java +++ b/module.cube/src/main/java/dev/anvilcraft/lib/v2/cube/client/CubeSelection.java @@ -24,12 +24,14 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; /** 仅在客户端初始化时注册命名空间;注册后的模型默认同时启用精确拾取和棱线高亮。 */ public final class CubeSelection { public static final int MAX_PARTS = 32; private static final Set NAMESPACES = ConcurrentHashMap.newKeySet(); private static final Set EXCLUDED = ConcurrentHashMap.newKeySet(); + private static final Map> TARGET_EXCLUSIONS = new ConcurrentHashMap<>(); private static final Map DYNAMIC = new ConcurrentHashMap<>(); private static final OutlineCache OUTLINES = new OutlineCache(); private static final Long2ObjectOpenHashMap FRAME = new Long2ObjectOpenHashMap<>(); @@ -50,6 +52,39 @@ public static void enableNamespace(String namespace) { public static void exclude(Block block) { EXCLUDED.add(block); } + /** + * 注册运行时目标排除规则;同一 id 再次注册会替换旧规则,不同 id 的规则互不覆盖。 + * 任意规则返回 true 时,target 返回 null,精确拾取与默认模型高亮均回退原版。 + * + *

规则不影响模型烘焙和 modelParts,可读取接入方当前的资源包配置。 + * 每次目标查询都会在帧缓存之前检查规则,因此修改配置或注销规则后即可恢复已加载的模型。 + * 回调应保持轻量、无副作用,不应捕获世界或方块实体等会跨存档保留的对象。 + * + * @param id 由调用模组自己的命名空间限定的规则标识 + * @param exclusion 返回 true 表示该状态不使用模型目标 + */ + public static void registerTargetExclusion(ResourceLocation id, Predicate exclusion) { + TARGET_EXCLUSIONS.put(id, exclusion); + } + + /** + * 注销指定运行时规则;不会清除其他规则或 exclude(Block) 设置的永久排除。 + * + * @return 是否存在并移除了该规则 + */ + public static boolean unregisterTargetExclusion(ResourceLocation id) { + return TARGET_EXCLUSIONS.remove(id) != null; + } + + /** 检查永久排除及当前运行时规则;不表示该状态一定存在可用的模型几何。 */ + public static boolean isTargetExcluded(BlockState state) { + if (EXCLUDED.contains(state.getBlock())) return true; + for (Predicate exclusion : TARGET_EXCLUSIONS.values()) { + if (exclusion.test(state)) return true; + } + return false; + } + /** 部件变换须与 BER 一致;最大包围盒用于寻找越过锚点格的机械臂等部件。 */ public static void registerDynamic(Block block, AABB maximumBounds, boolean includeStaticModel, BlockSelectionProvider provider) { if (!supportedBounds(block.defaultBlockState().hasOffsetFunction() ? maximumBounds.inflate(0.5) : maximumBounds)) { @@ -125,7 +160,7 @@ public static boolean extendsBlock(BlockState state) { } public static @Nullable Target target(ClientLevel level, BlockPos pos, BlockState state, float fraction) { - if (EXCLUDED.contains(state.getBlock())) return null; + if (isTargetExcluded(state)) return null; if (frameLevel != level || fraction != partialTick) beginFrame(level, fraction); Target cached = FRAME.get(pos.asLong()); if (cached != null && cached.state == state) return cached;