Merge remote-tracking branch 'origin/1.20' into 1.21

This commit is contained in:
embeddedt 2024-08-10 19:14:41 -04:00
commit 512a7e237c
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
7 changed files with 104 additions and 106 deletions

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.blockstate; package org.embeddedt.modernfix.blockstate;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Property;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -14,6 +15,7 @@ import java.util.*;
*/ */
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> { public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
private final Map<Property<?>, Comparable<?>>[] keys; private final Map<Property<?>, Comparable<?>>[] keys;
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
private final Object[] values; private final Object[] values;
private int usedSlots; private int usedSlots;
public FakeStateMap(int numStates) { public FakeStateMap(int numStates) {
@ -34,22 +36,39 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
@Override @Override
public boolean containsKey(Object o) { public boolean containsKey(Object o) {
throw new UnsupportedOperationException(); return getFastLookup().containsKey(o);
} }
@Override @Override
public boolean containsValue(Object o) { public boolean containsValue(Object o) {
throw new UnsupportedOperationException(); return getFastLookup().containsValue(o);
}
@SuppressWarnings("unchecked")
private Map<Map<Property<?>, Comparable<?>>, S> getFastLookup() {
if(fastLookup == null) {
var map = new Object2ObjectOpenHashMap<Map<Property<?>, Comparable<?>>, S>(usedSlots);
Map<Property<?>, Comparable<?>>[] keys = this.keys;
Object[] values = this.values;
for(int i = 0; i < usedSlots; i++) {
map.put(keys[i], (S)values[i]);
}
fastLookup = map;
}
return fastLookup;
} }
@Override @Override
public S get(Object o) { public S get(Object o) {
throw new UnsupportedOperationException(); return getFastLookup().get(o);
} }
@Nullable @Nullable
@Override @Override
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) { public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
if(fastLookup != null) {
throw new IllegalStateException("Cannot populate map after fast lookup is built");
}
keys[usedSlots] = propertyComparableMap; keys[usedSlots] = propertyComparableMap;
values[usedSlots] = s; values[usedSlots] = s;
usedSlots++; usedSlots++;
@ -70,7 +89,7 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
@Override @Override
public void clear() { public void clear() {
for(int i = 0; i < this.keys.length; i++) { for(int i = 0; i < usedSlots; i++) {
this.keys[i] = null; this.keys[i] = null;
this.values[i] = null; this.values[i] = null;
} }

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.common.mixin.perf.chunk_meshing;
import net.minecraft.client.renderer.chunk.SectionCompiler;
import net.minecraft.core.BlockPos;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.util.blockpos.SectionBlockPosIterator;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = SectionCompiler.class, priority = 2000)
@ClientOnlyMixin
public class RebuildTaskMixin {
/**
* @author embeddedt
* @reason Use a much faster iterator implementation than vanilla's Guava-based one.
*/
@Redirect(method = "compile", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;betweenClosed(Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/BlockPos;)Ljava/lang/Iterable;"))
private Iterable<BlockPos> fastBetweenClosed(BlockPos firstPos, BlockPos secondPos) {
return () -> new SectionBlockPosIterator(firstPos);
}
}

View File

@ -6,23 +6,25 @@ import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Property;
import org.embeddedt.modernfix.annotation.RequiresMod; import org.embeddedt.modernfix.annotation.RequiresMod;
import org.embeddedt.modernfix.blockstate.FakeStateMap; import org.embeddedt.modernfix.blockstate.FakeStateMap;
import org.embeddedt.modernfix.blockstate.FerriteCorePostProcess;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map; import java.util.Map;
// This optimization requires FerriteCore to be worthwhile, otherwise the FakeStateMap degrades to hash internally
@Mixin(StateDefinition.class) @Mixin(StateDefinition.class)
@RequiresMod("ferritecore") @RequiresMod("ferritecore")
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> { public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
@Shadow @Final private ImmutableSortedMap<String, Property<?>> propertiesByName; @Shadow @Final private ImmutableSortedMap<String, Property<?>> propertiesByName;
/**
* @author embeddedt
* @reason write states into a custom array map for fast iteration by FerriteCore, no need to waste time hashing
* and growing
*/
@ModifyVariable(method = "<init>", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8) @ModifyVariable(method = "<init>", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8)
private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) { private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) {
int numStates = 1; int numStates = 1;
@ -31,11 +33,4 @@ public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
} }
return new FakeStateMap<>(numStates); return new FakeStateMap<>(numStates);
} }
@Inject(method = "<init>", at = @At("TAIL"))
private void postProcess(CallbackInfo ci) {
// keep in dev only until upstream FC releases
if(ModernFixPlatformHooks.INSTANCE.isDevEnv())
FerriteCorePostProcess.postProcess((StateDefinition<O, S>)(Object)this);
}
} }

View File

@ -0,0 +1,39 @@
package org.embeddedt.modernfix.util.blockpos;
import net.minecraft.core.BlockPos;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SectionBlockPosIterator implements Iterator<BlockPos> {
private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
private int index = 0;
private final int baseX, baseY, baseZ;
public SectionBlockPosIterator(int baseX, int baseY, int baseZ) {
this.baseX = baseX;
this.baseY = baseY;
this.baseZ = baseZ;
}
public SectionBlockPosIterator(BlockPos pos) {
this(pos.getX(), pos.getY(), pos.getZ());
}
@Override
public boolean hasNext() {
return index < 4096;
}
@Override
public BlockPos next() {
int i = index;
if (i >= 4096) {
throw new NoSuchElementException();
}
index = i + 1;
var pos = this.pos;
pos.set(this.baseX + (i & 15), this.baseY + ((i >> 8) & 15), this.baseZ + ((i >> 4) & 15));
return pos;
}
}

