Merge remote-tracking branch 'origin/1.19.2' into 1.19.4
This commit is contained in:
commit
1ae3f9f319
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package org.embeddedt.modernfix;
|
package org.embeddedt.modernfix;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.InputConstants;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
||||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||||
import net.minecraft.client.gui.screens.TitleScreen;
|
import net.minecraft.client.gui.screens.TitleScreen;
|
||||||
|
import net.minecraftforge.client.ConfigScreenHandler;
|
||||||
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
|
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
|
||||||
import net.minecraft.util.MemoryReserve;
|
import net.minecraft.util.MemoryReserve;
|
||||||
import net.minecraftforge.client.event.ScreenEvent;
|
import net.minecraftforge.client.event.ScreenEvent;
|
||||||
|
|
@ -14,6 +17,7 @@ import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
||||||
import net.minecraftforge.client.event.*;
|
import net.minecraftforge.client.event.*;
|
||||||
|
import net.minecraftforge.client.settings.KeyConflictContext;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.TagsUpdatedEvent;
|
import net.minecraftforge.event.TagsUpdatedEvent;
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
|
@ -21,13 +25,18 @@ import net.minecraftforge.event.level.LevelEvent;
|
||||||
import net.minecraftforge.event.server.ServerStartingEvent;
|
import net.minecraftforge.event.server.ServerStartingEvent;
|
||||||
import net.minecraftforge.eventbus.api.EventPriority;
|
import net.minecraftforge.eventbus.api.EventPriority;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.IExtensionPoint;
|
||||||
import net.minecraftforge.fml.ModContainer;
|
import net.minecraftforge.fml.ModContainer;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.fml.ModLoadingContext;
|
||||||
|
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||||
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||||
|
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
|
||||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
|
@ -53,6 +62,26 @@ public class ModernFixClient {
|
||||||
if(mfContainer.isPresent())
|
if(mfContainer.isPresent())
|
||||||
brandingString = "ModernFix " + mfContainer.get().getModInfo().getVersion().toString();
|
brandingString = "ModernFix " + mfContainer.get().getModInfo().getVersion().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::keyBindRegister);
|
||||||
|
ModLoadingContext.get().registerExtensionPoint(
|
||||||
|
ConfigScreenHandler.ConfigScreenFactory.class,
|
||||||
|
() -> new ConfigScreenHandler.ConfigScreenFactory((mc, screen) -> new ModernFixConfigScreen(screen))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyMapping configKey;
|
||||||
|
|
||||||
|
private void keyBindRegister(RegisterKeyMappingsEvent event) {
|
||||||
|
configKey = new KeyMapping("key.modernfix.config", KeyConflictContext.UNIVERSAL, InputConstants.UNKNOWN, "key.modernfix");
|
||||||
|
event.register(configKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onConfigKey(TickEvent.ClientTickEvent event) {
|
||||||
|
if(event.phase == TickEvent.Phase.START && configKey.consumeClick()) {
|
||||||
|
Minecraft.getInstance().setScreen(new ModernFixConfigScreen(Minecraft.getInstance().screen));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetWorldLoadStateMachine() {
|
public void resetWorldLoadStateMachine() {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ public class ModernFixEarlyConfig {
|
||||||
|
|
||||||
public static final boolean OPTIFINE_PRESENT;
|
public static final boolean OPTIFINE_PRESENT;
|
||||||
|
|
||||||
|
private File configFile;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
boolean hasOfClass = false;
|
boolean hasOfClass = false;
|
||||||
try {
|
try {
|
||||||
|
|
@ -34,7 +36,8 @@ public class ModernFixEarlyConfig {
|
||||||
return FMLLoader.getLoadingModList().getModFileById(modId) != null;
|
return FMLLoader.getLoadingModList().getModFileById(modId) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModernFixEarlyConfig() {
|
private ModernFixEarlyConfig(File file) {
|
||||||
|
this.configFile = file;
|
||||||
// Defines the default rules which can be configured by the user or other mods.
|
// Defines the default rules which can be configured by the user or other mods.
|
||||||
// You must manually add a rule for any new mixins not covered by an existing package rule.
|
// You must manually add a rule for any new mixins not covered by an existing package rule.
|
||||||
this.addMixinRule("core", true); // TODO: Don't actually allow the user to disable this
|
this.addMixinRule("core", true); // TODO: Don't actually allow the user to disable this
|
||||||
|
|
@ -63,6 +66,7 @@ public class ModernFixEarlyConfig {
|
||||||
this.addMixinRule("perf.fast_forge_dummies", true);
|
this.addMixinRule("perf.fast_forge_dummies", true);
|
||||||
this.addMixinRule("perf.dynamic_structure_manager", true);
|
this.addMixinRule("perf.dynamic_structure_manager", true);
|
||||||
this.addMixinRule("bugfix.chunk_deadlock", true);
|
this.addMixinRule("bugfix.chunk_deadlock", true);
|
||||||
|
this.addMixinRule("bugfix.paper_chunk_patches", true);
|
||||||
this.addMixinRule("perf.thread_priorities", true);
|
this.addMixinRule("perf.thread_priorities", true);
|
||||||
this.addMixinRule("perf.scan_cache", true);
|
this.addMixinRule("perf.scan_cache", true);
|
||||||
this.addMixinRule("perf.kubejs", modPresent("kubejs"));
|
this.addMixinRule("perf.kubejs", modPresent("kubejs"));
|
||||||
|
|
@ -185,7 +189,7 @@ public class ModernFixEarlyConfig {
|
||||||
* created. The file on disk will then be updated to include any new options.
|
* created. The file on disk will then be updated to include any new options.
|
||||||
*/
|
*/
|
||||||
public static ModernFixEarlyConfig load(File file) {
|
public static ModernFixEarlyConfig load(File file) {
|
||||||
ModernFixEarlyConfig config = new ModernFixEarlyConfig();
|
ModernFixEarlyConfig config = new ModernFixEarlyConfig(file);
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
if(file.exists()) {
|
if(file.exists()) {
|
||||||
try (FileInputStream fin = new FileInputStream(file)){
|
try (FileInputStream fin = new FileInputStream(file)){
|
||||||
|
|
@ -197,7 +201,7 @@ public class ModernFixEarlyConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
config.writeConfig(file, props);
|
config.save();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("Could not write configuration file", e);
|
LOGGER.warn("Could not write configuration file", e);
|
||||||
}
|
}
|
||||||
|
|
@ -205,8 +209,8 @@ public class ModernFixEarlyConfig {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeConfig(File file, Properties props) throws IOException {
|
public void save() throws IOException {
|
||||||
File dir = file.getParentFile();
|
File dir = configFile.getParentFile();
|
||||||
|
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
if (!dir.mkdirs()) {
|
if (!dir.mkdirs()) {
|
||||||
|
|
@ -216,23 +220,24 @@ public class ModernFixEarlyConfig {
|
||||||
throw new IOException("The parent file is not a directory");
|
throw new IOException("The parent file is not a directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Writer writer = new FileWriter(file)) {
|
try (Writer writer = new FileWriter(configFile)) {
|
||||||
writer.write("# This is the configuration file for ModernFix.\n");
|
writer.write("# This is the configuration file for ModernFix.\n");
|
||||||
writer.write("#\n");
|
writer.write("#\n");
|
||||||
writer.write("# The following options can be enabled or disabled if there is a compatibility issue.\n");
|
writer.write("# The following options can be enabled or disabled if there is a compatibility issue.\n");
|
||||||
writer.write("# Add a line mixin.example_name=true/false without the # sign to enable/disable a rule.\n");
|
writer.write("# Add a line mixin.example_name=true/false without the # sign to enable/disable a rule.\n");
|
||||||
List<String> lines = this.options.keySet().stream()
|
List<String> keys = this.options.keySet().stream()
|
||||||
.filter(key -> !key.equals("mixin.core"))
|
.filter(key -> !key.equals("mixin.core"))
|
||||||
.sorted()
|
.sorted()
|
||||||
.map(key -> "# " + key + "\n")
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
for(String line : lines) {
|
for(String line : keys) {
|
||||||
writer.write(line);
|
if(!line.equals("mixin.core"))
|
||||||
|
writer.write("# " + line + "\n");
|
||||||
}
|
}
|
||||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
|
||||||
String key = (String) entry.getKey();
|
for (String key : keys) {
|
||||||
String value = (String) entry.getValue();
|
Option option = this.options.get(key);
|
||||||
writer.write(key + "=" + value + "\n");
|
if(option.isUserDefined())
|
||||||
|
writer.write(key + "=" + option.isEnabled() + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,4 +256,8 @@ public class ModernFixEarlyConfig {
|
||||||
.filter(Option::isOverridden)
|
.filter(Option::isOverridden)
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Option> getOptionMap() {
|
||||||
|
return Collections.unmodifiableMap(this.options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,15 @@ public class Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled, boolean userDefined) {
|
public void setEnabled(boolean enabled, boolean userDefined) {
|
||||||
|
if(this.enabled == enabled)
|
||||||
|
return;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.userDefined = userDefined;
|
this.userDefined = userDefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addModOverride(boolean enabled, String modId) {
|
public void addModOverride(boolean enabled, String modId) {
|
||||||
|
if(this.enabled == enabled)
|
||||||
|
return;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
|
||||||
if (this.modDefined == null) {
|
if (this.modDefined == null) {
|
||||||
|
|
@ -57,6 +61,10 @@ public class Option {
|
||||||
this.modDefined = null;
|
this.modDefined = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearUserDefined() {
|
||||||
|
this.userDefined = false;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<String> getDefiningMods() {
|
public Collection<String> getDefiningMods() {
|
||||||
return this.modDefined != null ? Collections.unmodifiableCollection(this.modDefined) : Collections.emptyList();
|
return this.modDefined != null ? Collections.unmodifiableCollection(this.modDefined) : Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
public interface IPaperChunkHolder {
|
||||||
|
boolean mfix$canAdvanceStatus();
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
|
||||||
|
|
||||||
public void setMissingModel(BakedModel model) {
|
public void setMissingModel(BakedModel model) {
|
||||||
this.missingModel = model;
|
this.missingModel = model;
|
||||||
|
this.permanentOverrides.put(ModelBakery.MISSING_MODEL_LOCATION, this.missingModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Triple<ResourceLocation, Transformation, Boolean> vanillaKey(Object o) {
|
private static Triple<ResourceLocation, Transformation, Boolean> vanillaKey(Object o) {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,10 @@ 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;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -20,7 +22,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 +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(ForgeRegistries.ITEMS.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,61 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.bugfix.paper_chunk_patches;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Mixin(ChunkHolder.class)
|
||||||
|
public abstract class ChunkHolderMixin implements IPaperChunkHolder {
|
||||||
|
|
||||||
|
@Shadow public abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus arg);
|
||||||
|
|
||||||
|
@Shadow @Final private static List<ChunkStatus> CHUNK_STATUSES;
|
||||||
|
|
||||||
|
public ChunkStatus mfix$getChunkHolderStatus() {
|
||||||
|
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||||
|
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||||
|
if (either == null || !either.left().isPresent()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkAccess mfix$getAvailableChunkNow() {
|
||||||
|
// TODO can we just getStatusFuture(EMPTY)?
|
||||||
|
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||||
|
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||||
|
if (either == null || !either.left().isPresent()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return either.left().get();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChunkStatus mfix$getNextStatus(ChunkStatus status) {
|
||||||
|
if (status == ChunkStatus.FULL) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return CHUNK_STATUSES.get(status.getIndex() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mfix$canAdvanceStatus() {
|
||||||
|
ChunkStatus status = mfix$getChunkHolderStatus();
|
||||||
|
ChunkAccess chunk = mfix$getAvailableChunkNow();
|
||||||
|
return chunk != null && (status == null || chunk.getStatus().isOrAfter(mfix$getNextStatus(status)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.bugfix.paper_chunk_patches;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.*;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
|
import net.minecraft.util.thread.BlockableEventLoop;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||||
|
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
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.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
|
||||||
|
@Mixin(ChunkMap.class)
|
||||||
|
public abstract class ChunkMapMixin {
|
||||||
|
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
|
||||||
|
|
||||||
|
@Shadow @Final private ChunkMap.DistanceManager distanceManager;
|
||||||
|
|
||||||
|
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder arg);
|
||||||
|
|
||||||
|
@Shadow @Final private ServerLevel level;
|
||||||
|
@Shadow @Final private ThreadedLevelLightEngine lightEngine;
|
||||||
|
@Shadow @Final private ChunkProgressListener progressListener;
|
||||||
|
|
||||||
|
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
||||||
|
|
||||||
|
@Shadow @Final private StructureTemplateManager structureTemplateManager;
|
||||||
|
private Executor mainInvokingExecutor;
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"), cancellable = true)
|
||||||
|
private void setup(CallbackInfo ci) {
|
||||||
|
this.mainInvokingExecutor = (runnable) -> {
|
||||||
|
if(ServerLifecycleHooks.getCurrentServer().isSameThread())
|
||||||
|
runnable.run();
|
||||||
|
else
|
||||||
|
this.mainThreadExecutor.execute(runnable);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
|
||||||
|
@ModifyArg(method = "prepareAccessibleChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
||||||
|
private Executor useMainThreadExecutor(Executor executor) {
|
||||||
|
return this.mainThreadExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://github.com/PaperMC/Paper/blob/master/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch */
|
||||||
|
@ModifyArg(method = "prepareEntityTickingChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
||||||
|
private Executor useMainInvokingExecutor(Executor executor) {
|
||||||
|
return this.mainInvokingExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@Redirect(method = "scheduleChunkGeneration", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||||
|
private CompletableFuture skipWorkerIfPossible(CompletableFuture inputFuture, Function function, Executor executor, ChunkHolder holder) {
|
||||||
|
Executor targetExecutor = (runnable) -> {
|
||||||
|
if(((IPaperChunkHolder)holder).mfix$canAdvanceStatus()) {
|
||||||
|
this.mainInvokingExecutor.execute(runnable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executor.execute(runnable);
|
||||||
|
};
|
||||||
|
return inputFuture.thenComposeAsync(function, targetExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason revert 1.17 chunk system changes, significantly reduces time and RAM needed to load chunks
|
||||||
|
*/
|
||||||
|
@Inject(method = "schedule", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void useLegacySchedulingLogic(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> cir) {
|
||||||
|
if(requiredStatus != ChunkStatus.EMPTY) {
|
||||||
|
ChunkPos chunkpos = holder.getPos();
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = holder.getOrScheduleFuture(requiredStatus.getParent(), (ChunkMap)(Object)this);
|
||||||
|
cir.setReturnValue(future.thenComposeAsync((either) -> {
|
||||||
|
Optional<ChunkAccess> optional = either.left();
|
||||||
|
if(!optional.isPresent())
|
||||||
|
return CompletableFuture.completedFuture(either);
|
||||||
|
|
||||||
|
if (requiredStatus == ChunkStatus.LIGHT) {
|
||||||
|
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from original method
|
||||||
|
if (optional.get().getStatus().isOrAfter(requiredStatus)) {
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureTemplateManager, this.lightEngine, (arg2) -> {
|
||||||
|
return this.protoChunkToFullChunk(holder);
|
||||||
|
}, (ChunkAccess)optional.get());
|
||||||
|
this.progressListener.onStatusChange(chunkpos, requiredStatus);
|
||||||
|
return completablefuture;
|
||||||
|
} else {
|
||||||
|
return this.scheduleChunkGeneration(holder, requiredStatus);
|
||||||
|
}
|
||||||
|
}, this.mainThreadExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainThreadExecutor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.bugfix.paper_chunk_patches;
|
||||||
|
|
||||||
|
import net.minecraft.util.SortedArraySet;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Mixin(SortedArraySet.class)
|
||||||
|
public abstract class SortedArraySetMixin<T> extends AbstractSet<T> {
|
||||||
|
@Shadow private int size;
|
||||||
|
|
||||||
|
@Shadow private T[] contents;
|
||||||
|
|
||||||
|
// Paper start - optimise removeIf
|
||||||
|
@Override
|
||||||
|
public boolean removeIf(Predicate<? super T> filter) {
|
||||||
|
// prev. impl used an iterator, which could be n^2 and creates garbage
|
||||||
|
int i = 0, len = this.size;
|
||||||
|
T[] backingArray = this.contents;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (i >= len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!filter.test(backingArray[i])) {
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only want to write back to backingArray if we really need to
|
||||||
|
|
||||||
|
int lastIndex = i; // this is where new elements are shifted to
|
||||||
|
|
||||||
|
for (; i < len; ++i) {
|
||||||
|
T curr = backingArray[i];
|
||||||
|
if (!filter.test(curr)) { // if test throws we're screwed
|
||||||
|
backingArray[lastIndex++] = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup end
|
||||||
|
Arrays.fill(backingArray, lastIndex, len, null);
|
||||||
|
this.size = lastIndex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,11 @@ import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
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 +25,8 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
||||||
super(arg);
|
super(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ModelResourceLocation SENTINEL = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "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 +34,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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,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;
|
||||||
|
|
@ -33,6 +34,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();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.dynamic_resources.rs;
|
||||||
|
|
||||||
|
import com.refinedmods.refinedstorage.render.BakedModelOverrideRegistry;
|
||||||
|
import com.refinedmods.refinedstorage.setup.ClientSetup;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(ClientSetup.class)
|
||||||
|
public class ClientSetupMixin {
|
||||||
|
@Shadow @Final private static BakedModelOverrideRegistry BAKED_MODEL_OVERRIDE_REGISTRY;
|
||||||
|
|
||||||
|
@Inject(method = "onClientSetup", at = @At("RETURN"), remap = false)
|
||||||
|
private static void addDynamicListener(CallbackInfo ci) {
|
||||||
|
MinecraftForge.EVENT_BUS.addListener(ClientSetupMixin::onDynamicModelBake);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void onDynamicModelBake(DynamicModelBakeEvent event) {
|
||||||
|
BakedModelOverrideRegistry.BakedModelOverrideFactory factory = BAKED_MODEL_OVERRIDE_REGISTRY.get(event.getLocation() instanceof ModelResourceLocation ? new ResourceLocation(event.getLocation().getNamespace(), event.getLocation().getPath()) : event.getLocation());
|
||||||
|
if(factory != null)
|
||||||
|
event.setModel(factory.create(event.getModel(), event.getModelBakery().getBakedTopLevelModels()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.embeddedt.modernfix.screen;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.CommonComponents;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class ModernFixConfigScreen extends Screen {
|
||||||
|
private OptionList optionList;
|
||||||
|
private Screen lastScreen;
|
||||||
|
|
||||||
|
public boolean madeChanges = false;
|
||||||
|
private Button doneButton;
|
||||||
|
public ModernFixConfigScreen(@Nullable Screen lastScreen) {
|
||||||
|
super(Component.translatable("modernfix.config"));
|
||||||
|
this.lastScreen = lastScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
this.optionList = new OptionList(this, this.minecraft);
|
||||||
|
this.addWidget(this.optionList);
|
||||||
|
this.doneButton = new Button.Builder(CommonComponents.GUI_DONE, (arg) -> {
|
||||||
|
this.minecraft.setScreen(lastScreen);
|
||||||
|
}).pos(this.width / 2 - 100, this.height - 29).size(200, 20).build();
|
||||||
|
this.addRenderableWidget(this.doneButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
this.renderBackground(poseStack);
|
||||||
|
this.optionList.render(poseStack, mouseX, mouseY, partialTicks);
|
||||||
|
drawCenteredString(poseStack, this.font, this.title, this.width / 2, 8, 16777215);
|
||||||
|
this.doneButton.setMessage(madeChanges ? Component.translatable("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
|
||||||
|
super.render(poseStack, mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/main/java/org/embeddedt/modernfix/screen/OptionList.java
Normal file
122
src/main/java/org/embeddedt/modernfix/screen/OptionList.java
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
package org.embeddedt.modernfix.screen;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.components.toasts.SystemToast;
|
||||||
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
|
import org.embeddedt.modernfix.core.config.Option;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
|
||||||
|
private final int maxNameWidth;
|
||||||
|
|
||||||
|
private static final Component OPTION_ON = Component.translatable("modernfix.option.on").withStyle(style -> style.withColor(ChatFormatting.GREEN));
|
||||||
|
private static final Component OPTION_OFF = Component.translatable("modernfix.option.off").withStyle(style -> style.withColor(ChatFormatting.RED));
|
||||||
|
|
||||||
|
private ModernFixConfigScreen mainScreen;
|
||||||
|
|
||||||
|
|
||||||
|
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
|
||||||
|
super(arg2,arg.width + 45, arg.height, 43, arg.height - 32, 20);
|
||||||
|
|
||||||
|
this.mainScreen = arg;
|
||||||
|
|
||||||
|
int maxW = 0;
|
||||||
|
Map<String, Option> optionMap = ModernFixMixinPlugin.config.getOptionMap();
|
||||||
|
List<String> sortedKeys = optionMap.keySet().stream().filter(key -> !key.equals("mixin.core")).sorted().collect(Collectors.toList());
|
||||||
|
for(String key : sortedKeys) {
|
||||||
|
Option option = optionMap.get(key);
|
||||||
|
int w = this.minecraft.font.width(key);
|
||||||
|
maxW = Math.max(w, maxW);
|
||||||
|
this.addEntry(new OptionEntry(key, option));
|
||||||
|
}
|
||||||
|
this.maxNameWidth = maxW;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getScrollbarPosition() {
|
||||||
|
return super.getScrollbarPosition() + 15 + 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRowWidth() {
|
||||||
|
return super.getRowWidth() + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionEntry extends Entry {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final Button toggleButton;
|
||||||
|
private final Option option;
|
||||||
|
|
||||||
|
public OptionEntry(String optionName, Option option) {
|
||||||
|
this.name = optionName;
|
||||||
|
this.option = option;
|
||||||
|
this.toggleButton = new Button.Builder(Component.literal(""), (arg) -> {
|
||||||
|
this.option.setEnabled(!this.option.isEnabled(), !this.option.isUserDefined());
|
||||||
|
try {
|
||||||
|
ModernFixMixinPlugin.config.save();
|
||||||
|
if(!OptionList.this.mainScreen.madeChanges) {
|
||||||
|
OptionList.this.mainScreen.madeChanges = true;
|
||||||
|
}
|
||||||
|
} catch(IOException e) {
|
||||||
|
// revert
|
||||||
|
this.option.setEnabled(!this.option.isEnabled(), !this.option.isUserDefined());
|
||||||
|
ModernFix.LOGGER.error("Unable to save config", e);
|
||||||
|
}
|
||||||
|
}).pos(0, 0).size(95, 20).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack matrixStack, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean isMouseOver, float partialTicks) {
|
||||||
|
MutableComponent nameComponent = Component.literal(this.name);
|
||||||
|
if(this.option.isUserDefined())
|
||||||
|
nameComponent = nameComponent.withStyle(ChatFormatting.ITALIC).append(Component.translatable("modernfix.config.not_default"));
|
||||||
|
OptionList.this.minecraft.font.draw(matrixStack, nameComponent, (float)(left + 160 - OptionList.this.maxNameWidth), (float)(top + height / 2 - 4), 16777215);
|
||||||
|
this.toggleButton.setPosition(left + 175, top);
|
||||||
|
this.toggleButton.setMessage(getOptionMessage(this.option));
|
||||||
|
this.toggleButton.render(matrixStack, mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component getOptionMessage(Option option) {
|
||||||
|
return option.isEnabled() ? OPTION_ON : OPTION_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends GuiEventListener> children() {
|
||||||
|
return ImmutableList.of(this.toggleButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||||
|
return this.toggleButton.mouseClicked(mouseX, mouseY, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||||
|
return this.toggleButton.mouseReleased(mouseX, mouseY, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends NarratableEntry> narratables() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Entry extends ContainerObjectSelectionList.Entry<Entry> {
|
||||||
|
public Entry() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,6 @@ public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl
|
||||||
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl <init>(Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl <init>(Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
||||||
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.ChunkMap$DistanceManager
|
||||||
|
public net.minecraft.resources.ResourceKey <init>(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
{
|
{
|
||||||
|
"key.modernfix": "ModernFix",
|
||||||
|
"key.modernfix.config": "Open config screen",
|
||||||
"modernfix.jei_load": "Loading JEI, this may take a while",
|
"modernfix.jei_load": "Loading JEI, this may take a while",
|
||||||
"modernfix.no_lazydfu": "ModernFix detected that DFU rules were compiled on startup. This slows down game launching. Installing LazyDFU to resolve this is highly recommended.",
|
"modernfix.no_lazydfu": "ModernFix detected that DFU rules were compiled on startup. This slows down game launching. Installing LazyDFU to resolve this is highly recommended.",
|
||||||
|
"modernfix.config": "ModernFix mixin config",
|
||||||
|
"modernfix.config.done_restart": "Done (restart required)",
|
||||||
|
"modernfix.option.on": "on",
|
||||||
|
"modernfix.option.off": "off",
|
||||||
|
"modernfix.config.not_default": " (modified)",
|
||||||
"asynclocator.map.locating": "Map (Locating...)",
|
"asynclocator.map.locating": "Map (Locating...)",
|
||||||
"asynclocator.map.none": "Map (No nearby feature found)"
|
"asynclocator.map.none": "Map (No nearby feature found)"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"core.BootstrapMixin",
|
"core.BootstrapMixin",
|
||||||
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
|
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
|
||||||
|
"bugfix.paper_chunk_patches.ChunkMapMixin",
|
||||||
|
"bugfix.paper_chunk_patches.ChunkHolderMixin",
|
||||||
|
"bugfix.paper_chunk_patches.SortedArraySetMixin",
|
||||||
"perf.dynamic_structure_manager.StructureManagerMixin",
|
"perf.dynamic_structure_manager.StructureManagerMixin",
|
||||||
"bugfix.chunk_deadlock.ServerChunkCacheMixin",
|
"bugfix.chunk_deadlock.ServerChunkCacheMixin",
|
||||||
"perf.dedicated_reload_executor.MinecraftServerMixin",
|
"perf.dedicated_reload_executor.MinecraftServerMixin",
|
||||||
|
|
@ -29,10 +32,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",
|
||||||
|
|
@ -41,7 +46,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",
|
||||||
|
|
@ -55,9 +61,11 @@
|
||||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||||
"perf.dynamic_resources.ModelBakerImplMixin",
|
"perf.dynamic_resources.ModelBakerImplMixin",
|
||||||
|
"perf.dynamic_resources.ItemRendererMixin",
|
||||||
"perf.dynamic_resources.ModelBakeryMixin",
|
"perf.dynamic_resources.ModelBakeryMixin",
|
||||||
"perf.dynamic_resources.ModelManagerMixin",
|
"perf.dynamic_resources.ModelManagerMixin",
|
||||||
"perf.dynamic_resources.ae2.RegistrationMixin",
|
"perf.dynamic_resources.ae2.RegistrationMixin",
|
||||||
|
"perf.dynamic_resources.rs.ClientSetupMixin",
|
||||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||||
"perf.dynamic_resources.ctm.CTMPackReloadListenerMixin",
|
"perf.dynamic_resources.ctm.CTMPackReloadListenerMixin",
|
||||||
"perf.dynamic_resources.supermartijncore.ClientRegistrationHandlerMixin",
|
"perf.dynamic_resources.supermartijncore.ClientRegistrationHandlerMixin",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user