Merge remote-tracking branch 'origin/1.20' into 1.21
This commit is contained in:
commit
512a7e237c
|
|
@ -1,5 +1,6 @@
|
|||
package org.embeddedt.modernfix.blockstate;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -14,6 +15,7 @@ import java.util.*;
|
|||
*/
|
||||
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
|
||||
private final Map<Property<?>, Comparable<?>>[] keys;
|
||||
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
|
||||
private final Object[] values;
|
||||
private int usedSlots;
|
||||
public FakeStateMap(int numStates) {
|
||||
|
|
@ -34,22 +36,39 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
|||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
return getFastLookup().containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public S get(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
return getFastLookup().get(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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;
|
||||
values[usedSlots] = s;
|
||||
usedSlots++;
|
||||
|
|
@ -70,7 +89,7 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
for(int i = 0; i < this.keys.length; i++) {
|
||||
for(int i = 0; i < usedSlots; i++) {
|
||||
this.keys[i] = null;
|
||||
this.values[i] = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,23 +6,25 @@ import net.minecraft.world.level.block.state.StateHolder;
|
|||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
||||
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.Mixin;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
// This optimization requires FerriteCore to be worthwhile, otherwise the FakeStateMap degrades to hash internally
|
||||
@Mixin(StateDefinition.class)
|
||||
@RequiresMod("ferritecore")
|
||||
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
|
||||
@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)
|
||||
private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) {
|
||||
int numStates = 1;
|
||||
|
|
@ -31,11 +33,4 @@ public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
package org.embeddedt.modernfix.testing.util;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
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.
|
||||
*/
|
||||
|
|
@ -15,6 +20,15 @@ public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback
|
|||
public void beforeAll(ExtensionContext context) throws Exception {
|
||||
SharedConstants.tryDetectVersion();
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.neoforge.ModernFixConfig;
|
||||
import org.embeddedt.modernfix.neoforge.classloading.ModFileScanDataDeduplicator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -41,7 +40,6 @@ public class ModernFixForge {
|
|||
NeoForge.EVENT_BUS.register(new ModernFixClientForge(modContainer, modBus));
|
||||
}
|
||||
modContainer.registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
||||
ModFileScanDataDeduplicator.deduplicate();
|
||||
}
|
||||
|
||||
private void registerItems(RegisterEvent event) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user