Merge remote-tracking branch 'origin/main' into 1.18
This commit is contained in:
commit
275266c85f
|
|
@ -5,10 +5,12 @@ import net.minecraft.Util;
|
|||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.event.server.ServerStoppedEvent;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.*;
|
||||
|
|
@ -20,6 +22,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
|||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.network.NetworkConstants;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
|
@ -98,6 +101,7 @@ public class ModernFix {
|
|||
MinecraftForge.EVENT_BUS.register(this);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onLoadComplete);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addGenericListener(Item.class, this::registerItems);
|
||||
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().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
||||
|
|
@ -107,6 +111,15 @@ public class ModernFix {
|
|||
ModFileScanDataDeduplicator.deduplicate();
|
||||
}
|
||||
|
||||
private void registerItems(RegistryEvent<Item> event) {
|
||||
if(Boolean.getBoolean("modernfix.largeRegistryTest")) {
|
||||
Item.Properties props = new Item.Properties();
|
||||
for(int i = 0; i < 1000000; i++) {
|
||||
ForgeRegistries.ITEMS.register(new Item(props).setRegistryName("modernfix", "item_" + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean dfuModPresent() {
|
||||
for(String modId : new String[] { "lazydfu", "datafixerslayer" }) {
|
||||
if(ModList.get().isLoaded(modId))
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ import net.minecraft.Util;
|
|||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
@ -20,7 +22,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ModelLocationCache {
|
||||
private static final LoadingCache<BlockState, ModelResourceLocation> locationCache = CacheBuilder.newBuilder()
|
||||
private static final LoadingCache<BlockState, ModelResourceLocation> blockLocationCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<BlockState, ModelResourceLocation>() {
|
||||
@Override
|
||||
|
|
@ -29,9 +31,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) {
|
||||
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) {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
|
|||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.client.ItemModelMesherForge;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
|
@ -22,6 +23,8 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
|||
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
|
||||
* from that location from the model manager.
|
||||
|
|
@ -29,7 +32,11 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
|||
@Overwrite
|
||||
@Override
|
||||
public BakedModel getItemModel(Item item) {
|
||||
ModelResourceLocation map = locations.get(item.delegate);
|
||||
ModelResourceLocation map = locations.getOrDefault(item.delegate, SENTINEL);
|
||||
if(map == SENTINEL) {
|
||||
/* generate the appropriate location from our cache */
|
||||
map = ModelLocationCache.get(item);
|
||||
}
|
||||
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.mojang.datafixers.util.Pair;
|
||||
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.client.color.block.BlockColors;
|
||||
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.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.ClientPackSource;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
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.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
|
|
@ -36,6 +41,8 @@ import net.minecraftforge.common.MinecraftForge;
|
|||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.ForgeRegistryEntry;
|
||||
import net.minecraftforge.resource.DelegatingResourcePack;
|
||||
import net.minecraftforge.resource.PathResourcePack;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
|
|
@ -56,6 +63,7 @@ import java.util.*;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
|
@ -192,12 +200,85 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
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 PathResourcePack ||
|
||||
pack instanceof ClientPackSource ||
|
||||
pack instanceof DelegatingResourcePack ||
|
||||
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.add(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");
|
||||
try (Resource resource = frm.getResource(fileLocation)) {
|
||||
knownLocations.add(blockstate);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all blockstate JSONs and model files, collect textures.
|
||||
*/
|
||||
private void gatherModelMaterials(Set<Material> materialSet) {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
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", Integer.MAX_VALUE, p -> p.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", Integer.MAX_VALUE, p -> p.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) {
|
||||
blockStateData.add(CompletableFuture.supplyAsync(() -> {
|
||||
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
|
||||
|
|
@ -205,7 +286,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
JsonParser parser = new JsonParser();
|
||||
return Pair.of(blockstate, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
|
||||
} catch(IOException | JsonParseException e) {
|
||||
ModernFix.LOGGER.error("Error reading blockstate {}: {}", blockstate, e);
|
||||
logOrSuppressError(blockstateErrors, "blockstate", blockstate, e);
|
||||
}
|
||||
return Pair.of(blockstate, null);
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
|
|
@ -254,12 +335,25 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
|
||||
}
|
||||
} 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;
|
||||
|
||||
/* 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<>();
|
||||
basicModels.put(MISSING_MODEL_LOCATION, (BlockModel)missingModel);
|
||||
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
|
||||
|
|
@ -276,7 +370,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
JsonParser parser = new JsonParser();
|
||||
return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
|
||||
} catch(IOException | JsonParseException e) {
|
||||
ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e);
|
||||
logOrSuppressError(blockstateErrors, "model", fileLocation, e);
|
||||
return Pair.of(fileLocation, null);
|
||||
}
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
|
|
@ -296,12 +390,17 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
continue;
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e);
|
||||
logOrSuppressError(blockstateErrors, "model", pair.getFirst(), e);
|
||||
}
|
||||
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
|
||||
}
|
||||
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;
|
||||
Function<ResourceLocation, UnbakedModel> modelGetter = 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.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import team.chisel.ctm.CTM;
|
||||
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
||||
|
|
@ -36,6 +37,11 @@ public abstract class TextureMetadataHandlerMixin {
|
|||
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) {
|
||||
UnbakedModel rootModel = event.getUnbakedModel();
|
||||
BakedModel baked = event.getModel();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,30 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.fast_registry_validation;
|
||||
|
||||
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.IForgeRegistryEntry;
|
||||
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.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
@Mixin(value = ForgeRegistry.class, remap = false)
|
||||
public class ForgeRegistryMixin {
|
||||
public class ForgeRegistryMixin<V extends IForgeRegistryEntry<V>> {
|
||||
private static Method bitSetTrimMethod = null;
|
||||
private static boolean bitSetTrimMethodRetrieved = false;
|
||||
|
||||
|
|
@ -25,4 +40,71 @@ public class ForgeRegistryMixin {
|
|||
}
|
||||
return bitSetTrimMethod;
|
||||
}
|
||||
|
||||
private int expectedNextBit = -1;
|
||||
|
||||
/**
|
||||
* Avoid calling nextClearBit and scanning the whole registry for every block registration.
|
||||
*/
|
||||
@Redirect(method = "add(ILnet/minecraftforge/registries/IForgeRegistryEntry;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/minecraftforge/registries/IForgeRegistryEntry;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,603 @@
|
|||
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 net.minecraftforge.registries.IForgeRegistryEntry;
|
||||
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 extends IForgeRegistryEntry<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];
|
||||
|
||||
public CachedResourcePath(Path path) {
|
||||
this(NO_PREFIX, path, path.getNameCount());
|
||||
this(NO_PREFIX, path, path.getNameCount(), true);
|
||||
}
|
||||
|
||||
public CachedResourcePath(String s) {
|
||||
// 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) {
|
||||
this(prefixElements, collection, collection.size());
|
||||
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection, boolean intern) {
|
||||
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];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(prefixElements[i]) : prefixElements[i];
|
||||
i++;
|
||||
}
|
||||
for(Object component : path) {
|
||||
String s = component.toString();
|
||||
if(s.length() == 0)
|
||||
continue;
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(s);
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(s) : s;
|
||||
i++;
|
||||
}
|
||||
pathComponents = components;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ public class CanonizingStringMap<T> implements Map<String, T> {
|
|||
@NotNull
|
||||
@Override
|
||||
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
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@ public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor
|
|||
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.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>
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@
|
|||
"feature.branding.BrandingControlMixin",
|
||||
"feature.direct_stack_trace.CrashReportMixin",
|
||||
"perf.nbt_memory_usage.CompoundTagMixin",
|
||||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||
"perf.kubejs.RecipeEventJSMixin",
|
||||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||
"perf.fast_registry_validation.ForgeRegistrySnapshotMixin",
|
||||
"perf.fast_registry_validation.ResourceKeyMixin",
|
||||
"perf.cache_strongholds.ChunkGeneratorMixin",
|
||||
"perf.cache_upgraded_structures.StructureManagerMixin",
|
||||
"perf.cache_strongholds.ServerLevelMixin",
|
||||
|
|
@ -44,7 +46,8 @@
|
|||
"perf.compress_blockstate.BlockBehaviourMixin",
|
||||
"perf.dedup_blockstate_flattening_map.BlockStateDataMixin",
|
||||
"perf.dedup_blockstate_flattening_map.ChunkPalettedStorageFixMixin",
|
||||
"devenv.MinecraftServerMixin"
|
||||
"devenv.MinecraftServerMixin",
|
||||
"devenv.GameDataMixin"
|
||||
],
|
||||
"client": [
|
||||
"core.MinecraftMixin",
|
||||
|
|
@ -56,6 +59,7 @@
|
|||
"perf.dynamic_resources.BlockElementFaceDeserializerMixin",
|
||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemRendererMixin",
|
||||
"perf.dynamic_resources.ModelBakeryMixin",
|
||||
"perf.dynamic_resources.ae2.RegistrationMixin",
|
||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user