diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/edge_chunk_not_saved/ChunkManagerMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/edge_chunk_not_saved/ChunkManagerMixin.java deleted file mode 100644 index 1b635a8f..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/edge_chunk_not_saved/ChunkManagerMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.bugfix.edge_chunk_not_saved; - -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ChunkMap; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.function.Predicate; - -/* https://github.com/SuperCoder7979/chunksavingfix-fabric/blob/main/src/main/java/supercoder79/chunksavingfix/mixin/MixinThreadedAnvilChunkStorage.java */ -@Mixin(ChunkMap.class) -public class ChunkManagerMixin { - // TODO: hits both at the moment- check and re-evaluate - @ModifyArg(method = "saveAllChunks(Z)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 0), require = 0) - private Predicate alwaysAccessibleFlush(Predicate chunkHolder) { - return c -> true; - } - @ModifyArg(method = "saveAllChunks(Z)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 1), require = 0) - private Predicate allowProtoChunkFlush(Predicate chunk) { - return c -> c instanceof ProtoChunk || c instanceof ImposterProtoChunk || c instanceof LevelChunk; - } - - @Redirect(method = "saveChunkIfNeeded", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkHolder;wasAccessibleSinceLastSave()Z"), require = 0) - private boolean alwaysAccessible(ChunkHolder chunkHolder) { - return true; - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java index 43c58aca..7d20362c 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_item_rendering/ItemRendererMixin.java @@ -2,55 +2,34 @@ package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.SimpleBakedModel; -import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import org.embeddedt.modernfix.render.FastItemRenderType; import org.embeddedt.modernfix.render.RenderState; -import org.spongepowered.asm.mixin.Final; +import org.embeddedt.modernfix.render.SimpleItemModelView; 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.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.List; - -@Mixin(ItemRenderer.class) +@Mixin(value = ItemRenderer.class, priority = 600) public abstract class ItemRendererMixin { - @Shadow @Final private ItemColors itemColors; - - private final RandomSource dummyRandom = RandomSource.createNewThreadLocalInstance(); - - private static final float[] COLOR_MULTIPLIER = new float[]{1.0F, 1.0F, 1.0F, 1.0F}; - private ItemDisplayContext transformType; + private final SimpleItemModelView modelView = new SimpleItemModelView(); @Inject(method = "render", at = @At("HEAD")) private void markRenderingType(ItemStack itemStack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) { this.transformType = transformType; } - private static final Direction[] ITEM_DIRECTIONS = new Direction[] { Direction.SOUTH }; - private static final Direction[] BLOCK_DIRECTIONS = new Direction[] { Direction.UP, Direction.EAST, Direction.NORTH }; - - private boolean isCorrectDirectionForType(FastItemRenderType type, Direction direction) { - if(type == FastItemRenderType.SIMPLE_ITEM) - return direction == Direction.SOUTH; - else { - return direction == Direction.UP || direction == Direction.EAST || direction == Direction.NORTH; - } - } - /** * If a model * - is a vanilla item model (SimpleBakedModel), @@ -59,8 +38,8 @@ public abstract class ItemRendererMixin { * we do not need to go through the process of rendering every quad. Just render the south ones (the ones facing the * camera). */ - @Inject(method = "renderModelLists", at = @At("HEAD"), cancellable = true) - private void fasterItemRender(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, CallbackInfo ci) { + @ModifyVariable(method = "renderModelLists", at = @At("HEAD"), index = 1, argsOnly = true) + private BakedModel useSimpleWrappedItemModel(BakedModel model, BakedModel arg, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer) { if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) { FastItemRenderType type; ItemTransform transform = model.getTransforms().gui; @@ -69,26 +48,12 @@ public abstract class ItemRendererMixin { else if(stack.getItem() instanceof BlockItem && isBlockTransforms(transform)) type = FastItemRenderType.SIMPLE_BLOCK; else - return; - ci.cancel(); - PoseStack.Pose pose = matrixStack.last(); - int[] combinedLights = new int[] {combinedLight, combinedLight, combinedLight, combinedLight}; - Direction[] directions = type == FastItemRenderType.SIMPLE_ITEM ? ITEM_DIRECTIONS : BLOCK_DIRECTIONS; - for(Direction direction : directions) { - List culledFaces = model.getQuads(null, direction, dummyRandom); - /* check size to avoid instantiating iterator when the list is empty */ - if(culledFaces.size() > 0) { - for(BakedQuad quad : culledFaces) { - render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay); - } - } - } - List unculledFaces = model.getQuads(null, null, dummyRandom); - for(BakedQuad quad : unculledFaces) { - if(isCorrectDirectionForType(type, quad.getDirection())) - render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay); - } - } + return model; + modelView.setItem(model); + modelView.setType(type); + return modelView; + } else + return model; } private boolean isBlockTransforms(ItemTransform transform) { @@ -96,16 +61,4 @@ public abstract class ItemRendererMixin { && transform.rotation.y() == 225f && transform.rotation.z() == 0f; } - - private void render2dItemFace(BakedQuad quad, ItemStack stack, VertexConsumer buffer, PoseStack.Pose pose, int[] combinedLights, int combinedOverlay) { - int i = -1; - if (quad.isTinted()) { - i = this.itemColors.getColor(stack, quad.getTintIndex()); - } - - float f = (float)(i >> 16 & 255) / 255.0F; - float f1 = (float)(i >> 8 & 255) / 255.0F; - float f2 = (float)(i & 255) / 255.0F; - buffer.putBulkData(pose, quad, COLOR_MULTIPLIER, f, f1, f2, combinedLights, combinedOverlay, true); - } } diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 8ecd0453..c375e443 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -59,6 +59,8 @@ public class ModernFixEarlyConfig { private final Set mixinOptions = new ObjectOpenHashSet<>(); private final Map mixinsMissingMods = new Object2ObjectOpenHashMap<>(); + public static boolean isFabric = false; + public Map getPermanentlyDisabledMixins() { return mixinsMissingMods; } @@ -71,6 +73,8 @@ public class ModernFixEarlyConfig { if(stream == null) continue; try(Reader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + if(configFile.contains("fabric")) + isFabric = true; JsonObject configObject = (JsonObject)new JsonParser().parse(reader); JsonArray mixinList = configObject.getAsJsonArray("mixins"); String packageName = configObject.get("package").getAsString().replace('.', '/'); @@ -178,15 +182,20 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.perf.thread_priorities", "smoothboot"); disableIfModPresent("mixin.perf.boost_worker_count", "smoothboot"); disableIfModPresent("mixin.perf.async_jei", "modernui"); - disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge"); + disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge" ,"skyblockbuilder"); disableIfModPresent("mixin.bugfix.mc218112", "performant"); disableIfModPresent("mixin.bugfix.remove_block_chunkloading", "performant"); disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me"); + // DimThread makes changes to the server chunk manager (understandably), C2ME probably does the same + disableIfModPresent("mixin.bugfix.chunk_deadlock", "c2me", "dimthread"); disableIfModPresent("mixin.perf.reuse_datapacks", "tac"); disableIfModPresent("mixin.launch.class_search_cache", "optifine"); disableIfModPresent("mixin.perf.faster_texture_stitching", "optifine"); disableIfModPresent("mixin.perf.datapack_reload_exceptions", "cyanide"); disableIfModPresent("mixin.perf.faster_texture_loading", "stitch", "optifine", "changed"); + if(isFabric) { + disableIfModPresent("mixin.bugfix.packet_leak", "memoryleakfix"); + } } private void disableIfModPresent(String configName, String... ids) { diff --git a/common/src/main/java/org/embeddedt/modernfix/render/SimpleItemModelView.java b/common/src/main/java/org/embeddedt/modernfix/render/SimpleItemModelView.java new file mode 100644 index 00000000..b1a27b0f --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/render/SimpleItemModelView.java @@ -0,0 +1,94 @@ +package org.embeddedt.modernfix.render; + +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Wrapper class that presents a fake view of item models (only showing the simple front-facing quads), rather + * than every quad. + */ +public class SimpleItemModelView implements BakedModel { + private BakedModel wrappedItem; + private FastItemRenderType type; + + public void setItem(BakedModel model) { + this.wrappedItem = model; + } + + public void setType(FastItemRenderType type) { + this.type = type; + } + + private boolean isCorrectDirectionForType(Direction direction) { + if(type == FastItemRenderType.SIMPLE_ITEM) + return direction == Direction.SOUTH; + else { + return direction == Direction.UP || direction == Direction.EAST || direction == Direction.NORTH; + } + } + + private final List nullQuadList = new ObjectArrayList<>(); + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) { + if(side != null) { + return isCorrectDirectionForType(side) ? wrappedItem.getQuads(state, side, rand) : ImmutableList.of(); + } else { + nullQuadList.clear(); + List realList = wrappedItem.getQuads(state, null, rand); + for(int i = 0; i < realList.size(); i++) { + BakedQuad quad = realList.get(i); + if(isCorrectDirectionForType(quad.getDirection())) { + nullQuadList.add(quad); + } + } + return nullQuadList; + } + } + + @Override + public boolean useAmbientOcclusion() { + return wrappedItem.useAmbientOcclusion(); + } + + @Override + public boolean isGui3d() { + return wrappedItem.isGui3d(); + } + + @Override + public boolean usesBlockLight() { + return wrappedItem.usesBlockLight(); + } + + @Override + public boolean isCustomRenderer() { + return wrappedItem.isCustomRenderer(); + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return wrappedItem.getParticleIcon(); + } + + @Override + public ItemTransforms getTransforms() { + return wrappedItem.getTransforms(); + } + + @Override + public ItemOverrides getOverrides() { + return wrappedItem.getOverrides(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java index f48bb6ac..15debcd0 100644 --- a/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java @@ -1,22 +1,28 @@ package org.embeddedt.modernfix.searchtree; +import com.google.common.base.Predicates; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.impl.client.search.AsyncSearchManager; +import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl; +import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; import net.minecraft.client.searchtree.RefreshableSearchTree; import net.minecraft.world.item.ItemStack; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; public class REIBackedSearchTree extends DummySearchTree { - private final AsyncSearchManager searchManager = new AsyncSearchManager(EntryRegistry.getInstance()::getPreFilteredList, () -> { - return stack -> true; - }, EntryStack::normalize); + private final AsyncSearchManager searchManager = createSearchManager(); private final boolean filteringByTag; private String lastSearchText = ""; @@ -39,14 +45,23 @@ public class REIBackedSearchTree extends DummySearchTree { if(!pSearchText.equals(lastSearchText)) { listCache.clear(); this.searchManager.updateFilter(pSearchText); - List> stacks; + List stacks; try { stacks = this.searchManager.getNow(); } catch(RuntimeException e) { ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e); stacks = Collections.emptyList(); } - for(EntryStack stack : stacks) { + for(Object o : stacks) { + EntryStack stack; + if(o instanceof EntryStack) + stack = (EntryStack)o; + else if(o instanceof HashedEntryStackWrapper) { + stack = ((HashedEntryStackWrapper)o).unwrap(); + } else { + ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName()); + continue; + } if(stack.getType() == VanillaEntryTypes.ITEM) { listCache.add(stack.cheatsAs().getValue()); } @@ -56,6 +71,50 @@ public class REIBackedSearchTree extends DummySearchTree { return listCache; } + @SuppressWarnings({"unchecked", "rawtypes"}) + private static AsyncSearchManager createSearchManager() { + Method m, normalizeMethod; + try { + try { + m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList"); + m.setAccessible(true); + normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize"); + normalizeMethod.setAccessible(true); + } catch(NoSuchMethodException e) { + m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList"); + m.setAccessible(true); + normalizeMethod = EntryStack.class.getDeclaredMethod("normalize"); + normalizeMethod.setAccessible(true); + } + final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m); + final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod); + final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance(); + Supplier stackListSupplier = () -> { + try { + return (List)getListMethod.invokeExact(registry); + } catch(Throwable e) { + if(e instanceof RuntimeException) + throw (RuntimeException)e; + throw new RuntimeException(e); + } + }; + UnaryOperator normalizeOperator = o -> { + try { + return normalize.invoke(o); + } catch(Throwable e) { + if(e instanceof RuntimeException) + throw (RuntimeException)e; + throw new RuntimeException(e); + } + }; + return new AsyncSearchManager(stackListSupplier, () -> { + return Predicates.alwaysTrue(); + }, normalizeOperator); + } catch(ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() { @Override public RefreshableSearchTree getSearchTree(boolean tag) { diff --git a/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java b/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java index 05bd219b..1202500b 100644 --- a/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java +++ b/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java @@ -141,6 +141,7 @@ public class CanonizingStringMap implements Map { } public static CanonizingStringMap deepCopy(CanonizingStringMap inputMap, Function deepCopier) { + Objects.requireNonNull(deepCopier); Object2ObjectMap copiedBackingMap; int size = inputMap.backingMap.size(); if(size > GROWTH_THRESHOLD) { @@ -148,7 +149,8 @@ public class CanonizingStringMap implements Map { } else copiedBackingMap = new Object2ObjectArrayMap<>(size); inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> { - copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue())); + if(entry.getKey() != null && entry.getValue() != null) + copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue())); }); return new CanonizingStringMap<>(copiedBackingMap); }