问题描述
注意:这并非特定于 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,Integer>>> professionToVillagerTaskMap;
// The hook that any mods will use in their source code
public static void addVillagerTasks(VillagerProfession executingProfession,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();
}
谢谢!
解决方法
简单的回答是:你不能做你想做的事。
问题是,虽然我知道永远不会在运行时调用“put”方法,但我不知道编译器是否会调用。
必须在运行时调用 put
方法才能使您的 mod 有用。当您的代码以可以执行的形式加载时——这就是运行时。这可能是您的 mod 的设置阶段,但它在 JVM 中运行。
如果源代码不包含注册表本身,那么编译器就不能将它翻译成可执行代码;它无法优化它不知道存在的东西。您(开发人员)无法知道将加载哪些 mod,因此编译器无法知道,因此无法对其进行优化或预先计算。这就是您为动态加载代码付出的代价。
至于你贴的代码:它不会工作。
static
块在类加载时执行。将其视为类的构造函数而不是对象。当 mod 可以调用它的任何方法时,必须加载该类,并且它的静态块已经被执行。在从外部调用任何方法之前,您的地图将被设置为空。添加的所有任务将永远留在构建器中,未使用、未见、未爱。
保留建设者。让 mods 添加他们的条目。然后,当所有 mod 加载完成并且游戏开始时,调用 build()
并将结果用作注册表。 (使用您的模组框架提供的任何“游戏开始”挂钩。)