0

注意:这不是 Minecraft Fabric 特有的。我只是刚接触严格的运行前优化。

我正在为 Minecraft 模组编写一个 API 挂钩,它允许将各种任务映射到村民的“职业”属性,允许其他模组为自定义职业添加自定义任务。我已经完成了所有后端代码,所以现在我担心优化。

我有一个ImmutableMap.Builder<VillagerProfession, VillagerTask>用来存储其他模组添加的任务的。问题是,虽然知道永远不会在运行时调用“put”方法,但我不知道编译器是否这样做。显然,由于这是一款游戏,并且模组包中的启动时间已经很长,我想尽可能地优化它,因为每个希望添加新村民任务的模组都会使用它。

这是我当前的“任务注册表”源代码:

private static final ImmutableMap.Builder<VillagerProfession, ImmutableList<Pair<Task<? super VillagerEntity>, Integer>>> professionToVillagerTaskBuilder = ImmutableMap.builder();
    
    private static final ImmutableMap<VillagerProfession, ImmutableList<Pair<Task<? super VillagerEntity>, Integer>>> professionToVillagerTaskMap;
    
    // The hook that any mods will use in their source code
    public static void addVillagerTasks(VillagerProfession executingProfession, ImmutableList<Pair<Task<? super VillagerEntity>, Integer>> task)
    {
        professionToVillagerTaskBuilder.put(executingProfession, task);
    }
    
    //The tasklist retrieval method used at runtime
    static ImmutableList<Pair<Task<? super VillagerEntity>, Integer>> getVillagerRandomTasks(VillagerProfession profession)
    {
        return professionToVillagerTaskMap.get(profession);
    }
    
    static { // probably not the correct way to do this, but it lets me mark the map as final
        professionToVillagerTaskMap = professionToVillagerTaskBuilder.build();
    }

谢谢!

4

1 回答 1

1

简短的回答是:你不能做你想做的事。

问题是,虽然我知道永远不会在运行时调用“put”方法,但我不知道编译器是否这样做。

必须在运行时调用该put方法才能使您的 mod 有用。当您的代码以可以执行的形式加载时——这就是运行时。这可能是您的 mod 的设置阶段,但它在 JVM 中运行。

如果源代码不包含注册表本身,则编译器无法将其转换为可执行代码;它无法优化它不知道存在的东西。您(开发人员)不知道将加载哪些模块,因此编译器不知道,因此它无法优化或预先计算它。这就是您为动态加载代码所付出的代价。


至于你提出的代码:它不会工作。

static块在加载类时执行。将其视为您的类而不是对象的构造函数。当一个 mod 可以调用它的任何方法时,这个类必须被加载,并且它的静态块已经被执行了。在从外部调用任何方法之前,您的地图将被设置并清空。添加的所有任务将永远留在构建器中,未使用,未见,未爱。

保留建设者。让模组添加他们的条目。然后,当所有 mod 加载完成并且游戏开始时,调用build()并将结果用作注册表。(使用您的改装框架提供的任何“游戏正在开始”挂钩。)

于 2021-06-05T15:57:02.570 回答