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 net.minecraft.Util;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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;
|
||||
|
|
@ -21,6 +23,8 @@ import net.minecraftforge.fml.loading.FMLConfig;
|
|||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
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.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
|
@ -99,6 +103,7 @@ public class ModernFix {
|
|||
MinecraftForge.EVENT_BUS.register(this);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onLoadComplete);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(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);
|
||||
|
|
@ -108,6 +113,17 @@ public class ModernFix {
|
|||
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() {
|
||||
if(FMLConfig.isOptimizedDFUDisabled())
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraftforge.client.ConfigScreenHandler;
|
||||
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
|
||||
import net.minecraft.util.MemoryReserve;
|
||||
import net.minecraftforge.client.event.ScreenEvent;
|
||||
|
|
@ -14,6 +17,7 @@ import net.minecraft.network.syncher.SynchedEntityData;
|
|||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
||||
import net.minecraftforge.client.event.*;
|
||||
import net.minecraftforge.client.settings.KeyConflictContext;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.TagsUpdatedEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
|
|
@ -21,13 +25,18 @@ import net.minecraftforge.event.level.LevelEvent;
|
|||
import net.minecraftforge.event.server.ServerStartingEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.IExtensionPoint;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
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.network.NetworkEvent;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
|
||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
|
@ -53,6 +62,26 @@ public class ModernFixClient {
|
|||
if(mfContainer.isPresent())
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ public class ModernFixEarlyConfig {
|
|||
|
||||
public static final boolean OPTIFINE_PRESENT;
|
||||
|
||||
private File configFile;
|
||||
|
||||
static {
|
||||
boolean hasOfClass = false;
|
||||
try {
|
||||
|
|
@ -34,7 +36,8 @@ public class ModernFixEarlyConfig {
|
|||
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.
|
||||
// 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
|
||||
|
|
@ -63,6 +66,7 @@ public class ModernFixEarlyConfig {
|
|||
this.addMixinRule("perf.fast_forge_dummies", true);
|
||||
this.addMixinRule("perf.dynamic_structure_manager", true);
|
||||
this.addMixinRule("bugfix.chunk_deadlock", true);
|
||||
this.addMixinRule("bugfix.paper_chunk_patches", true);
|
||||
this.addMixinRule("perf.thread_priorities", true);
|
||||
this.addMixinRule("perf.scan_cache", true);
|
||||
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.
|
||||
*/
|
||||
public static ModernFixEarlyConfig load(File file) {
|
||||
ModernFixEarlyConfig config = new ModernFixEarlyConfig();
|
||||
ModernFixEarlyConfig config = new ModernFixEarlyConfig(file);
|
||||
Properties props = new Properties();
|
||||
if(file.exists()) {
|
||||
try (FileInputStream fin = new FileInputStream(file)){
|
||||
|
|
@ -197,7 +201,7 @@ public class ModernFixEarlyConfig {
|
|||
}
|
||||
|
||||
try {
|
||||
config.writeConfig(file, props);
|
||||
config.save();
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write configuration file", e);
|
||||
}
|
||||
|
|
@ -205,8 +209,8 @@ public class ModernFixEarlyConfig {
|
|||
return config;
|
||||
}
|
||||
|
||||
private void writeConfig(File file, Properties props) throws IOException {
|
||||
File dir = file.getParentFile();
|
||||
public void save() throws IOException {
|
||||
File dir = configFile.getParentFile();
|
||||
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
|
|
@ -216,23 +220,24 @@ public class ModernFixEarlyConfig {
|
|||
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("#\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");
|
||||
List<String> lines = this.options.keySet().stream()
|
||||
List<String> keys = this.options.keySet().stream()
|
||||
.filter(key -> !key.equals("mixin.core"))
|
||||
.sorted()
|
||||
.map(key -> "# " + key + "\n")
|
||||
.collect(Collectors.toList());
|
||||
for(String line : lines) {
|
||||
writer.write(line);
|
||||
for(String line : keys) {
|
||||
if(!line.equals("mixin.core"))
|
||||
writer.write("# " + line + "\n");
|
||||
}
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
String value = (String) entry.getValue();
|
||||
writer.write(key + "=" + value + "\n");
|
||||
|
||||
for (String key : keys) {
|
||||
Option option = this.options.get(key);
|
||||
if(option.isUserDefined())
|
||||
writer.write(key + "=" + option.isEnabled() + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -251,4 +256,8 @@ public class ModernFixEarlyConfig {
|
|||
.filter(Option::isOverridden)
|
||||
.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) {
|
||||
if(this.enabled == enabled)
|
||||
return;
|
||||
this.enabled = enabled;
|
||||
this.userDefined = userDefined;
|
||||
}
|
||||
|
||||
public void addModOverride(boolean enabled, String modId) {
|
||||
if(this.enabled == enabled)
|
||||
return;
|
||||
this.enabled = enabled;
|
||||
|
||||
if (this.modDefined == null) {
|
||||
|
|
@ -57,6 +61,10 @@ public class Option {
|
|||
this.modDefined = null;
|
||||
}
|
||||
|
||||
public void clearUserDefined() {
|
||||
this.userDefined = false;
|
||||
}
|
||||
|
||||
public Collection<String> getDefiningMods() {
|
||||
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) {
|
||||
this.missingModel = model;
|
||||
this.permanentOverrides.put(ModelBakery.MISSING_MODEL_LOCATION, this.missingModel);
|
||||
}
|
||||
|
||||
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.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.ForgeRegistries;
|
||||
|
||||
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(ForgeRegistries.ITEMS.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,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.ModelResourceLocation;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.client.model.ForgeItemModelShaper;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
|
@ -23,6 +25,8 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
|||
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
|
||||
* from that location from the model manager.
|
||||
|
|
@ -30,7 +34,11 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
|||
@Overwrite
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.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;
|
||||
|
|
@ -33,6 +34,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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
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 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> {
|
||||
private static Method bitSetTrimMethod = null;
|
||||
private static boolean bitSetTrimMethodRetrieved = false;
|
||||
|
||||
|
|
@ -25,4 +39,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/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];
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.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.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.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.none": "Map (No nearby feature found)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
"mixins": [
|
||||
"core.BootstrapMixin",
|
||||
"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",
|
||||
"bugfix.chunk_deadlock.ServerChunkCacheMixin",
|
||||
"perf.dedicated_reload_executor.MinecraftServerMixin",
|
||||
|
|
@ -29,10 +32,12 @@
|
|||
"feature.branding.BrandingControlMixin",
|
||||
"feature.direct_stack_trace.CrashReportMixin",
|
||||
"perf.nbt_memory_usage.CompoundTagMixin",
|
||||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||
"perf.kubejs.RecipeEventJSMixin",
|
||||
"perf.tag_id_caching.TagEntryMixin",
|
||||
"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_upgraded_structures.StructureManagerMixin",
|
||||
"perf.cache_strongholds.ServerLevelMixin",
|
||||
|
|
@ -41,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",
|
||||
|
|
@ -55,9 +61,11 @@
|
|||
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||
"perf.dynamic_resources.ModelBakerImplMixin",
|
||||
"perf.dynamic_resources.ItemRendererMixin",
|
||||
"perf.dynamic_resources.ModelBakeryMixin",
|
||||
"perf.dynamic_resources.ModelManagerMixin",
|
||||
"perf.dynamic_resources.ae2.RegistrationMixin",
|
||||
"perf.dynamic_resources.rs.ClientSetupMixin",
|
||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||
"perf.dynamic_resources.ctm.CTMPackReloadListenerMixin",
|
||||
"perf.dynamic_resources.supermartijncore.ClientRegistrationHandlerMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user