Merge remote-tracking branch 'origin/1.18' into 1.19.2

This commit is contained in:
embeddedt 2023-03-04 16:29:04 -05:00
commit 5d317b6d94
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
21 changed files with 516 additions and 18 deletions

View File

@ -88,6 +88,9 @@ dependencies {
// compile against the JEI API but do not include it at runtime
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
modRuntimeOnly("curse.maven:jei-238222:4405346")
modCompileOnly("curse.maven:jeresources-240630:3951643")
}
tasks.withType(JavaCompile) {
@ -153,7 +156,7 @@ curseforge {
id = "790626"
changelog = '[Changelog is not currently available]'
changelogType = "markdown"
releaseType = "release"
releaseType = "beta"
addGameVersion "Forge"
addGameVersion minecraft_version
mainArtifact remapJar

View File

@ -4,18 +4,20 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.IExtensionPoint;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.*;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.network.NetworkConstants;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.core.config.ModernFixConfig;
import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler;
import org.embeddedt.modernfix.packet.PacketHandler;
import java.lang.management.ManagementFactory;
import java.util.concurrent.CountDownLatch;
@ -62,9 +64,30 @@ public class ModernFix {
INSTANCE = this;
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
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);
MinecraftForge.EVENT_BUS.register(EntityDataIDSyncHandler.class);
PacketHandler.register();
}
private static boolean dfuModPresent() {
for(String modId : new String[] { "lazydfu", "datafixerslayer" }) {
if(ModList.get().isLoaded(modId))
return true;
}
return false;
}
@SubscribeEvent
public void commonSetup(FMLCommonSetupEvent event) {
if(!dfuModPresent()) {
event.enqueueWork(() -> {
ModLoader.get().addWarning(new ModLoadingWarning(ModLoadingContext.get().getActiveContainer().getModInfo(), ModLoadingStage.COMMON_SETUP, "modernfix.no_lazydfu"));
});
}
}
@SubscribeEvent

View File

@ -1,22 +1,32 @@
package org.embeddedt.modernfix;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.ConnectScreen;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.network.NetworkEvent;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.load.LoadEvents;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
import java.lang.management.ManagementFactory;
import java.util.Optional;
import java.lang.reflect.Field;
import java.sql.Ref;
import java.util.*;
import java.util.function.Supplier;
public class ModernFixClient {
public static long worldLoadStartTime;
@ -71,4 +81,106 @@ public class ModernFixClient {
event.getLeft().add(brandingString);
}
}
/**
* Check if the IDs match and remap them if not.
* @return true if ID remap was needed
*/
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
if(accessor.id != newId) {
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
eClass,
fieldName,
accessor.id,
newId);
accessor.id = newId;
return true;
} else {
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
return false;
}
}
/**
* Horrendous hack to allow tracking every synced entity data manager.
*
* This is to ensure we can perform ID fixup on already constructed managers.
*/
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
private static final Field entriesArrayField;
static {
Field field;
try {
field = SynchedEntityData.class.getDeclaredField("entriesArray");
field.setAccessible(true);
} catch(ReflectiveOperationException e) {
field = null;
}
entriesArrayField = field;
}
/**
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
*
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
*/
public static void handleEntityIDSync(EntityIDSyncPacket packet, Supplier<NetworkEvent.Context> context) {
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
context.get().enqueueWork(() -> {
boolean fixNeeded = false;
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
Class<? extends Entity> eClass = entry.getKey();
for(Pair<String, Integer> field : entry.getValue()) {
String fieldName = field.getFirst();
int newId = field.getSecond();
try {
Field f = eClass.getDeclaredField(fieldName);
f.setAccessible(true);
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
fixNeeded = true;
} catch(NoSuchFieldException e) {
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Unexpected exception", e);
}
}
}
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
List<SynchedEntityData> dataEntries;
synchronized (allEntityDatas) {
if(fixNeeded) {
dataEntries = new ArrayList<>(allEntityDatas);
for(SynchedEntityData manager : dataEntries) {
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
for(SynchedEntityData.DataItem<?> item : items) {
fixedMap.put(item.getAccessor().id, item);
}
manager.lock.writeLock().lock();
try {
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
if(entriesArrayField != null) {
try {
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
for(int i = 0; i < dataArray.length; i++) {
dataArray[i] = fixedMap.get(i);
}
entriesArrayField.set(manager, dataArray);
} catch(ReflectiveOperationException e) {
ModernFix.LOGGER.error(e);
}
}
} finally {
manager.lock.writeLock().unlock();
}
}
}
allEntityDatas.clear();
}
});
context.get().setPacketHandled(true);
}
}