View File

@ -1,12 +1,17 @@
package org.embeddedt.modernfix.testing.util; package org.embeddedt.modernfix.testing.util;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.Bootstrap; import net.minecraft.server.Bootstrap;
import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import java.lang.reflect.Field;
import java.util.IdentityHashMap;
/** /**
* Simple extension to run vanilla bootstrap, inspired by AE2. * Simple extension to run vanilla bootstrap, inspired by AE2.
*/ */
@ -15,6 +20,15 @@ public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback
public void beforeAll(ExtensionContext context) throws Exception { public void beforeAll(ExtensionContext context) throws Exception {
SharedConstants.tryDetectVersion(); SharedConstants.tryDetectVersion();
Bootstrap.bootStrap(); Bootstrap.bootStrap();
// Allow blocks to be created in tests
Field field = MappedRegistry.class.getDeclaredField("unregisteredIntrusiveHolders");
field.setAccessible(true);
if(field.get(BuiltInRegistries.BLOCK) == null) {
field.set(BuiltInRegistries.BLOCK, new IdentityHashMap<>());
field = MappedRegistry.class.getDeclaredField("frozen");
field.setAccessible(true);
field.setBoolean(BuiltInRegistries.BLOCK, false);
}
} }
@Override @Override

View File

@ -1,89 +0,0 @@
package org.embeddedt.modernfix.neoforge.classloading;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import net.neoforged.fml.ModList;
import net.neoforged.neoforgespi.language.ModFileScanData;
import net.neoforged.neoforgespi.locating.IModFile;
import org.objectweb.asm.Type;
import java.lang.reflect.Field;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ModFileScanDataDeduplicator {
private final Interner<Type> typeInterner = Interners.newStrongInterner();
private final Function<Type, Type> internerFn = type -> type != null ? typeInterner.intern(type) : null;
private static Field classClazzField, parentField, interfacesField, annotationClazzField, annotationTypeField;
private static final boolean reflectionSuccessful;
static {
boolean success = false;
try {
classClazzField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
classClazzField.setAccessible(true);
parentField = ModFileScanData.ClassData.class.getDeclaredField("parent");
parentField.setAccessible(true);
interfacesField = ModFileScanData.ClassData.class.getDeclaredField("interfaces");
interfacesField.setAccessible(true);
annotationClazzField = ModFileScanData.AnnotationData.class.getDeclaredField("clazz");
annotationClazzField.setAccessible(true);
annotationTypeField = ModFileScanData.AnnotationData.class.getDeclaredField("annotationType");
annotationTypeField.setAccessible(true);
success = true;
} catch(ReflectiveOperationException | RuntimeException e) {
}
reflectionSuccessful = success;
}
ModFileScanDataDeduplicator() {
}
private void runDeduplication() {
ModList.get().forEachModFile(this::deduplicateFile);
}
private void deduplicateFile(IModFile file) {
ModFileScanData data = file.getScanResult();
if(data != null) {
data.getClasses().forEach(this::deduplicateClass);
data.getAnnotations().forEach(this::deduplicateAnnotation);
}
}
private void deduplicateClass(ModFileScanData.ClassData data) {
try {
Type type = (Type)classClazzField.get(data);
type = internerFn.apply(type);
classClazzField.set(data, type);
type = (Type)parentField.get(data);
type = internerFn.apply(type);
parentField.set(data, type);
Set<Type> types = (Set<Type>)interfacesField.get(data);
types = types.stream().map(internerFn).collect(Collectors.toSet());
interfacesField.set(data, types);
} catch(ReflectiveOperationException e) {
}
}
private void deduplicateAnnotation(ModFileScanData.AnnotationData data) {
try {
Type type = (Type)annotationClazzField.get(data);
type = internerFn.apply(type);
annotationClazzField.set(data, type);
type = (Type)annotationTypeField.get(data);
type = internerFn.apply(type);
annotationTypeField.set(data, type);
} catch(ReflectiveOperationException e) {
}
}
public static void deduplicate() {
if(!reflectionSuccessful)
return;
new ModFileScanDataDeduplicator().runDeduplication();
}
}

View File

@ -22,7 +22,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.neoforge.ModernFixConfig; import org.embeddedt.modernfix.neoforge.ModernFixConfig;
import org.embeddedt.modernfix.neoforge.classloading.ModFileScanDataDeduplicator;
import java.util.List; import java.util.List;
@ -41,7 +40,6 @@ public class ModernFixForge {
NeoForge.EVENT_BUS.register(new ModernFixClientForge(modContainer, modBus)); NeoForge.EVENT_BUS.register(new ModernFixClientForge(modContainer, modBus));
} }
modContainer.registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG); modContainer.registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
ModFileScanDataDeduplicator.deduplicate();
} }
private void registerItems(RegisterEvent event) { private void registerItems(RegisterEvent event) {