代码之家  ›  专栏  ›  技术社区  ›  AnDrew the Awesome

存储2048个minecraft ItemTag的最佳数据结构?

  •  1
  • AnDrew the Awesome  · 技术社区  · 11 月前

    上下文,我正在做一个 Minecraft mod (1.21,即Java 21),其目的是:

    1. 使用max_stack_size数据组件以直接配置的方式驱动项目的明显默认最大堆栈大小。要使项目显示为默认的最大堆栈大小N,只需将项目添加到max_stack_size_N.json中,其余部分将由项目标签逻辑完成。
    2. 撕下硬编码 99 将最大堆栈大小限制为碎片,并用更有用的东西替换。

    对于N=2,我通过单独定义、分配一个监听器,并检查每个标签的2次幂来实现这两个目标。

    
    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_2 = 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 );
    [...]
    }
    }
    

    当我选择它时,我知道一种策略不会扩大规模,但它的天真会使mod的其余逻辑易于编码,检查一下。我希望在我满意地解决其他所有问题的时间里,我能积累专业知识,用更好的系统来取代它。嗯,据我所知,那一刻已经到来。

    我当然没有:我的问题,

    系统地定义、存储和检查2048个minecraft物品标签的最佳方法是什么?为了那些真正想要max_stack_sizes为372的人?

    我已经尝试将其实现为ArrayList,这一直会导致stackOverflows。除此之外,我只是没有Java知识来产生想法。

    1 回复  |  直到 11 月前
        1
  •  1
  •   QBrute    11 月前

    我认为你应该考虑的第一件事是:“我真的需要人们能够选择1837的堆栈大小吗?”。玩家选择这样一个任意数字有什么好处?仅仅因为你不能,并不意味着你应该。

    玩家习惯于1、16和64的堆叠,所以以我天真的思维方式,我会说:坚持2的幂,这就足够了。不仅如此,它还通过减少玩家的选择负担,使你的生活更轻松,也让玩家的生活更美好。(我冒昧地说,玩家不在乎不同的堆栈大小 完全 。他们可能会选择尽可能大的尺寸……)

    也就是说,在处理重复创建和读取对象时,使用循环是一个好主意,也许还可以使用一个地图,在那里你可以很容易地存储相同类型的对象。

    这样,您还可以消除为每种堆栈大小创建常量的需要。

    我不是最新的Minecraft改装,但我认为大多数代码都是由以下部分组成的 static 变量和方法。此外,我也不是100%确定如何 thisAsStack.isIn 虽然可以,但我将尝试为您提供一个如何概括代码并使其可扩展的起点。最后,我建议如下:

    public class ModTags {
        /**
         * Allows for stack sizes up to 2^11 = 2048
         */
        private static final Integer MAX_POWER_OF_TWO = 11;
        public static final Map<Integer, TagKey<Item>> STACK_SIZES;
    
        static {
            Map<Integer, Object> tmpMap = new HashMap<>();
            for(int i = 0; i <= MAX_POWER_OF_TWO; i++) {
                int stackSize = 1 << i;
                String tagName = "stack_size_" + i;
                tmpMap.put(stackSize, createTag(tagName));
                addReloadListener(tagName);
            }
            STACK_SIZES = Collections.unmodifiableMap(tmpMap);
        }
        ...
    }
    

    以及您的实用程序类:

    ...
    private void updateMaxStackSizeWithTag(CallbackInfoReturnable<Integer> cir){
        ItemStack thisAsStack = (ItemStack)(Object) this;
        ModTags.STACK_SIZES.entrySet().stream()
            .filter(entry -> thisAsStack.isIn(entry.getValue()))
            .findFirst()
            .map(Map.Entry::getKey)
            .ifPresentOrElse(stackSize -> ChangeStackSize(thisAsStack, stackSize), () -> /* Handle not found here */);
    }
                
    

    有了这个,你就有了一种通用的方法,可以用2的幂来添加堆栈大小,并在几行中处理所有的注册和更新。不再创建数百个类似的常量。

    同样,由于我不能100%确定你代码中的所有内容是如何工作的,我希望我至少能给你一个工作的基础。