View File

@ -53,7 +53,7 @@ public class BlockStateCacheHandler {
currentRebuildThread = null;
}
} else {
ModernFix.LOGGER.warn("Deferred blockstate cache rebuild");
ModernFix.LOGGER.debug("Deferred blockstate cache rebuild");
}
}

View File

@ -1,6 +1,7 @@
package org.embeddedt.modernfix.core.config;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -22,6 +23,7 @@ public class ModernFixEarlyConfig {
this.addMixinRule("feature.measure_time", true);
this.addMixinRule("feature.reduce_loading_screen_freezes", false);
this.addMixinRule("perf.fast_registry_validation", true);
this.addMixinRule("perf.use_integrated_resources", true);
this.addMixinRule("perf.remove_biome_temperature_cache", true);
this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true);
this.addMixinRule("perf.parallelize_model_loading", true);
@ -30,6 +32,7 @@ public class ModernFixEarlyConfig {
this.addMixinRule("perf.cache_upgraded_structures", true);
this.addMixinRule("bugfix.concurrency", true);
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
this.addMixinRule("perf.async_jei", true);
this.addMixinRule("perf.thread_priorities", true);
this.addMixinRule("perf.sync_executor_sleep", true);
this.addMixinRule("perf.scan_cache", true);
@ -43,7 +46,8 @@ public class ModernFixEarlyConfig {
/* off by default in 1.18 because it doesn't work as well */
this.addMixinRule("perf.faster_singleplayer_load", false);
/* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */
this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null);
Optional<ModInfo> jeiMod = FMLLoader.getLoadingModList().getMods().stream().filter(mod -> mod.getModId().equals("jei")).findFirst();
this.addMixinRule("perf.blast_search_trees", jeiMod.isPresent() && jeiMod.get().getVersion().getMajorVersion() >= 10);
this.addMixinRule("safety", true);
this.addMixinRule("launch.transformer_cache", false);
this.addMixinRule("launch.class_search_cache", true);
@ -52,6 +56,7 @@ public class ModernFixEarlyConfig {
disableIfModPresent("mixin.perf.thread_priorities", "smoothboot");
disableIfModPresent("mixin.perf.async_jei", "modernui");
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate");
disableIfModPresent("mixin.bugfix.mc218112", "performant");
}
private void disableIfModPresent(String configName, String... ids) {

View File

@ -0,0 +1,10 @@
package org.embeddedt.modernfix.duck.reuse_datapacks;
import net.minecraft.server.ReloadableServerResources;
import java.util.Collection;
public interface ICachingResourceClient {
void setCachedResources(ReloadableServerResources r);
void setCachedDataPackConfig(Collection<String> c);
}

View File

@ -0,0 +1,69 @@
package org.embeddedt.modernfix.entity;
import com.mojang.datafixers.util.Pair;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.event.OnDatapackSyncEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.server.ServerLifecycleHooks;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
import org.embeddedt.modernfix.packet.PacketHandler;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EntityDataIDSyncHandler {
private static Map<Class<? extends Entity>, List<Pair<String, Integer>>> fieldsToSyncMap;
@SubscribeEvent(priority = EventPriority.HIGHEST)
@SuppressWarnings("unchecked")
public static void onDatapackSyncEvent(OnDatapackSyncEvent event) {
if(event.getPlayer() != null) {
if(!ServerLifecycleHooks.getCurrentServer().isDedicatedServer() && event.getPlayerList().getPlayerCount() == 0) {
ModernFix.LOGGER.debug("Not syncing IDs on integrated server");
return;
}
/* Compute the current set of serializer IDs in use and send them */
try {
if(fieldsToSyncMap == null) {
fieldsToSyncMap = new HashMap<>();
Field entityPoolField = ObfuscationReflectionHelper.findField(SynchedEntityData.class, "f_135343_");
Map<Class<? extends Entity>, Integer> entityPoolMap = (Map<Class<? extends Entity>, Integer>)entityPoolField.get(null);
List<Field> fieldsToSync = new ArrayList<>();
for(Class<? extends Entity> eClass : entityPoolMap.keySet()) {
fieldsToSync.clear();
Field[] classFields = eClass.getDeclaredFields();
for(Field field : classFields) {
if(!Modifier.isStatic(field.getModifiers()))
continue;
field.setAccessible(true);
Object o = field.get(null);
if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) {
fieldsToSync.add(field);
}
}
for(Field field : fieldsToSync) {
int id = ((EntityDataAccessor<?>)field.get(null)).id;
fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id));
}
}
}
EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap);
ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes");
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(event::getPlayer), packet);
} catch(ObfuscationReflectionHelper.UnableToFindFieldException | ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,24 @@
package org.embeddedt.modernfix.mixin.core;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFixClient;
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.CallbackInfo;
@Mixin(SynchedEntityData.class)
public class SynchedEntityDataMixin {
/**
* Store this in our set of all entity data objects.
*
* Not an ideal solution, but it should guarantee compatibility with mods.
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void storeInSet(Entity arg, CallbackInfo ci) {
synchronized (ModernFixClient.allEntityDatas) {
ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this);
}
}
}

View File

@ -13,9 +13,9 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.List;
import java.util.Optional;
@Mixin(value = BrandingControl.class, remap = false)
@Mixin(value = BrandingControl.class, remap = false, priority = 1100)
public class BrandingControlMixin {
@Inject(method = "computeBranding", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModList;get()Lnet/minecraftforge/fml/ModList;"), locals = LocalCapture.CAPTURE_FAILHARD)
@Inject(method = "computeBranding", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModList;get()Lnet/minecraftforge/fml/ModList;"), locals = LocalCapture.CAPTURE_FAILHARD, require = 0)
private static void addModernFixBranding(CallbackInfo ci, ImmutableList.Builder<String> builder) {
Optional<? extends ModContainer> mfContainer = ModList.get().getModContainerById("modernfix");
if(mfContainer.isPresent())

View File

@ -2,7 +2,9 @@ package org.embeddedt.modernfix.mixin.perf.blast_search_trees;
import net.minecraft.client.Minecraft;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.forgespi.language.IModFileInfo;
import org.embeddedt.modernfix.searchtree.DummySearchTree;
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
import org.spongepowered.asm.mixin.Final;
@ -12,6 +14,8 @@ 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.Optional;
@Mixin(Minecraft.class)
public class MinecraftMixin {
@Shadow @Final private SearchRegistry searchRegistry;
@ -19,7 +23,8 @@ public class MinecraftMixin {
@Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true)
private void replaceSearchTrees(CallbackInfo ci) {
ci.cancel();
if(ModList.get().getModFileById("jei") != null) {
Optional<? extends ModContainer> jeiContainer = ModList.get().getModContainerById("jei");
if(jeiContainer.isPresent() && jeiContainer.get().getModInfo().getVersion().getMajorVersion() >= 10) {
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, list -> new JEIBackedSearchTree(false));
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, list -> new JEIBackedSearchTree(true));
} else {

View File

@ -19,7 +19,6 @@ public class IntegratedServerMixin {
@Inject(method = "<init>", at = @At("RETURN"))
private void adjustServerPriority(Thread thread, Minecraft arg, LevelStorageSource.LevelStorageAccess arg2, PackRepository arg3, WorldStem arg4, Services arg5, ChunkProgressListenerFactory arg6, CallbackInfo ci) {
int pri = ModernFixConfig.INTEGRATED_SERVER_PRIORITY.get();
ModernFix.LOGGER.info("Changing server thread priority to " + pri);
thread.setPriority(pri);
}
}

View File

@ -0,0 +1,20 @@
package org.embeddedt.modernfix.mixin.perf.use_integrated_resources;
import jeresources.util.LootTableHelper;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.Level;
import net.minecraftforge.server.ServerLifecycleHooks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LootTableHelper.class)
public class LootTableHelperMixin {
@Redirect(method = "getLootTables", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getServer()Lnet/minecraft/server/MinecraftServer;"))
private static MinecraftServer useIntegrated(Level level) {
MinecraftServer server = level.getServer();
if(server != null)
return server;
return ServerLifecycleHooks.getCurrentServer();
}
}

View File

@ -0,0 +1,78 @@
package org.embeddedt.modernfix.packet;
import com.mojang.datafixers.util.Pair;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFix;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
public class EntityIDSyncPacket {
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
this.map = map;
}
public Map<Class<? extends Entity>, List<Pair<String, Integer>>> getFieldInfo() {
return this.map;
}
public EntityIDSyncPacket() {
this.map = new HashMap<>();
}
public void serialize(FriendlyByteBuf buf) {
buf.writeVarInt(map.keySet().size());
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : map.entrySet()) {
buf.writeUtf(entry.getKey().getName());
buf.writeVarInt(entry.getValue().size());
for(Pair<String, Integer> field : entry.getValue()) {
buf.writeUtf(field.getFirst());
buf.writeVarInt(field.getSecond());
}
}
}
@SuppressWarnings("unchecked")
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
EntityIDSyncPacket self = new EntityIDSyncPacket();
int numEntityClasses = buf.readVarInt();
for(int i = 0; i < numEntityClasses; i++) {
String clzName = buf.readUtf();
try {
Class<?> clz;
try {
clz = Class.forName(clzName);
} catch(ClassNotFoundException e) {
ModernFix.LOGGER.warn("Entity class not found: {}", clzName);
break;
}
if(!Entity.class.isAssignableFrom(clz)) {
ModernFix.LOGGER.error("Not an entity: " + clzName);
break;
}
int numFields = buf.readVarInt();
for(int j = 0; j < numFields; j++) {
String fieldName = buf.readUtf();
int id = buf.readVarInt();
Field f = clz.getDeclaredField(fieldName);
if(!Modifier.isStatic(f.getModifiers()))
continue;
f.setAccessible(true);
if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) {
ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName);
continue;
}
self.map.computeIfAbsent((Class<? extends Entity>)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id));
}
} catch(ReflectiveOperationException e) {
ModernFix.LOGGER.error("Error deserializing packet", e);
}
}
return self;
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.packet;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
public class PacketHandler {
private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
new ResourceLocation(ModernFix.MODID, "main"),
() -> PROTOCOL_VERSION,
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION),
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION)
);
public static void register() {
int id = 1;
INSTANCE.registerMessage(id++, EntityIDSyncPacket.class, EntityIDSyncPacket::serialize, EntityIDSyncPacket::deserialize, ModernFixClient::handleEntityIDSync);
}
}

View File

@ -11,12 +11,119 @@ import org.lwjgl.stb.STBRPNode;
import org.lwjgl.stb.STBRPRect;
import org.lwjgl.stb.STBRectPack;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import java.lang.invoke.MethodHandle;
import java.sql.Ref;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/* Source: https://github.com/GTNewHorizons/lwjgl3ify/blob/f21364cd3d178aef863458a2faa1f5718a4e350d/src/main/java/me/eigenraven/lwjgl3ify/textures/StbStitcher.java */
public class StbStitcher {
/* Most of this logic is to allow use of LWJGL versions where coordinates are short and versions where they are int */
private static final MethodHandle MH_rect_shortSet, MH_rect_intSet, MH_rect_intX, MH_rect_intY, MH_rect_shortX,
MH_rect_shortY;
static {
MethodHandle shortM = null, intM = null;
List<ReflectiveOperationException> exceptions = new ArrayList<>();
try {
intM = publicLookup().findVirtual(STBRPRect.class, "set", methodType(STBRPRect.class,
int.class, /* id */
int.class,
int.class,
int.class,
int.class,
boolean.class));
} catch(ReflectiveOperationException e) {
exceptions.add(e);
}
try {
shortM = publicLookup().findVirtual(STBRPRect.class, "set", methodType(STBRPRect.class,
int.class, /* id */
short.class,
short.class,
short.class,
short.class,
boolean.class));
} catch(ReflectiveOperationException e) {
exceptions.add(e);
}
if(shortM == null && intM == null) {
IllegalStateException e = new IllegalStateException("An STBRPRect set method could not be located");
exceptions.forEach(e::addSuppressed);
throw e;
}
MH_rect_shortSet = shortM;
MH_rect_intSet = intM;
/* Now look for X methods */
exceptions.clear();
try {
intM = publicLookup().findVirtual(STBRPRect.class, "x", methodType(int.class));
} catch(ReflectiveOperationException e) {
exceptions.add(e);
}
try {
shortM = publicLookup().findVirtual(STBRPRect.class, "x", methodType(short.class));
} catch(ReflectiveOperationException e) {
exceptions.add(e);
}
if(shortM == null && intM == null) {
IllegalStateException e = new IllegalStateException("An STBRPRect x() method could not be located");
exceptions.forEach(e::addSuppressed);
throw e;
}
MH_rect_shortX = shortM;
MH_rect_intX = intM;
/* Assume that Y is the same */
try {
if(MH_rect_shortX != null) {
MH_rect_shortY = publicLookup().findVirtual(STBRPRect.class, "y", methodType(short.class));
MH_rect_intY = null;
} else { /* it must be int */
MH_rect_intY = publicLookup().findVirtual(STBRPRect.class, "y", methodType(int.class));
MH_rect_shortY = null;
}
} catch(ReflectiveOperationException e) {
throw new IllegalStateException("An STBRPRect y() method could not be located", e);
}
}
private static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) {
try {
if(MH_rect_shortSet != null)
return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)0, (short)0, false);
else
return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, 0, 0, false);
} catch(Throwable e) {
throw new AssertionError(e);
}
}
private static int getX(STBRPRect rect) {
try {
if(MH_rect_shortX != null)
return (short)MH_rect_shortX.invokeExact(rect);
else
return (int)MH_rect_intX.invokeExact(rect);
} catch(Throwable e) {
throw new AssertionError(e);
}
}
private static int getY(STBRPRect rect) {
try {
if(MH_rect_shortX != null)
return (short)MH_rect_shortY.invokeExact(rect);
else
return (int)MH_rect_intY.invokeExact(rect);
} catch(Throwable e) {
throw new AssertionError(e);
}
}
public static Pair<Pair<Integer, Integer>, List<LoadableSpriteInfo>> packRects(Stitcher.Holder[] holders) {
int holderSize = holders.length;
@ -36,7 +143,9 @@ public class StbStitcher {
int height = holder.height;
// The ID here is just the array index, for easy lookup later
rectBuf.get(j).set(j, (short)width, (short)height, (short)0, (short)0, false);
STBRPRect rect = rectBuf.get(j);
setWrapper(rect, j, width, height, 0, 0, false);
sqSize += (width * height);
}
@ -63,7 +172,7 @@ public class StbStitcher {
}
// Initialize the sprite now with the position and size that we've calculated so far
infoList.add(new LoadableSpriteInfo(holder.spriteInfo, width, height, rect.x(), rect.y()));
infoList.add(new LoadableSpriteInfo(holder.spriteInfo, width, height, getX(rect), getY(rect)));
//holder.spriteInfo.initSprite(size, size, rect.x(), rect.y(), false);
}

