Skip to content

Commit 3ad7a8c

Browse files
committed
Merge 1.19.2 into 1.20
2 parents 0e9d024 + b916ef5 commit 3ad7a8c

File tree

8 files changed

+297
-0
lines changed

8 files changed

+297
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
2+
3+
import net.minecraft.world.entity.ambient.Bat;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.injection.At;
6+
import org.spongepowered.asm.mixin.injection.Redirect;
7+
8+
import java.time.LocalDate;
9+
10+
@Mixin(value = Bat.class, priority = 1200)
11+
public class BatMixin {
12+
private static long mfix$lastQueriedTime = -1L;
13+
private static LocalDate mfix$lastQueriedDate = null;
14+
15+
/**
16+
* @author embeddedt
17+
* @reason avoid excessive allocations from continuously querying the date, only get a new date once every 30 seconds
18+
*/
19+
@Redirect(method = "isHalloween", at = @At(value = "INVOKE", target = "Ljava/time/LocalDate;now()Ljava/time/LocalDate;"), require = 0)
20+
private static LocalDate useCachedLocalDate() {
21+
LocalDate date = mfix$lastQueriedDate;
22+
if(date == null || Math.abs(System.currentTimeMillis() - mfix$lastQueriedTime) > 30000) {
23+
mfix$lastQueriedDate = date = LocalDate.now();
24+
mfix$lastQueriedTime = System.currentTimeMillis();
25+
}
26+
return date;
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
2+
3+
import net.minecraft.world.level.chunk.ChunkGenerator;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.injection.At;
6+
import org.spongepowered.asm.mixin.injection.Redirect;
7+
8+
import java.util.Collections;
9+
import java.util.Map;
10+
import java.util.Set;
11+
12+
@Mixin(ChunkGenerator.class)
13+
public class ChunkGeneratorMixin {
14+
/**
15+
* @author embeddedt
16+
* @reason Avoid allocation if the chunk contains no structures
17+
*/
18+
@Redirect(method = "getMobsAt", at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;"), require = 0)
19+
private Set<?> avoidSetAllocation(Map<?, ?> instance) {
20+
if(instance.isEmpty()) {
21+
return Collections.emptySet();
22+
} else {
23+
return instance.entrySet();
24+
}
25+
}
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
2+
3+
import com.mojang.datafixers.util.Either;
4+
import net.minecraft.server.level.ChunkHolder;
5+
import net.minecraft.world.level.chunk.LevelChunk;
6+
import org.embeddedt.modernfix.util.EitherUtil;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.Overwrite;
9+
import org.spongepowered.asm.mixin.Shadow;
10+
11+
import java.util.concurrent.CompletableFuture;
12+
13+
@Mixin(value = ChunkHolder.class, priority = 500)
14+
public abstract class ChunkHolderMixin {
15+
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture();
16+
17+
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture();
18+
19+
/**
20+
* @author embeddedt
21+
* @reason avoid Optional allocation
22+
*/
23+
@Overwrite
24+
public LevelChunk getTickingChunk() {
25+
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getTickingChunkFuture();
26+
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
27+
return either == null ? null : EitherUtil.leftOrNull(either);
28+
}
29+
30+
/**
31+
* @author embeddedt
32+
* @reason avoid Optional allocation
33+
*/
34+
@Overwrite
35+
public LevelChunk getFullChunk() {
36+
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getFullChunkFuture();
37+
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
38+
return either == null ? null : EitherUtil.leftOrNull(either);
39+
}
40+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.embeddedt.modernfix.util;
2+
3+
import com.mojang.datafixers.util.Either;
4+
5+
import java.lang.invoke.MethodHandle;
6+
import java.lang.invoke.MethodHandles;
7+
import java.lang.invoke.MethodType;
8+
import java.lang.reflect.Field;
9+
10+
public class EitherUtil {
11+
private static final Class<?> LEFT, RIGHT;
12+
private static final MethodHandle LEFT_VAL, RIGHT_VAL;
13+
14+
static {
15+
try {
16+
LEFT = Class.forName("com.mojang.datafixers.util.Either$Left");
17+
RIGHT = Class.forName("com.mojang.datafixers.util.Either$Right");
18+
Field lvalue = LEFT.getDeclaredField("value");
19+
lvalue.setAccessible(true);
20+
Field rvalue = RIGHT.getDeclaredField("value");
21+
rvalue.setAccessible(true);
22+
LEFT_VAL = MethodHandles.publicLookup().unreflectGetter(lvalue).asType(MethodType.methodType(Object.class, Either.class));
23+
RIGHT_VAL = MethodHandles.publicLookup().unreflectGetter(rvalue).asType(MethodType.methodType(Object.class, Either.class));
24+
} catch(ReflectiveOperationException e) {
25+
throw new AssertionError("Failed to hook DFU Either", e);
26+
}
27+
}
28+
29+
@SuppressWarnings("unchecked")
30+
public static <L, R> L leftOrNull(Either<L, R> either) {
31+
if(either.getClass() == LEFT) {
32+
try {
33+
return (L)LEFT_VAL.invokeExact(either);
34+
} catch(Throwable e) {
35+
throw new RuntimeException(e);
36+
}
37+
}
38+
return null;
39+
}
40+
41+
@SuppressWarnings("unchecked")
42+
public static <L, R> R rightOrNull(Either<L, R> either) {
43+
if(either.getClass() == RIGHT) {
44+
try {
45+
return (R)RIGHT_VAL.invokeExact(either);
46+
} catch(Throwable e) {
47+
throw new RuntimeException(e);
48+
}
49+
}
50+
return null;
51+
}
52+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.embeddedt.modernfix.forge.config;
2+
3+
import com.electronwill.nightconfig.core.file.FileWatcher;
4+
import com.google.common.collect.ForwardingCollection;
5+
import com.google.common.collect.ForwardingMap;
6+
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
7+
8+
import java.util.Collection;
9+
import java.util.Iterator;
10+
import java.util.Map;
11+
import java.util.concurrent.TimeUnit;
12+
import java.util.concurrent.locks.LockSupport;
13+
14+
/**
15+
* Throttle NightConfig's file watching. There are reports of this consuming excessive CPU time
16+
* (<a href="https://github.com/TheElectronWill/night-config/pull/144">example</a>) and the spammed iterator calls
17+
* end up being 10% of allocations when testing in a dev environment.
18+
*/
19+
public class NightConfigWatchThrottler {
20+
private static final long DELAY = TimeUnit.MILLISECONDS.toNanos(1000);
21+
22+
@SuppressWarnings("rawtypes")
23+
public static void throttle() {
24+
Map watchedDirs = ObfuscationReflectionHelper.getPrivateValue(FileWatcher.class, FileWatcher.defaultInstance(), "watchedDirs");
25+
ObfuscationReflectionHelper.setPrivateValue(FileWatcher.class, FileWatcher.defaultInstance(), new ForwardingMap() {
26+
@Override
27+
protected Map delegate() {
28+
return watchedDirs;
29+
}
30+
31+
private Collection cachedValues;
32+
33+
@Override
34+
public Collection values() {
35+
if(cachedValues == null) {
36+
Collection values = super.values();
37+
cachedValues = new ForwardingCollection() {
38+
@Override
39+
protected Collection delegate() {
40+
return values;
41+
}
42+
43+
@Override
44+
public Iterator iterator() {
45+
// iterator() is called at the beginning of each iteration of the watch loop,
46+
// so it is a good spot to inject the delay.
47+
LockSupport.parkNanos(DELAY);
48+
return super.iterator();
49+
}
50+
};
51+
}
52+
return cachedValues;
53+
}
54+
}, "watchedDirs");
55+
}
56+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.embeddedt.modernfix.forge.mixin.perf.potential_spawns_alloc;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.util.random.WeightedRandomList;
5+
import net.minecraft.world.entity.MobCategory;
6+
import net.minecraft.world.level.LevelAccessor;
7+
import net.minecraft.world.level.biome.MobSpawnSettings;
8+
import net.minecraftforge.event.ForgeEventFactory;
9+
import org.spongepowered.asm.mixin.Mixin;
10+
import org.spongepowered.asm.mixin.injection.At;
11+
import org.spongepowered.asm.mixin.injection.Redirect;
12+
13+
import java.util.List;
14+
15+
@Mixin(ForgeEventFactory.class)
16+
public class ForgeEventFactoryMixin {
17+
@Redirect(method = "getPotentialSpawns", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/random/WeightedRandomList;create(Ljava/util/List;)Lnet/minecraft/util/random/WeightedRandomList;"))
18+
private static WeightedRandomList<MobSpawnSettings.SpawnerData> reuseOldList(List<MobSpawnSettings.SpawnerData> items, LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList<MobSpawnSettings.SpawnerData> oldList) {
19+
// Our patched version of PotentialSpawns will return the same list as unwrap() if no one mutated the list
20+
if(items == oldList.unwrap()) {
21+
return oldList;
22+
}
23+
return WeightedRandomList.create(items);
24+
}
25+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.embeddedt.modernfix.forge.mixin.perf.potential_spawns_alloc;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.util.random.WeightedRandomList;
5+
import net.minecraft.world.entity.MobCategory;
6+
import net.minecraft.world.level.LevelAccessor;
7+
import net.minecraft.world.level.biome.MobSpawnSettings;
8+
import net.minecraftforge.event.level.LevelEvent;
9+
import org.spongepowered.asm.mixin.Final;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Mutable;
12+
import org.spongepowered.asm.mixin.Shadow;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
import org.spongepowered.asm.mixin.injection.Inject;
15+
import org.spongepowered.asm.mixin.injection.Redirect;
16+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
17+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
@Mixin(LevelEvent.PotentialSpawns.class)
25+
public class PotentialSpawnsMixin {
26+
@Shadow(remap = false) @Final @Mutable private List<MobSpawnSettings.SpawnerData> view;
27+
@Shadow(remap = false) @Final @Mutable private List<MobSpawnSettings.SpawnerData> list;
28+
29+
private static final ArrayList<MobSpawnSettings.SpawnerData> SENTINEL = new ArrayList<>();
30+
31+
@Redirect(method = "<init>", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 1))
32+
private ArrayList<?> avoidListAlloc1() {
33+
return SENTINEL;
34+
}
35+
36+
@Redirect(method = "<init>", at = @At(value = "NEW", target = "java/util/ArrayList", ordinal = 0))
37+
private ArrayList<?> avoidListAlloc2(Collection c) {
38+
return SENTINEL;
39+
}
40+
41+
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Collections;unmodifiableList(Ljava/util/List;)Ljava/util/List;"))
42+
private List<?> avoidListAlloc3(List<?> l) {
43+
return null;
44+
}
45+
46+
@Inject(method = "<init>", at = @At("RETURN"))
47+
private void initializeSmartLists(LevelAccessor level, MobCategory category, BlockPos pos, WeightedRandomList<MobSpawnSettings.SpawnerData> oldList, CallbackInfo ci) {
48+
this.view = oldList.unwrap();
49+
this.list = null;
50+
}
51+
52+
private void mfix$populateList() {
53+
if(this.list == null) {
54+
this.list = new ArrayList<>(this.view);
55+
this.view = Collections.unmodifiableList(this.list);
56+
}
57+
}
58+
59+
@Inject(method = {"addSpawnerData" }, at = @At("HEAD"), remap = false)
60+
private void populateList(MobSpawnSettings.SpawnerData data, CallbackInfo ci) {
61+
mfix$populateList();
62+
}
63+
64+
@Inject(method = {"removeSpawnerData" }, at = @At("HEAD"), remap = false)
65+
private void populateList(MobSpawnSettings.SpawnerData data, CallbackInfoReturnable<Boolean> cir) {
66+
mfix$populateList();
67+
}
68+
}

forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.embeddedt.modernfix.forge.classloading.ATInjector;
2626
import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList;
2727
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
28+
import org.embeddedt.modernfix.forge.config.NightConfigWatchThrottler;
2829
import org.embeddedt.modernfix.forge.init.ModernFixForge;
2930
import org.embeddedt.modernfix.forge.packet.PacketHandler;
3031
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
@@ -112,6 +113,7 @@ public void injectPlatformSpecificHacks() {
112113
}
113114

114115
NightConfigFixer.monitorFileWatcher();
116+
NightConfigWatchThrottler.throttle();
115117
}
116118

117119
public void applyASMTransformers(String mixinClassName, ClassNode targetClass) {

0 commit comments

Comments
 (0)