You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

存储2048个Minecraft ItemTag的最优数据结构方案问询

解决Minecraft模组中动态物品堆叠容量的系统化实现问题

背景

我正在开发一款Minecraft 1.21版本的模组(基于Java 21),项目目标是:

  • 利用max_stack_size数据组件,让用户通过直观的配置方式设置物品默认最大堆叠容量——只需把物品加入max_stack_size_N.json,ItemTag逻辑会自动处理后续流程。
  • 移除硬编码的99最大堆叠上限,替换为更灵活的方案。

目前我仅完成了N为2的幂次的情况,通过手动逐个定义Tag、注册监听器,再在Mixin里逐一判断:

public class ModTags{
public static final TagKey<Item> IS_STACK_SIZE_1 = createTag("stack_size_1" );
public static final TagKey<Item> IS_STACK_SIZE_2 = createTag("stack_size_2" );
public static final TagKey<Item> IS_STACK_SIZE_4 = createTag("stack_size_4" );
[...] 
public static final TagKey<Item> IS_STACK_SIZE_2048 = createTag("stack_size_2048");

[...] 
addReloadListener("stack_size_1"   );
addReloadListener("stack_size_2"   );
addReloadListener("stack_size_4"   );
[...] 
addReloadListener("stack_size_2048");
}}
@Mixin(ItemStack.class)
public abstract class ItemStack_SizeMixin implements ComponentHolder, FabricItemStack {
[...] 
@Inject(method="getMaxCount", at = @At("HEAD"))
    private void updateMaxStackSizeWithTag(CallbackInfoReturnable<Integer> cir){
        ItemStack thisAsStack = (ItemStack)(Object) this;
        
        if      ( thisAsStack.isIn(ModTags.Items.IS_STACK_SIZE_2048)) ChangeStackSize(thisAsStack, 2048 );
        else if ( thisAsStack.isIn(ModTags.Items.IS_STACK_SIZE_1024)) ChangeStackSize(thisAsStack, 1024 );
[...] 
}
}

这种方案扩展性极差,现在需要替换成能支持任意数值(比如372这类非2的幂次)的系统,但缺乏相关Java实现经验。之前尝试用ArrayList实现时一直触发栈溢出,想知道如何系统化定义、存储并检查最多2048个ItemTag,满足自定义堆叠容量的需求。


解决方案

核心思路

放弃手动逐个定义Tag的方式,改用动态映射+自动注册的模式,把堆叠容量数值与对应的TagKey绑定,避免重复代码,同时解决扩展性问题。

步骤1:用Map动态管理Tag与数值的映射

修改ModTags类,用HashMap存储所有堆叠容量对应的TagKey,同时自动完成Tag创建和监听器注册:

public class ModTags {
    // 存储堆叠容量到TagKey的映射
    public static final Map<Integer, TagKey<Item>> STACK_SIZE_TAGS = new HashMap<>();
    // 最大支持的堆叠容量,可根据需求调整
    private static final int MAX_SUPPORTED_SIZE = 2048;
    // 替换为你的模组ID
    private static final String MOD_ID = "你的模组ID";

    static {
        // 自动初始化1到2048的所有Tag
        for (int size = 1; size <= MAX_SUPPORTED_SIZE; size++) {
            String tagPath = "stack_size_" + size;
            TagKey<Item> tag = TagKey.of(Registry.ITEM_KEY, new Identifier(MOD_ID, tagPath));
            STACK_SIZE_TAGS.put(size, tag);
            // 自动注册重载监听器
            addReloadListener(tagPath);
        }
    }

    // 保留原有的Tag创建逻辑
    private static TagKey<Item> createTag(String path) {
        return TagKey.of(Registry.ITEM_KEY, new Identifier(MOD_ID, path));
    }

    // 保留原有的监听器注册逻辑
    private static void addReloadListener(String tagPath) {
        ServerResourceManagerBuilder.registerReloadListener(
            new TagReloadListener(Registry.ITEM_KEY, new Identifier(MOD_ID, tagPath))
        );
    }
}

步骤2:优化Mixin中的堆叠容量判断逻辑

替换原有的链式if-else,改为通过物品的Tag集合反向匹配堆叠容量,同时避免递归调用导致栈溢出:

@Mixin(ItemStack.class)
public abstract class ItemStack_SizeMixin implements ComponentHolder, FabricItemStack {
    // 替换为你的模组ID
    private static final String MOD_ID = "你的模组ID";

    @Inject(method = "getMaxCount", at = @At("HEAD"), cancellable = true)
    private void updateMaxStackSizeWithDynamicTags(CallbackInfoReturnable<Integer> cir) {
        ItemStack stack = (ItemStack) (Object) this;
        Item item = stack.getItem();

        // 遍历物品的所有Tag,匹配自定义堆叠容量Tag
        for (TagKey<Item> tag : item.getRegistryEntry().streamTags().toList()) {
            Identifier tagId = tag.id();
            // 只处理本模组的Tag
            if (!MOD_ID.equals(tagId.getNamespace())) continue;

            String tagPath = tagId.getPath();
            if (tagPath.startsWith("stack_size_")) {
                try {
                    // 提取堆叠容量数值
                    int targetSize = Integer.parseInt(tagPath.substring("stack_size_".length()));
                    // 直接返回自定义容量,终止原方法执行
                    cir.setReturnValue(targetSize);
                    return;
                } catch (NumberFormatException e) {
                    // 格式错误的Tag,直接忽略
                    continue;
                }
            }
        }

        // 未找到自定义Tag时,保留原方法逻辑
    }
}

栈溢出问题的解决要点

之前用ArrayList出现栈溢出,大概率是因为递归调用或循环引用:

  • 确保Mixin的getMaxCount方法中不会再次调用getMaxCount(比如原有的ChangeStackSize方法如果内部触发了这个调用,就会导致无限递归)
  • 用cir.setReturnValue直接终止原方法执行,避免重复修改栈对象
  • 遍历Tag时不要修改物品的Tag集合,防止循环遍历

额外优化建议

  • 可添加配置文件,让用户自定义MAX_SUPPORTED_SIZE的值
  • 加入缓存逻辑:用Map<Item, Integer>缓存已处理物品的堆叠容量,避免每次调用getMaxCount都遍历Tag
  • 给用户添加提示:不要给同一个物品添加多个stack_size_N Tag,否则只会生效第一个匹配的Tag

内容的提问来源于stack exchange,提问作者AnDrew the Awesome

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 23:46:13