View File

@ -1,10 +1,22 @@
package org.embeddedt.modernfix.util;
import java.io.File;
import java.util.regex.Pattern;
public class FileUtil {
public static File childFile(File file) {
file.getParentFile().mkdirs();
return file;
}
private static final Pattern SLASH_PATTERN = Pattern.compile("(?:\\\\+|\\/+)");
/**
* Normalize a path by removing double slashes, etc.
* @param path input path
* @return a normalized version of the path
*/
public static String normalize(String path) {
return SLASH_PATTERN.matcher(path).replaceAll("/");
}
}

View File

@ -8,4 +8,7 @@ public net.minecraft.client.renderer.model.ModelBakery field_217849_F # unbakedC
public net.minecraft.client.renderer.texture.Stitcher$Holder
public net.minecraft.util.thread.BlockableEventLoop m_18699_()V # runAllTasks
public net.minecraft.server.MinecraftServer f_129726_ # nextTickTime
public net.minecraft.client.Minecraft f_90999_ # progressListener
public net.minecraft.client.Minecraft f_90999_ # progressListener
public-f net.minecraft.network.syncher.EntityDataAccessor f_135010_ # id
public-f net.minecraft.network.syncher.SynchedEntityData f_135345_ # itemsById
public-f net.minecraft.network.syncher.SynchedEntityData f_135346_ # lock

