Merge remote-tracking branch 'origin/1.18' into 1.19.2
This commit is contained in:
commit
6768662f1d
|
|
@ -2,9 +2,11 @@ package org.embeddedt.modernfix;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||||
|
|
@ -21,6 +23,8 @@ import net.minecraftforge.fml.loading.FMLConfig;
|
||||||
import net.minecraftforge.fml.loading.FMLLoader;
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
import net.minecraftforge.network.NetworkConstants;
|
import net.minecraftforge.network.NetworkConstants;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegisterEvent;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
@ -99,6 +103,7 @@ public class ModernFix {
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
MinecraftForge.EVENT_BUS.register(this);
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onLoadComplete);
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onLoadComplete);
|
||||||
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::registerItems);
|
||||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient()));
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient()));
|
||||||
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
||||||
|
|
@ -108,6 +113,17 @@ public class ModernFix {
|
||||||
ModFileScanDataDeduplicator.deduplicate();
|
ModFileScanDataDeduplicator.deduplicate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerItems(RegisterEvent event) {
|
||||||
|
if(Boolean.getBoolean("modernfix.largeRegistryTest")) {
|
||||||
|
event.register(ForgeRegistries.Keys.ITEMS, helper -> {
|
||||||
|
Item.Properties props = new Item.Properties();
|
||||||
|
for(int i = 0; i < 1000000; i++) {
|
||||||
|
helper.register(new ResourceLocation("modernfix", "item_" + i), new Item(props));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean dfuModPresent() {
|
private static boolean dfuModPresent() {
|
||||||
if(FMLConfig.isOptimizedDFUDisabled())
|
if(FMLConfig.isOptimizedDFUDisabled())
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import net.minecraft.Util;
|
||||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public class ModelLocationCache {
|
public class ModelLocationCache {
|
||||||
private static final LoadingCache<BlockState, ModelResourceLocation> locationCache = CacheBuilder.newBuilder()
|
private static final LoadingCache<BlockState, ModelResourceLocation> blockLocationCache = CacheBuilder.newBuilder()
|
||||||
.maximumSize(10000)
|
.maximumSize(10000)
|
||||||
.build(new CacheLoader<BlockState, ModelResourceLocation>() {
|
.build(new CacheLoader<BlockState, ModelResourceLocation>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -29,9 +30,26 @@ public class ModelLocationCache {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private static final LoadingCache<Item, ModelResourceLocation> itemLocationCache = CacheBuilder.newBuilder()
|
||||||
|
.maximumSize(10000)
|
||||||
|
.build(new CacheLoader<Item, ModelResourceLocation>() {
|
||||||
|
@Override
|
||||||
|
public ModelResourceLocation load(Item key) throws Exception {
|
||||||
|
return new ModelResourceLocation(Registry.ITEM.getKey(key), "inventory");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
public static ModelResourceLocation get(BlockState state) {
|
public static ModelResourceLocation get(BlockState state) {
|
||||||
try {
|
try {
|
||||||
return locationCache.get(state);
|
return blockLocationCache.get(state);
|
||||||
|
} catch(ExecutionException e) {
|
||||||
|
throw new RuntimeException(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelResourceLocation get(Item item) {
|
||||||
|
try {
|
||||||
|
return itemLocationCache.get(item);
|
||||||
} catch(ExecutionException e) {
|
} catch(ExecutionException e) {
|
||||||
throw new RuntimeException(e.getCause());
|
throw new RuntimeException(e.getCause());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.devenv;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistry;
|
||||||
|
import net.minecraftforge.registries.GameData;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(GameData.class)
|
||||||
|
public class GameDataMixin {
|
||||||
|
|
||||||
|
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/registries/ForgeRegistry;dump(Lnet/minecraft/resources/ResourceLocation;)V"))
|
||||||
|
private static void noDump(ForgeRegistry<?> reg, ResourceLocation id) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import net.minecraft.core.Holder;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraftforge.client.model.ForgeItemModelShaper;
|
import net.minecraftforge.client.model.ForgeItemModelShaper;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||||
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.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
|
@ -23,6 +24,8 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
||||||
super(arg);
|
super(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ModelResourceLocation SENTINEL = new ModelResourceLocation("modernfix", "sentinel");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Get the stored location for that item and meta, and get the model
|
* @reason Get the stored location for that item and meta, and get the model
|
||||||
* from that location from the model manager.
|
* from that location from the model manager.
|
||||||
|
|
@ -30,7 +33,11 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
||||||
@Overwrite
|
@Overwrite
|
||||||
@Override
|
@Override
|
||||||
public BakedModel getItemModel(Item item) {
|
public BakedModel getItemModel(Item item) {
|
||||||
ModelResourceLocation map = locations.get(ForgeRegistries.ITEMS.getDelegateOrThrow(item));
|
ModelResourceLocation map = locations.getOrDefault(ForgeRegistries.ITEMS.getDelegateOrThrow(item), SENTINEL);
|
||||||
|
if(map == SENTINEL) {
|
||||||
|
/* generate the appropriate location from our cache */
|
||||||
|
map = ModelLocationCache.get(item);
|
||||||
|
}
|
||||||
return map == null ? null : getModelManager().getModel(map);
|
return map == null ? null : getModelManager().getModel(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.ItemModelShaper;
|
||||||
|
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(ItemRenderer.class)
|
||||||
|
public class ItemRendererMixin {
|
||||||
|
/**
|
||||||
|
* Don't waste space putting all these locations into the cache, compute them on demand later.
|
||||||
|
*/
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemModelShaper;register(Lnet/minecraft/world/item/Item;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
|
||||||
|
private void skipDefaultRegistration(ItemModelShaper shaper, Item item, ModelResourceLocation mrl) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,8 @@ import com.google.common.collect.Sets;
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.math.Transformation;
|
import com.mojang.math.Transformation;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.color.block.BlockColors;
|
import net.minecraft.client.color.block.BlockColors;
|
||||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
|
|
@ -19,8 +21,11 @@ import net.minecraft.client.renderer.texture.AtlasSet;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
import net.minecraft.client.renderer.texture.TextureManager;
|
||||||
|
import net.minecraft.client.resources.ClientPackSource;
|
||||||
import net.minecraft.client.resources.model.*;
|
import net.minecraft.client.resources.model.*;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.*;
|
||||||
|
import net.minecraft.server.packs.resources.FallbackResourceManager;
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
|
|
@ -35,6 +40,8 @@ import net.minecraftforge.client.model.geometry.GeometryLoaderManager;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.fml.ModLoader;
|
import net.minecraftforge.fml.ModLoader;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.resource.DelegatingPackResources;
|
||||||
|
import net.minecraftforge.resource.PathPackResources;
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
import org.apache.commons.lang3.tuple.Triple;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||||
|
|
@ -57,6 +64,7 @@ import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
@ -196,12 +204,84 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int ERROR_THRESHOLD = 200;
|
||||||
|
|
||||||
|
private void logOrSuppressError(Object2IntOpenHashMap<String> suppressionMap, String type, ResourceLocation location, Throwable e) {
|
||||||
|
int numErrors;
|
||||||
|
synchronized (suppressionMap) {
|
||||||
|
numErrors = suppressionMap.computeInt(location.getNamespace(), (k, oldVal) -> (oldVal == null ? 1 : oldVal + 1));
|
||||||
|
}
|
||||||
|
if(numErrors <= ERROR_THRESHOLD)
|
||||||
|
ModernFix.LOGGER.error("Error reading {} {}: {}", type, location, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean trustedResourcePack(PackResources pack) {
|
||||||
|
return pack instanceof VanillaPackResources ||
|
||||||
|
pack instanceof PathPackResources ||
|
||||||
|
pack instanceof ClientPackSource ||
|
||||||
|
pack instanceof DelegatingPackResources ||
|
||||||
|
pack instanceof FolderPackResources ||
|
||||||
|
pack instanceof FilePackResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gatherAdditionalViaManualScan(List<PackResources> untrustedPacks, Set<ResourceLocation> knownLocations,
|
||||||
|
Collection<ResourceLocation> uncertainLocations, String filePrefix) {
|
||||||
|
if(untrustedPacks.size() > 0) {
|
||||||
|
/* Now make a fallback resource manager and use it on the remaining packs to see if they actually contain these files */
|
||||||
|
FallbackResourceManager frm = new FallbackResourceManager(PackType.CLIENT_RESOURCES, "dummy");
|
||||||
|
for (int i = untrustedPacks.size() - 1; i >= 0; i--) {
|
||||||
|
frm.push(untrustedPacks.get(i));
|
||||||
|
}
|
||||||
|
for (ResourceLocation blockstate : uncertainLocations) {
|
||||||
|
if (knownLocations.contains(blockstate))
|
||||||
|
continue; // don't check ones we know exist
|
||||||
|
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), filePrefix + blockstate.getPath() + ".json");
|
||||||
|
Optional<Resource> resource = frm.getResource(fileLocation);
|
||||||
|
if(resource.isPresent())
|
||||||
|
knownLocations.add(blockstate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all blockstate JSONs and model files, collect textures.
|
* Load all blockstate JSONs and model files, collect textures.
|
||||||
*/
|
*/
|
||||||
private void gatherModelMaterials(Set<Material> materialSet) {
|
private void gatherModelMaterials(Set<Material> materialSet) {
|
||||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> blockStateData = new ArrayList<>();
|
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> blockStateData = new ArrayList<>();
|
||||||
|
final Object2IntOpenHashMap<String> blockstateErrors = new Object2IntOpenHashMap<>();
|
||||||
|
/*
|
||||||
|
* First, gather all vanilla packs, and use listResources on them. This will allow us to (hopefully) avoid
|
||||||
|
* scanning most packs a lot.
|
||||||
|
*/
|
||||||
|
List<PackResources> allPackResources = new ArrayList<>(this.resourceManager.listPacks().collect(Collectors.toList()));
|
||||||
|
Collections.reverse(allPackResources);
|
||||||
|
ObjectOpenHashSet<ResourceLocation> allAvailableModels = new ObjectOpenHashSet<>(), allAvailableStates = new ObjectOpenHashSet<>();
|
||||||
|
allPackResources.removeIf(pack -> {
|
||||||
|
if(trustedResourcePack(pack)) {
|
||||||
|
for(String namespace : pack.getNamespaces(PackType.CLIENT_RESOURCES)) {
|
||||||
|
Collection<ResourceLocation> allBlockstates = pack.getResources(PackType.CLIENT_RESOURCES, namespace, "blockstates", p -> p.getPath().endsWith(".json"));
|
||||||
|
for(ResourceLocation blockstate : allBlockstates) {
|
||||||
|
allAvailableStates.add(new ResourceLocation(blockstate.getNamespace(), blockstate.getPath().replace("blockstates/", "").replace(".json", "")));
|
||||||
|
}
|
||||||
|
Collection<ResourceLocation> allModels = pack.getResources(PackType.CLIENT_RESOURCES, namespace, "models", p -> p.getPath().endsWith(".json"));
|
||||||
|
for(ResourceLocation blockstate : allModels) {
|
||||||
|
allAvailableModels.add(new ResourceLocation(blockstate.getNamespace(), blockstate.getPath().replace("models/", "").replace(".json", "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ModernFix.LOGGER.debug("Pack with class {} needs manual scan", pack.getClass().getName());
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
gatherAdditionalViaManualScan(allPackResources, allAvailableStates, blockStateFiles, "blockstates/");
|
||||||
|
// We now have a list of all blockstates known to exist. Delete anything that we don't have
|
||||||
|
blockStateFiles.retainAll(allAvailableStates);
|
||||||
|
allAvailableStates.clear();
|
||||||
|
allAvailableStates.trim();
|
||||||
|
|
||||||
|
|
||||||
for(ResourceLocation blockstate : blockStateFiles) {
|
for(ResourceLocation blockstate : blockStateFiles) {
|
||||||
blockStateData.add(CompletableFuture.supplyAsync(() -> {
|
blockStateData.add(CompletableFuture.supplyAsync(() -> {
|
||||||
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
|
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
|
||||||
|
|
@ -211,7 +291,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
JsonParser parser = new JsonParser();
|
JsonParser parser = new JsonParser();
|
||||||
return Pair.of(blockstate, parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)));
|
return Pair.of(blockstate, parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)));
|
||||||
} catch(IOException | JsonParseException e) {
|
} catch(IOException | JsonParseException e) {
|
||||||
ModernFix.LOGGER.error("Error reading blockstate {}: {}", blockstate, e);
|
logOrSuppressError(blockstateErrors, "blockstate", blockstate, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair.of(blockstate, null);
|
return Pair.of(blockstate, null);
|
||||||
|
|
@ -261,12 +341,25 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch(RuntimeException e) {
|
} catch(RuntimeException e) {
|
||||||
ModernFix.LOGGER.error("Error with blockstate {}: {}", pair.getFirst(), e);
|
logOrSuppressError(blockstateErrors, "blockstate", pair.getFirst(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
blockstateErrors.object2IntEntrySet().forEach(entry -> {
|
||||||
|
if(entry.getIntValue() > ERROR_THRESHOLD) {
|
||||||
|
ModernFix.LOGGER.error("Suppressed additional {} blockstate errors for domain {}", entry.getIntValue(), entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
blockstateErrors.clear();
|
||||||
blockStateData = null;
|
blockStateData = null;
|
||||||
|
|
||||||
|
/* figure out which models we should actually load */
|
||||||
|
gatherAdditionalViaManualScan(allPackResources, allAvailableModels, modelFiles, "models/");
|
||||||
|
modelFiles.retainAll(allAvailableModels);
|
||||||
|
allAvailableModels.clear();
|
||||||
|
allAvailableModels.trim();
|
||||||
|
|
||||||
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>();
|
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>();
|
||||||
basicModels.put(MISSING_MODEL_LOCATION, (BlockModel)missingModel);
|
basicModels.put(MISSING_MODEL_LOCATION, (BlockModel)missingModel);
|
||||||
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
|
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
|
||||||
|
|
@ -285,7 +378,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
JsonParser parser = new JsonParser();
|
JsonParser parser = new JsonParser();
|
||||||
return Pair.of(model, parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)));
|
return Pair.of(model, parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)));
|
||||||
} catch(IOException | JsonParseException e) {
|
} catch(IOException | JsonParseException e) {
|
||||||
ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e);
|
logOrSuppressError(blockstateErrors, "model", fileLocation, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair.of(fileLocation, null);
|
return Pair.of(fileLocation, null);
|
||||||
|
|
@ -306,12 +399,17 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} catch(Throwable e) {
|
} catch(Throwable e) {
|
||||||
ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e);
|
logOrSuppressError(blockstateErrors, "model", pair.getFirst(), e);
|
||||||
}
|
}
|
||||||
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
|
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
|
||||||
}
|
}
|
||||||
UVController.useDummyUv.set(Boolean.FALSE);
|
UVController.useDummyUv.set(Boolean.FALSE);
|
||||||
}
|
}
|
||||||
|
blockstateErrors.object2IntEntrySet().forEach(entry -> {
|
||||||
|
if(entry.getIntValue() > ERROR_THRESHOLD) {
|
||||||
|
ModernFix.LOGGER.error("Suppressed additional {} model errors for domain {}", entry.getIntValue(), entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
modelFiles = null;
|
modelFiles = null;
|
||||||
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> {
|
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> {
|
||||||
UnbakedModel m = basicModels.get(loc);
|
UnbakedModel m = basicModels.get(loc);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ 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.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import team.chisel.ctm.CTM;
|
import team.chisel.ctm.CTM;
|
||||||
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
||||||
|
|
@ -36,6 +37,11 @@ public abstract class TextureMetadataHandlerMixin {
|
||||||
MinecraftForge.EVENT_BUS.addListener(this::onDynamicModelBake);
|
MinecraftForge.EVENT_BUS.addListener(this::onDynamicModelBake);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "onModelBake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BakedModel;isCustomRenderer()Z"))
|
||||||
|
private boolean checkModelValid(BakedModel model) {
|
||||||
|
return model == null || model.isCustomRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
public void onDynamicModelBake(DynamicModelBakeEvent event) {
|
public void onDynamicModelBake(DynamicModelBakeEvent event) {
|
||||||
UnbakedModel rootModel = event.getUnbakedModel();
|
UnbakedModel rootModel = event.getUnbakedModel();
|
||||||
BakedModel baked = event.getModel();
|
BakedModel baked = event.getModel();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,29 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.fast_registry_validation;
|
package org.embeddedt.modernfix.mixin.perf.fast_registry_validation;
|
||||||
|
|
||||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
|
import com.google.common.collect.BiMap;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.registries.ForgeRegistry;
|
import net.minecraftforge.registries.ForgeRegistry;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.Marker;
|
||||||
|
import org.embeddedt.modernfix.registry.FastForgeRegistry;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
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.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Mixin(value = ForgeRegistry.class, remap = false)
|
@Mixin(value = ForgeRegistry.class, remap = false)
|
||||||
public class ForgeRegistryMixin {
|
public class ForgeRegistryMixin<V> {
|
||||||
private static Method bitSetTrimMethod = null;
|
private static Method bitSetTrimMethod = null;
|
||||||
private static boolean bitSetTrimMethodRetrieved = false;
|
private static boolean bitSetTrimMethodRetrieved = false;
|
||||||
|
|
||||||
|
|
@ -25,4 +39,71 @@ public class ForgeRegistryMixin {
|
||||||
}
|
}
|
||||||
return bitSetTrimMethod;
|
return bitSetTrimMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int expectedNextBit = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avoid calling nextClearBit and scanning the whole registry for every block registration.
|
||||||
|
*/
|
||||||
|
@Redirect(method = "add(ILnet/minecraft/resources/ResourceLocation;Ljava/lang/Object;Ljava/lang/String;)I", at = @At(value = "INVOKE", target = "Ljava/util/BitSet;nextClearBit(I)I"))
|
||||||
|
private int useCachedBit(BitSet availabilityMap, int minimum) {
|
||||||
|
int bit = availabilityMap.nextClearBit(expectedNextBit != -1 ? expectedNextBit : minimum);
|
||||||
|
expectedNextBit = bit + 1;
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = { "sync", "clear", "block" }, at = @At("HEAD"))
|
||||||
|
private void clearBitCache(CallbackInfo ci) {
|
||||||
|
expectedNextBit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "createAndAddDummy", at = @At(value = "INVOKE", target = "Ljava/util/BitSet;clear(I)V"))
|
||||||
|
private void clearBitCache2(CallbackInfo ci) {
|
||||||
|
expectedNextBit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "add(ILnet/minecraft/resources/ResourceLocation;Ljava/lang/Object;Ljava/lang/String;)I", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;trace(Lorg/apache/logging/log4j/Marker;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V"))
|
||||||
|
private void skipTrace(Logger logger, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable private BiMap<Integer, V> ids;
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable private BiMap<ResourceKey<V>, V> keys;
|
||||||
|
|
||||||
|
@Shadow @Final private ResourceKey<Registry<V>> key;
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable private BiMap<ResourceLocation, V> names;
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable private BiMap owners;
|
||||||
|
|
||||||
|
private FastForgeRegistry<V> fastRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following code replaces the Forge HashBiMaps with a more efficient data structure based around
|
||||||
|
* an array list for IDs and one HashMap going from value -> information.
|
||||||
|
*/
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void replaceBackingMaps(CallbackInfo ci) {
|
||||||
|
this.fastRegistry = new FastForgeRegistry<>(this.key);
|
||||||
|
this.ids = fastRegistry.getIds();
|
||||||
|
this.keys = fastRegistry.getKeys();
|
||||||
|
this.names = fastRegistry.getNames();
|
||||||
|
this.owners = fastRegistry.getOwners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "freeze", at = @At("RETURN"))
|
||||||
|
private void optimizeRegistry(CallbackInfo ci) {
|
||||||
|
this.fastRegistry.optimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "sync", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;clear()V"))
|
||||||
|
private void clearBiMap(BiMap map) {
|
||||||
|
if(map == this.owners) {
|
||||||
|
this.fastRegistry.clear();
|
||||||
|
} else if(map == this.keys || map == this.names || map == this.ids) {
|
||||||
|
// do nothing, the registry is faster at clearing everything at once
|
||||||
|
} else
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.fast_registry_validation;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistry;
|
||||||
|
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.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Mixin(ForgeRegistry.Snapshot.class)
|
||||||
|
public class ForgeRegistrySnapshotMixin {
|
||||||
|
@Shadow @Final @Mutable public Map<ResourceLocation, Integer> ids;
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable public Set<ResourceLocation> dummied;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The only good reason to use tree maps here is to keep the order the same. But we are tracking IDs
|
||||||
|
* anyway so order shouldn't matter. We replace the maps that will be most used.
|
||||||
|
*/
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void replaceSnapshotMaps(CallbackInfo ci) {
|
||||||
|
this.ids = new Object2ObjectOpenHashMap<>();
|
||||||
|
this.dummied = new ObjectOpenHashSet<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.fast_registry_validation;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(ResourceKey.class)
|
||||||
|
public class ResourceKeyMixin<T> {
|
||||||
|
private static Map<ResourceLocation, Map<ResourceLocation, ResourceKey<?>>> INTERNING_MAP = new Object2ObjectOpenHashMap<>();
|
||||||
|
@Inject(method = "create(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceKey;", at = @At("HEAD"), cancellable = true)
|
||||||
|
private static <T> void createEfficient(ResourceLocation parent, ResourceLocation location, CallbackInfoReturnable<ResourceKey<T>> cir) {
|
||||||
|
synchronized (ResourceKey.class) {
|
||||||
|
Map<ResourceLocation, ResourceKey<?>> keys = INTERNING_MAP.computeIfAbsent(parent, k -> new Object2ObjectOpenHashMap<>());
|
||||||
|
ResourceKey<?> key = keys.get(location);
|
||||||
|
if(key == null) {
|
||||||
|
key = new ResourceKey<>(parent, location);
|
||||||
|
keys.put(location, key);
|
||||||
|
}
|
||||||
|
cir.setReturnValue((ResourceKey<T>)key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,602 @@
|
||||||
|
package org.embeddedt.modernfix.registry;
|
||||||
|
|
||||||
|
import com.google.common.collect.BiMap;
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class FastForgeRegistry<V> {
|
||||||
|
private final BiMap<Integer, V> ids;
|
||||||
|
private final DataFieldBiMap<ResourceLocation> names;
|
||||||
|
private final DataFieldBiMap<ResourceKey<V>> keys;
|
||||||
|
private final DataFieldBiMap<?> owners;
|
||||||
|
private final ResourceKey<Registry<V>> registryKey;
|
||||||
|
|
||||||
|
private final ObjectArrayList<V> valuesById;
|
||||||
|
private final Object2ObjectOpenHashMap<V, RegistryValueData> infoByValue;
|
||||||
|
|
||||||
|
private void storeId(V value, int id) {
|
||||||
|
RegistryValueData pair = infoByValue.computeIfAbsent(value, k -> new RegistryValueData());
|
||||||
|
pair.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInfoPairAndClearIfNull(V v, Consumer<RegistryValueData> consumer) {
|
||||||
|
infoByValue.compute(v, (oldValue, oldPair) -> {
|
||||||
|
if(oldPair == null)
|
||||||
|
oldPair = new RegistryValueData();
|
||||||
|
consumer.accept(oldPair);
|
||||||
|
if(oldPair.isEmpty())
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return oldPair;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureArrayCanFitId(int id) {
|
||||||
|
int desiredSize = id + 1;
|
||||||
|
while(valuesById.size() < desiredSize) {
|
||||||
|
valuesById.add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.infoByValue.clear();
|
||||||
|
for(int i = 0; i < this.valuesById.size(); i++) {
|
||||||
|
this.valuesById.set(i, null);
|
||||||
|
}
|
||||||
|
this.names.clearUnsafe();
|
||||||
|
this.keys.clearUnsafe();
|
||||||
|
this.owners.clearUnsafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastForgeRegistry(ResourceKey<Registry<V>> registryKey) {
|
||||||
|
this.registryKey = registryKey;
|
||||||
|
this.valuesById = new ObjectArrayList<>();
|
||||||
|
this.infoByValue = new Object2ObjectOpenHashMap<>();
|
||||||
|
this.keys = new DataFieldBiMap<>(p -> (ResourceKey<V>) p.key, (p, k) -> p.key = k);
|
||||||
|
this.owners = new DataFieldBiMap<>(p -> p.overrideOwner, (p, k) -> p.overrideOwner = k);
|
||||||
|
this.names = new DataFieldBiMap<>(p -> p.location, (p, l) -> p.location = l);
|
||||||
|
// IDs require a specialized implementation, as we back the K->V direction with an array
|
||||||
|
this.ids = new BiMap<Integer, V>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public V put(@Nullable Integer key, @Nullable V value) {
|
||||||
|
RegistryValueData data = infoByValue.get(value);
|
||||||
|
int unboxedKey = key;
|
||||||
|
if(data != null && data.id != -1 && data.id != unboxedKey)
|
||||||
|
throw new IllegalArgumentException("Existing mapping for ID " + data.id + " value " + value + " when new ID " + unboxedKey + " was requested");
|
||||||
|
ensureArrayCanFitId(unboxedKey);
|
||||||
|
V oldValue = valuesById.set(unboxedKey, value);
|
||||||
|
storeId(value, unboxedKey);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public V forcePut(@Nullable Integer key, @Nullable V value) {
|
||||||
|
ensureArrayCanFitId(key);
|
||||||
|
V oldValue = valuesById.set(key, value);
|
||||||
|
if(oldValue != null) {
|
||||||
|
updateInfoPairAndClearIfNull(oldValue, pair -> pair.id = -1);
|
||||||
|
}
|
||||||
|
storeId(value, key);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends Integer, ? extends V> map) {
|
||||||
|
map.forEach(this::put);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> values() {
|
||||||
|
return Collections.unmodifiableSet(infoByValue.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiMap<V, Integer> inverse() {
|
||||||
|
return new BiMap<V, Integer>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Integer put(@Nullable V key, @Nullable Integer value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Integer forcePut(@Nullable V key, @Nullable Integer value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends V, ? extends Integer> map) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> values() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiMap<Integer, V> inverse() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer get(Object key) {
|
||||||
|
RegistryValueData pair = infoByValue.get(key);
|
||||||
|
if(pair == null)
|
||||||
|
return null;
|
||||||
|
return pair.id == -1 ? null : pair.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer remove(Object key) {
|
||||||
|
RegistryValueData pair = infoByValue.get(key);
|
||||||
|
if(pair == null)
|
||||||
|
return null;
|
||||||
|
int id = pair.id;
|
||||||
|
if(id != -1)
|
||||||
|
valuesById.set(id, null);
|
||||||
|
updateInfoPairAndClearIfNull((V)key, p -> p.id = -1);
|
||||||
|
return id == -1 ? null : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<V> keySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<V, Integer>> entrySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return infoByValue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object key) {
|
||||||
|
int id = (Integer)key;
|
||||||
|
if(id < 0 || id >= valuesById.size())
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return valuesById.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
valuesById.clear();
|
||||||
|
infoByValue.values().removeIf(pair -> {
|
||||||
|
pair.id = -1;
|
||||||
|
return pair.isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Integer> keySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<Integer, V>> entrySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(BiConsumer<? super Integer, ? super V> action) {
|
||||||
|
for(int i = 0 ; i < valuesById.size(); i++) {
|
||||||
|
V val = valuesById.get(i);
|
||||||
|
if(val != null)
|
||||||
|
action.accept(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void optimize() {
|
||||||
|
this.keys.optimize();
|
||||||
|
this.owners.optimize();
|
||||||
|
this.names.optimize();
|
||||||
|
this.infoByValue.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BiMap<Integer, V> getIds() {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BiMap<ResourceKey<V>, V> getKeys() {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BiMap<ResourceLocation, V> getNames() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataFieldBiMap<?> getOwners() {
|
||||||
|
return owners;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom BiMap implementation that uses one internal hash map for the K->V direction, and the shared global
|
||||||
|
* information hash map for the V->K direction.
|
||||||
|
*/
|
||||||
|
class DataFieldBiMap<K> implements BiMap<K, V> {
|
||||||
|
public final Object2ObjectOpenHashMap<K, V> valuesByKey = new Object2ObjectOpenHashMap<>();
|
||||||
|
private final Function<RegistryValueData, K> getter;
|
||||||
|
private final BiConsumer<RegistryValueData, K> setter;
|
||||||
|
|
||||||
|
public DataFieldBiMap(Function<RegistryValueData, K> getter, BiConsumer<RegistryValueData, K> setter) {
|
||||||
|
this.getter = getter;
|
||||||
|
this.setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void optimize() {
|
||||||
|
this.valuesByKey.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearUnsafe() {
|
||||||
|
this.valuesByKey.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private V forcePut(@Nullable K key, @Nullable V value, boolean throwOnExisting) {
|
||||||
|
if(throwOnExisting) {
|
||||||
|
RegistryValueData dataForValue = infoByValue.get(value);
|
||||||
|
// null check could be wrong if null is a valid value but doesn't matter in Forge's use
|
||||||
|
if(dataForValue != null && getter.apply(dataForValue) != null && !Objects.equals(getter.apply(dataForValue), key)) {
|
||||||
|
throw new IllegalArgumentException("Existing mapping for key " + key + " value " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
V oldValue = valuesByKey.put(key, value);
|
||||||
|
if(oldValue != null) {
|
||||||
|
updateInfoPairAndClearIfNull(oldValue, p -> setter.accept(p, null));
|
||||||
|
}
|
||||||
|
updateInfoPairAndClearIfNull(value, p -> setter.accept(p, key));
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public V put(@Nullable K key, @Nullable V value) {
|
||||||
|
return forcePut(key, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public V forcePut(@Nullable K key, @Nullable V value) {
|
||||||
|
return forcePut(key, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
map.forEach(this::put);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<V> values() {
|
||||||
|
return Collections.unmodifiableSet(infoByValue.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFieldBiMapInverse<K> inverse = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiMap<V, K> inverse() {
|
||||||
|
if(inverse == null)
|
||||||
|
inverse = new DataFieldBiMapInverse<>(this);
|
||||||
|
return inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return valuesByKey.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return valuesByKey.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return valuesByKey.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return infoByValue.containsKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object key) {
|
||||||
|
return valuesByKey.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
V value = get(key);
|
||||||
|
if(value == null)
|
||||||
|
return null;
|
||||||
|
inverse().remove(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
valuesByKey.values().forEach(v -> updateInfoPairAndClearIfNull(v, p -> p.key = null));
|
||||||
|
valuesByKey.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
return Collections.unmodifiableSet(valuesByKey.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Map.Entry<K, V>> entrySet() {
|
||||||
|
return Collections.unmodifiableSet(valuesByKey.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataFieldBiMapInverse<K> implements BiMap<V, K> {
|
||||||
|
private final DataFieldBiMap<K> forward;
|
||||||
|
|
||||||
|
public DataFieldBiMapInverse(DataFieldBiMap<K> forward) {
|
||||||
|
this.forward = forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public K put(@Nullable V key, @Nullable K value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public K forcePut(@Nullable V key, @Nullable K value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends V, ? extends K> map) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> values() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiMap<K, V> inverse() {
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return infoByValue.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return forward.valuesByKey.containsKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K get(Object key) {
|
||||||
|
RegistryValueData pair = infoByValue.get(key);
|
||||||
|
if(pair == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return forward.getter.apply(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K remove(Object key) {
|
||||||
|
RegistryValueData pair = infoByValue.get(key);
|
||||||
|
if(pair == null)
|
||||||
|
return null;
|
||||||
|
else {
|
||||||
|
K rk = forward.getter.apply(pair);
|
||||||
|
forward.valuesByKey.remove(rk);
|
||||||
|
updateInfoPairAndClearIfNull((V)key, p -> forward.setter.accept(p, null));
|
||||||
|
return rk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<V> keySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<V, K>> entrySet() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FastForgeRegistryLocationSet implements Set<ResourceLocation> {
|
||||||
|
private final Set<ResourceKey<V>> backingSet;
|
||||||
|
|
||||||
|
public FastForgeRegistryLocationSet(Set<ResourceKey<V>> backingSet) {
|
||||||
|
this.backingSet = backingSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return backingSet.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return backingSet.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return backingSet.contains(ResourceKey.create(FastForgeRegistry.this.registryKey, (ResourceLocation)o));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<ResourceLocation> iterator() {
|
||||||
|
return Iterators.transform(backingSet.iterator(), ResourceKey::location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
Object[] keyArray = backingSet.toArray();
|
||||||
|
for(int i = 0; i < keyArray.length; i++) {
|
||||||
|
keyArray[i] = ((ResourceKey<V>)keyArray[i]).location();
|
||||||
|
}
|
||||||
|
return keyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(@NotNull T[] a) {
|
||||||
|
Object[] keyArray = backingSet.toArray();
|
||||||
|
T[] finalArray = Arrays.copyOf(a, keyArray.length);
|
||||||
|
for(int i = 0; i < keyArray.length; i++) {
|
||||||
|
finalArray[i] = (T)((ResourceKey<V>)keyArray[i]).location();
|
||||||
|
}
|
||||||
|
return finalArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(ResourceLocation resourceLocation) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(@NotNull Collection<?> c) {
|
||||||
|
for(Object o : c) {
|
||||||
|
if(!contains(o))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(@NotNull Collection<? extends ResourceLocation> c) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(@NotNull Collection<?> c) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(@NotNull Collection<?> c) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RegistryValueData {
|
||||||
|
public ResourceKey<?> key;
|
||||||
|
public ResourceLocation location;
|
||||||
|
public int id = -1;
|
||||||
|
public Object overrideOwner;
|
||||||
|
|
||||||
|
boolean isEmpty() {
|
||||||
|
return key == null && location == null && id == -1 && overrideOwner == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,30 +25,30 @@ public class CachedResourcePath {
|
||||||
private static final String[] NO_PREFIX = new String[0];
|
private static final String[] NO_PREFIX = new String[0];
|
||||||
|
|
||||||
public CachedResourcePath(Path path) {
|
public CachedResourcePath(Path path) {
|
||||||
this(NO_PREFIX, path, path.getNameCount());
|
this(NO_PREFIX, path, path.getNameCount(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedResourcePath(String s) {
|
public CachedResourcePath(String s) {
|
||||||
// normalize so we can guarantee there are no empty sections
|
// normalize so we can guarantee there are no empty sections
|
||||||
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)));
|
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection) {
|
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection, boolean intern) {
|
||||||
this(prefixElements, collection, collection.size());
|
this(prefixElements, collection, collection.size(), intern);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count) {
|
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count, boolean intern) {
|
||||||
String[] components = new String[prefixElements.length + count];
|
String[] components = new String[prefixElements.length + count];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(i < prefixElements.length) {
|
while(i < prefixElements.length) {
|
||||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(prefixElements[i]) : prefixElements[i];
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
for(Object component : path) {
|
for(Object component : path) {
|
||||||
String s = component.toString();
|
String s = component.toString();
|
||||||
if(s.length() == 0)
|
if(s.length() == 0)
|
||||||
continue;
|
continue;
|
||||||
components[i] = PATH_COMPONENT_INTERNER.intern(s);
|
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(s) : s;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
pathComponents = components;
|
pathComponents = components;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,8 @@ public class CanonizingStringMap<T> implements Map<String, T> {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<String> keySet() {
|
public Set<String> keySet() {
|
||||||
return Collections.unmodifiableSet(this.backingMap.keySet());
|
// has to be modifiable because mods (cough, Tinkers) use it to clear the tag
|
||||||
|
return this.backingMap.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,5 @@ public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor
|
||||||
public net.minecraft.nbt.CompoundTag <init>(Ljava/util/Map;)V # <init>
|
public net.minecraft.nbt.CompoundTag <init>(Ljava/util/Map;)V # <init>
|
||||||
public net.minecraft.client.renderer.texture.TextureAtlasSprite <init>(Lnet/minecraft/client/renderer/texture/TextureAtlas;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIIILcom/mojang/blaze3d/platform/NativeImage;)V # <init>
|
public net.minecraft.client.renderer.texture.TextureAtlasSprite <init>(Lnet/minecraft/client/renderer/texture/TextureAtlas;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIIILcom/mojang/blaze3d/platform/NativeImage;)V # <init>
|
||||||
public net.minecraft.server.level.DistanceManager m_140792_(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V # addTicket
|
public net.minecraft.server.level.DistanceManager m_140792_(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V # addTicket
|
||||||
public net.minecraft.server.level.ChunkMap$DistanceManager
|
public net.minecraft.server.level.ChunkMap$DistanceManager
|
||||||
|
public net.minecraft.resources.ResourceKey <init>(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,12 @@
|
||||||
"feature.branding.BrandingControlMixin",
|
"feature.branding.BrandingControlMixin",
|
||||||
"feature.direct_stack_trace.CrashReportMixin",
|
"feature.direct_stack_trace.CrashReportMixin",
|
||||||
"perf.nbt_memory_usage.CompoundTagMixin",
|
"perf.nbt_memory_usage.CompoundTagMixin",
|
||||||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
|
||||||
"perf.kubejs.RecipeEventJSMixin",
|
"perf.kubejs.RecipeEventJSMixin",
|
||||||
"perf.tag_id_caching.TagEntryMixin",
|
"perf.tag_id_caching.TagEntryMixin",
|
||||||
"perf.tag_id_caching.TagOrElementLocationMixin",
|
"perf.tag_id_caching.TagOrElementLocationMixin",
|
||||||
|
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||||
|
"perf.fast_registry_validation.ForgeRegistrySnapshotMixin",
|
||||||
|
"perf.fast_registry_validation.ResourceKeyMixin",
|
||||||
"perf.cache_strongholds.ChunkGeneratorMixin",
|
"perf.cache_strongholds.ChunkGeneratorMixin",
|
||||||
"perf.cache_upgraded_structures.StructureManagerMixin",
|
"perf.cache_upgraded_structures.StructureManagerMixin",
|
||||||
"perf.cache_strongholds.ServerLevelMixin",
|
"perf.cache_strongholds.ServerLevelMixin",
|
||||||
|
|
@ -47,7 +49,8 @@
|
||||||
"perf.compress_blockstate.BlockBehaviourMixin",
|
"perf.compress_blockstate.BlockBehaviourMixin",
|
||||||
"perf.dedup_blockstate_flattening_map.BlockStateDataMixin",
|
"perf.dedup_blockstate_flattening_map.BlockStateDataMixin",
|
||||||
"perf.dedup_blockstate_flattening_map.ChunkPalettedStorageFixMixin",
|
"perf.dedup_blockstate_flattening_map.ChunkPalettedStorageFixMixin",
|
||||||
"devenv.MinecraftServerMixin"
|
"devenv.MinecraftServerMixin",
|
||||||
|
"devenv.GameDataMixin"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"core.MinecraftMixin",
|
"core.MinecraftMixin",
|
||||||
|
|
@ -61,6 +64,7 @@
|
||||||
"perf.dynamic_resources.BlockElementFaceDeserializerMixin",
|
"perf.dynamic_resources.BlockElementFaceDeserializerMixin",
|
||||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||||
|
"perf.dynamic_resources.ItemRendererMixin",
|
||||||
"perf.dynamic_resources.ModelBakeryMixin",
|
"perf.dynamic_resources.ModelBakeryMixin",
|
||||||
"perf.dynamic_resources.ae2.RegistrationMixin",
|
"perf.dynamic_resources.ae2.RegistrationMixin",
|
||||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user