Merge 1.19.2 into 1.20
This commit is contained in:
commit
3ad7a8ce9d
|
|
@ -0,0 +1,28 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
|
||||
|
||||
import net.minecraft.world.entity.ambient.Bat;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Mixin(value = Bat.class, priority = 1200)
|
||||
public class BatMixin {
|
||||
private static long mfix$lastQueriedTime = -1L;
|
||||
private static LocalDate mfix$lastQueriedDate = null;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason avoid excessive allocations from continuously querying the date, only get a new date once every 30 seconds
|
||||
*/
|
||||
@Redirect(method = "isHalloween", at = @At(value = "INVOKE", target = "Ljava/time/LocalDate;now()Ljava/time/LocalDate;"), require = 0)
|
||||
private static LocalDate useCachedLocalDate() {
|
||||
LocalDate date = mfix$lastQueriedDate;
|
||||
if(date == null || Math.abs(System.currentTimeMillis() - mfix$lastQueriedTime) > 30000) {
|
||||
mfix$lastQueriedDate = date = LocalDate.now();
|
||||
mfix$lastQueriedTime = System.currentTimeMillis();
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
|
||||
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ChunkGenerator.class)
|
||||
public class ChunkGeneratorMixin {
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Avoid allocation if the chunk contains no structures
|
||||
*/
|
||||
@Redirect(method = "getMobsAt", at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;"), require = 0)
|
||||
private Set<?> avoidSetAllocation(Map<?, ?> instance) {
|
||||
if(instance.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
return instance.entrySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.embeddedt.modernfix.util.EitherUtil;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(value = ChunkHolder.class, priority = 500)
|
||||
public abstract class ChunkHolderMixin {
|
||||
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture();
|
||||
|
||||
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture();
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason avoid Optional allocation
|
||||
*/
|
||||
@Overwrite
|
||||
public LevelChunk getTickingChunk() {
|
||||
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getTickingChunkFuture();
|
||||
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
|
||||
return either == null ? null : EitherUtil.leftOrNull(either);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason avoid Optional allocation
|
||||
*/
|
||||
@Overwrite
|
||||
public LevelChunk getFullChunk() {
|
||||
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getFullChunkFuture();
|
||||
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
|
||||
return either == null ? null : EitherUtil.leftOrNull(either);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class EitherUtil {
|
||||
private static final Class<?> LEFT, RIGHT;
|
||||
private static final MethodHandle LEFT_VAL, RIGHT_VAL;
|
||||
|
||||
static {
|
||||
try {
|
||||
LEFT = Class.forName("com.mojang.datafixers.util.Either$Left");
|
||||
RIGHT = Class.forName("com.mojang.datafixers.util.Either$Right");
|
||||
Field lvalue = LEFT.getDeclaredField("value");
|
||||
lvalue.setAccessible(true);
|
||||
Field rvalue = RIGHT.getDeclaredField("value");
|
||||
rvalue.setAccessible(true);
|
||||
LEFT_VAL = MethodHandles.publicLookup().unreflectGetter(lvalue).asType(MethodType.methodType(Object.class, Either.class));
|
||||
RIGHT_VAL = MethodHandles.publicLookup().unreflectGetter(rvalue).asType(MethodType.methodType(Object.class, Either.class));
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new AssertionError("Failed to hook DFU Either", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <L, R> L leftOrNull(Either<L, R> either) {
|
||||
if(either.getClass() == LEFT) {
|
||||
try {
|
||||
return (L)LEFT_VAL.invokeExact(either);
|
||||
} catch(Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <L, R> R rightOrNull(Either<L, R> either) {
|
||||
if(either.getClass() == RIGHT) {
|
||||
try {
|
||||
return (R)RIGHT_VAL.invokeExact(either);
|
||||
} catch(Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package org.embeddedt.modernfix.forge.config;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.FileWatcher;
|
||||
import com.google.common.collect.ForwardingCollection;
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* Throttle NightConfig's file watching. There are reports of this consuming excessive CPU time
|
||||
* (<a href="https://github.com/TheElectronWill/night-config/pull/144">example</a>) and the spammed iterator calls
|
||||
* end up being 10% of allocations when testing in a dev environment.
|
||||
*/
|
||||
public class NightConfigWatchThrottler {
|
||||
private static final long DELAY = TimeUnit.MILLISECONDS.toNanos(1000);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static void throttle() {
|
||||
Map watchedDirs = ObfuscationReflectionHelper.getPrivateValue(FileWatcher.class, FileWatcher.defaultInstance(), "watchedDirs");
|
||||
ObfuscationReflectionHelper.setPrivateValue(FileWatcher.class, FileWatcher.defaultInstance(), new ForwardingMap() {
|
||||
@Override
|
||||
protected Map delegate() {
|
||||
return watchedDirs;
|
||||
}
|
||||
|
||||
private Collection cachedValues;
|
||||
|
||||
@Override
|
||||
public Collection values() {
|
||||
if(cachedValues == null) {
|
||||
Collection values = super.values();
|
||||
cachedValues = new ForwardingCollection() {
|
||||
@Override
|
||||
protected Collection delegate() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator iterator() {
|
||||
// iterator() is called at the beginning of each iteration of the watch loop,
|
||||
// so it is a good spot to inject the delay.
|
||||
LockSupport.parkNanos(DELAY);
|
||||
return super.iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
return cachedValues;
|
||||
}
|
||||
}, "watchedDirs");
|
||||
}
|
||||
}
|
||||
|
|
@ -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<MobSpawnSettings.SpawnerData> reuseOldList(List<MobSpawnSettings.SpawnerData> items, LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList<MobSpawnSettings.SpawnerData> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.level.LevelEvent;
|
||||
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(LevelEvent.PotentialSpawns.class)
|
||||
public class PotentialSpawnsMixin {
|
||||
@Shadow(remap = false) @Final @Mutable private List<MobSpawnSettings.SpawnerData> view;
|
||||
@Shadow(remap = false) @Final @Mutable private List<MobSpawnSettings.SpawnerData> list;
|
||||
|
||||
private static final ArrayList<MobSpawnSettings.SpawnerData> SENTINEL = new ArrayList<>();
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 1))
|
||||
private ArrayList<?> avoidListAlloc1() {
|
||||
return SENTINEL;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 0))
|
||||
private ArrayList<?> avoidListAlloc2(Collection c) {
|
||||
return SENTINEL;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Collections;unmodifiableList(Ljava/util/List;)Ljava/util/List;"))
|
||||
private List<?> avoidListAlloc3(List<?> l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initializeSmartLists(LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList<MobSpawnSettings.SpawnerData> 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<Boolean> cir) {
|
||||
mfix$populateList();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|||
import org.embeddedt.modernfix.forge.classloading.ATInjector;
|
||||
import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList;
|
||||
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
|
||||
import org.embeddedt.modernfix.forge.config.NightConfigWatchThrottler;
|
||||
import org.embeddedt.modernfix.forge.init.ModernFixForge;
|
||||
import org.embeddedt.modernfix.forge.packet.PacketHandler;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
|
@ -112,6 +113,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
|||
}
|
||||
|
||||
NightConfigFixer.monitorFileWatcher();
|
||||
NightConfigWatchThrottler.throttle();
|
||||
}
|
||||
|
||||
public void applyASMTransformers(String mixinClassName, ClassNode targetClass) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user