From 1814bd3e1faff1518119a7b5fcf85784c796023d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:39:24 -0400 Subject: [PATCH] Reduce overhead of the PotentialSpawns event --- .../ForgeEventFactoryMixin.java | 25 +++++++ .../PotentialSpawnsMixin.java | 68 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/ForgeEventFactoryMixin.java create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/PotentialSpawnsMixin.java diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/ForgeEventFactoryMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/ForgeEventFactoryMixin.java new file mode 100644 index 00000000..ca203b86 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/ForgeEventFactoryMixin.java @@ -0,0 +1,25 @@ +package org.embeddedt.modernfix.forge.mixin.perf.potential_spawns_alloc; + +import net.minecraft.core.BlockPos; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraftforge.event.ForgeEventFactory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(ForgeEventFactory.class) +public class ForgeEventFactoryMixin { + @Redirect(method = "getPotentialSpawns", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/random/WeightedRandomList;create(Ljava/util/List;)Lnet/minecraft/util/random/WeightedRandomList;")) + private static WeightedRandomList reuseOldList(List items, LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList oldList) { + // Our patched version of PotentialSpawns will return the same list as unwrap() if no one mutated the list + if(items == oldList.unwrap()) { + return oldList; + } + return WeightedRandomList.create(items); + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/PotentialSpawnsMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/PotentialSpawnsMixin.java new file mode 100644 index 00000000..9fac5799 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/potential_spawns_alloc/PotentialSpawnsMixin.java @@ -0,0 +1,68 @@ +package org.embeddedt.modernfix.forge.mixin.perf.potential_spawns_alloc; + +import net.minecraft.core.BlockPos; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraftforge.event.world.WorldEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +@Mixin(WorldEvent.PotentialSpawns.class) +public class PotentialSpawnsMixin { + @Shadow(remap = false) @Final @Mutable private List view; + @Shadow(remap = false) @Final @Mutable private List list; + + private static final ArrayList SENTINEL = new ArrayList<>(); + + @Redirect(method = "", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 1)) + private ArrayList avoidListAlloc1() { + return SENTINEL; + } + + @Redirect(method = "", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 0)) + private ArrayList avoidListAlloc2(Collection c) { + return SENTINEL; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Collections;unmodifiableList(Ljava/util/List;)Ljava/util/List;")) + private List avoidListAlloc3(List l) { + return null; + } + + @Inject(method = "", at = @At("RETURN")) + private void initializeSmartLists(LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList oldList, CallbackInfo ci) { + this.view = oldList.unwrap(); + this.list = null; + } + + private void mfix$populateList() { + if(this.list == null) { + this.list = new ArrayList<>(this.view); + this.view = Collections.unmodifiableList(this.list); + } + } + + @Inject(method = {"addSpawnerData" }, at = @At("HEAD"), remap = false) + private void populateList(MobSpawnSettings.SpawnerData data, CallbackInfo ci) { + mfix$populateList(); + } + + @Inject(method = {"removeSpawnerData" }, at = @At("HEAD"), remap = false) + private void populateList(MobSpawnSettings.SpawnerData data, CallbackInfoReturnable cir) { + mfix$populateList(); + } +}