View File

@ -60,6 +60,6 @@ side = "BOTH"
modId = "jei"
mandatory = false
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange = "[9.9999,)"
versionRange = "[11,)"
ordering = "BEFORE"
side = "CLIENT"

View File

@ -1,5 +1,6 @@
{
"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.",
"asynclocator.map.locating": "Map (Locating...)",
"asynclocator.map.none": "Map (No nearby feature found)"
}

View File

@ -1,5 +1,6 @@
{
"modernfix.jei_load": "正在加载JEI这需要一点时间",
"asynclocator.map.locating": "地图(正在定位...",
"modernfix.jei_load": "正在加载JEI这需要一点时间。",
"modernfix.no_lazydfu": "现代化修复检测到DFU规则在游戏启动时已被编译。这会降低游戏启动速度。非常推荐安装DFU载入优化来解决这个问题。",
"asynclocator.map.locating": "地图(定位中……)",
"asynclocator.map.none": "地图(未能找到相关地物)"
}

View File

@ -32,6 +32,7 @@
],
"client": [
"core.MinecraftMixin",
"core.SynchedEntityDataMixin",
"feature.measure_time.MinecraftMixin",
"feature.reduce_loading_screen_freezes.ModelBakeryMixin",
"bugfix.concurrency.MinecraftMixin",
@ -56,6 +57,7 @@
"perf.cache_model_materials.VanillaModelMixin",
"perf.cache_model_materials.MultipartMixin",
"perf.faster_texture_stitching.StitcherMixin",
"perf.use_integrated_resources.LootTableHelperMixin",
"perf.faster_singleplayer_load.MinecraftServerMixin"
],
"injectors": {