diff options
| author | shedaniel <daniel@shedaniel.me> | 2023-01-22 00:08:47 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2023-01-22 00:14:02 +0800 |
| commit | 7dac5856acd5a1b4111f009d3c348a501f1bc397 (patch) | |
| tree | 9bd05e823a6d6db0605ff81264be339d0deddd62 | |
| parent | 3426ed6edef377ad690c44be3997a9be73df1638 (diff) | |
| download | RoughlyEnoughItems-7dac5856acd5a1b4111f009d3c348a501f1bc397.tar.gz RoughlyEnoughItems-7dac5856acd5a1b4111f009d3c348a501f1bc397.tar.bz2 RoughlyEnoughItems-7dac5856acd5a1b4111f009d3c348a501f1bc397.zip | |
Add support for c:hidden_from_recipe_viewers
9 files changed, 206 insertions, 3 deletions
diff --git a/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinClientPacketListener.java b/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinClientPacketListener.java index 1e1eb0d3a..5f355b9fa 100644 --- a/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinClientPacketListener.java +++ b/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinClientPacketListener.java @@ -26,6 +26,7 @@ package me.shedaniel.rei.mixin.fabric; import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket; import net.minecraft.world.item.crafting.RecipeManager; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -42,4 +43,9 @@ public class MixinClientPacketListener { private void handleUpdateRecipes(ClientboundUpdateRecipesPacket clientboundUpdateRecipesPacket, CallbackInfo ci) { RoughlyEnoughItemsCoreClient.PRE_UPDATE_RECIPES.invoker().update(recipeManager); } + + @Inject(method = "handleUpdateTags", at = @At("HEAD")) + private void handleUpdateTags(ClientboundUpdateTagsPacket packet, CallbackInfo ci) { + RoughlyEnoughItemsCoreClient.POST_UPDATE_TAGS.invoker().run(); + } }
\ No newline at end of file diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index bb8a221ae..70e6b0d83 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -28,7 +28,8 @@ ], "rei_client": [ "me.shedaniel.rei.plugin.client.DefaultClientPlugin", - "me.shedaniel.rei.plugin.client.runtime.DefaultClientRuntimePlugin" + "me.shedaniel.rei.plugin.client.runtime.DefaultClientRuntimePlugin", + "me.shedaniel.rei.plugin.client.runtime.HideIngredientsFromTagsPlugin" ] }, "accessWidener": "roughlyenoughitems.accessWidener", diff --git a/forge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java b/forge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java index 64dbf3d72..0ec02248c 100644 --- a/forge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java +++ b/forge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java @@ -35,6 +35,7 @@ import me.shedaniel.rei.api.common.plugins.REIServerPlugin; import me.shedaniel.rei.impl.init.PluginDetector; import me.shedaniel.rei.plugin.client.forge.DefaultClientPluginImpl; import me.shedaniel.rei.plugin.client.runtime.DefaultClientRuntimePlugin; +import me.shedaniel.rei.plugin.client.runtime.HideIngredientsFromTagsPlugin; import me.shedaniel.rei.plugin.common.forge.DefaultPluginImpl; import me.shedaniel.rei.plugin.common.runtime.DefaultRuntimePlugin; import net.minecraftforge.api.distmarker.Dist; @@ -178,6 +179,7 @@ public class PluginDetectorImpl implements PluginDetector { return () -> () -> { PluginView.getClientInstance().registerPlugin(wrapPlugin(Collections.singletonList("roughlyenoughitems"), new DefaultClientPluginImpl())); PluginView.getClientInstance().registerPlugin(wrapPlugin(Collections.singletonList("roughlyenoughitems"), new DefaultClientRuntimePlugin())); + PluginView.getClientInstance().registerPlugin(wrapPlugin(Collections.singletonList("roughlyenoughitems"), new HideIngredientsFromTagsPlugin())); // Legacy Annotation AnnotationUtils.<REIPlugin, REIClientPlugin>scanAnnotation(REIPlugin.class, REIClientPlugin.class::isAssignableFrom, (modId, plugin, clazz) -> { diff --git a/forge/src/main/java/me/shedaniel/rei/mixin/forge/MixinClientPacketListener.java b/forge/src/main/java/me/shedaniel/rei/mixin/forge/MixinClientPacketListener.java index 5911cb28d..989b0eb63 100644 --- a/forge/src/main/java/me/shedaniel/rei/mixin/forge/MixinClientPacketListener.java +++ b/forge/src/main/java/me/shedaniel/rei/mixin/forge/MixinClientPacketListener.java @@ -26,6 +26,7 @@ package me.shedaniel.rei.mixin.forge; import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket; +import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket; import net.minecraft.world.item.crafting.RecipeManager; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -42,4 +43,9 @@ public class MixinClientPacketListener { private void handleUpdateRecipes(ClientboundUpdateRecipesPacket clientboundUpdateRecipesPacket, CallbackInfo ci) { RoughlyEnoughItemsCoreClient.PRE_UPDATE_RECIPES.invoker().update(recipeManager); } + + @Inject(method = "handleUpdateTags", at = @At("HEAD")) + private void handleUpdateTags(ClientboundUpdateTagsPacket packet, CallbackInfo ci) { + RoughlyEnoughItemsCoreClient.POST_UPDATE_TAGS.invoker().run(); + } }
\ No newline at end of file diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java index 73643b59d..f1b1110e8 100644 --- a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java @@ -129,6 +129,7 @@ import java.util.stream.Stream; @Environment(EnvType.CLIENT) public class RoughlyEnoughItemsCoreClient { public static final Event<ClientRecipeUpdateEvent> PRE_UPDATE_RECIPES = EventFactory.createLoop(); + public static final Event<Runnable> POST_UPDATE_TAGS = EventFactory.createLoop(); public static boolean isLeftMousePressed = false; private static final ExecutorService RELOAD_PLUGINS = Executors.newSingleThreadScheduledExecutor(task -> { Thread thread = new Thread(task, "REI-ReloadPlugins"); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java index a0ed5189d..6d899d5a5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java @@ -272,7 +272,7 @@ public class FilteringRulesScreen extends Screen { Function<Boolean, Component> function = bool -> { return Component.translatable("rule.roughlyenoughitems.filtering.search.show." + bool); }; - Map<FilteringContextType, Set<HashedEntryStackWrapper>> stacks = FilteringLogic.hidden(FilteringLogic.getRules(), false, false, EntryRegistry.getInstance().getEntryStacks().collect(Collectors.toList())); + Map<FilteringContextType, Set<HashedEntryStackWrapper>> stacks = FilteringLogic.hidden(List.of(r), false, false, EntryRegistry.getInstance().getEntryStacks().collect(Collectors.toList())); entryConsumer.accept(new SubRulesEntry(rule, () -> function.apply(true), Collections.singletonList(new SearchFilteringRuleType.EntryStacksRuleEntry(rule, diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/FilteringLogic.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/FilteringLogic.java index fe64c6975..7dd162834 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/FilteringLogic.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/FilteringLogic.java @@ -24,8 +24,11 @@ package me.shedaniel.rei.impl.common.entry.type; import com.google.common.base.Stopwatch; +import me.shedaniel.rei.api.client.config.ConfigManager; import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.entry.filtering.FilteringRule; +import me.shedaniel.rei.api.client.entry.filtering.FilteringRuleType; +import me.shedaniel.rei.api.client.entry.filtering.FilteringRuleTypeRegistry; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; @@ -52,7 +55,18 @@ public class FilteringLogic { } public static List<FilteringRule<?>> getRules() { - return ((ConfigObjectImpl) ConfigObject.getInstance()).getFilteringRules(); + List<FilteringRule<?>> rules = ((ConfigObjectImpl) ConfigObject.getInstance()).getFilteringRules(); + boolean added = false; + for (FilteringRuleType<?> type : FilteringRuleTypeRegistry.getInstance()) { + if (type.isSingular() && rules.stream().noneMatch(filteringRule -> filteringRule.getType().equals(type))) { + rules.add(type.createNew()); + added = true; + } + } + if (added) { + ConfigManager.getInstance().saveConfig(); + } + return rules; } private static LinkedHashMap<FilteringRule<?>, Object> prepareCache(List<FilteringRule<?>> rules, boolean async, Collection<EntryStack<?>> entries) { diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/HideIngredientsFromTagsPlugin.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/HideIngredientsFromTagsPlugin.java new file mode 100644 index 000000000..8b641380f --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/HideIngredientsFromTagsPlugin.java @@ -0,0 +1,171 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.plugin.client.runtime; + +import it.unimi.dsi.fastutil.longs.LongCollection; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; +import me.shedaniel.rei.api.client.entry.filtering.*; +import me.shedaniel.rei.api.client.plugins.REIClientPlugin; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; +import me.shedaniel.rei.api.common.util.EntryIngredients; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.impl.common.InternalLogger; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A plugin to hide any ingredients from the <code>c:hidden_from_recipe_viewers</code> tag. + * This can be implemented in a few lines, but is instead implemented as a filtering rule type + * such that users may view the list of hidden ingredients separately in config. + */ +@Environment(EnvType.CLIENT) +@ApiStatus.Internal +public class HideIngredientsFromTagsPlugin implements REIClientPlugin { + private static final ResourceLocation HIDDEN_TAG = new ResourceLocation("c:hidden_from_recipe_viewers"); + + static { + FilteringRuleTypeRegistry.getInstance().register(new ResourceLocation("roughlyenoughitems", "hidden_from_recipe_viewers"), HideTagsFilteringRuleType.INSTANCE); + RoughlyEnoughItemsCoreClient.POST_UPDATE_TAGS.register(HideTagsFilteringRule.INSTANCE::markDirty); + } + + private enum HideTagsFilteringRuleType implements FilteringRuleType<HideTagsFilteringRule> { + INSTANCE; + + @Override + public CompoundTag saveTo(HideTagsFilteringRule rule, CompoundTag tag) { + return tag; + } + + @Override + public HideTagsFilteringRule readFrom(CompoundTag tag) { + return HideTagsFilteringRule.INSTANCE; + } + + @Override + public Component getTitle(HideTagsFilteringRule rule) { + return Component.translatable("rule.roughlyenoughitems.filtering.hide.tag"); + } + + @Override + public Component getSubtitle(HideTagsFilteringRule rule) { + return Component.translatable("rule.roughlyenoughitems.filtering.hide.tag.subtitle"); + } + + @Override + public HideTagsFilteringRule createNew() { + return HideTagsFilteringRule.INSTANCE; + } + + @Override + public boolean isSingular() { + return true; + } + } + + private enum HideTagsFilteringRule implements FilteringRule<HideTagsFilteringRule.Cache> { + INSTANCE; + + private Cache cache; + + private record Cache(EntryIngredient ingredient, LongSet hashes) {} + + @Override + public FilteringRuleType<? extends FilteringRule<HideTagsFilteringRule.Cache>> getType() { + return HideTagsFilteringRuleType.INSTANCE; + } + + @Override + public Cache prepareCache(boolean async) { + try { + EntryIngredient ingredient = EntryIngredient.builder() + .addAll(EntryIngredients.ofItemTag(TagKey.create(Registry.ITEM_REGISTRY, HIDDEN_TAG))) + .addAll(EntryIngredients.ofItemTag(TagKey.create(Registry.BLOCK_REGISTRY, HIDDEN_TAG))) + .addAll(EntryIngredients.ofFluidTag(TagKey.create(Registry.FLUID_REGISTRY, HIDDEN_TAG))) + .build(); + LongSet hashes = new LongOpenHashSet(); + for (EntryStack<?> stack : ingredient) { + hashes.add(EntryStacks.hashExact(stack)); + } + return this.cache = new Cache(ingredient, hashes); + } catch (Throwable e) { + InternalLogger.getInstance().warn("Failed to load hidden ingredients from tag, falling back to empty cache.", e); + return this.cache = null; + } + } + + @Override + public FilteringResult processFilteredStacks(FilteringContext context, FilteringResultFactory resultFactory, HideTagsFilteringRule.Cache cache, boolean async) { + FilteringResult result = resultFactory.create(); + if (cache != null) { + process(result, context.getShownStacks(), context.getShownExactHashes(), cache); + process(result, context.getUnsetStacks(), context.getUnsetExactHashes(), cache); + } + return result; + } + + private void process(FilteringResult result, Collection<EntryStack<?>> stacks, LongCollection hashes, HideTagsFilteringRule.Cache cache) { + Iterator<EntryStack<?>> stackIterator = stacks.iterator(); + LongIterator hashIterator = hashes.iterator(); + while (stackIterator.hasNext()) { + EntryStack<?> stack = stackIterator.next(); + long hash = hashIterator.nextLong(); + if (cache.hashes().contains(hash)) { + result.hide(stack); + } + } + } + + private void markDirty() { + InternalLogger.getInstance().debug("Marking hidden ingredients from tag cache as dirty."); + if (this.cache != null) { + this.markDirty(this.cache.ingredient(), this.cache.hashes()); + } + try { + VanillaEntryTypes.ITEM.getDefinition(); + } catch (NullPointerException ignored) { + this.cache = null; + return; + } + this.cache = this.prepareCache(false); + if (this.cache != null) { + this.markDirty(this.cache.ingredient(), this.cache.hashes()); + } + InternalLogger.getInstance().debug("Marked %d hidden ingredients from tag cache as dirty.", this.cache == null ? 0 : this.cache.hashes().size()); + } + } +} diff --git a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index b4af25079..680d7019f 100755 --- a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -368,6 +368,8 @@ "rule.roughlyenoughitems.filtering.search.show.false": "Hide", "rule.roughlyenoughitems.filtering.basic": "Basic Plugin Filtering", "rule.roughlyenoughitems.filtering.basic.subtitle": "Filter entries by REI plugins. This is not configurable here.", + "rule.roughlyenoughitems.filtering.hide.tag": "Tag Filtering", + "rule.roughlyenoughitems.filtering.hide.tag.subtitle": "Filter entries by 'c:hidden_from_recipe_viewers'. This is not configurable here.", "language.roughlyenoughitems.english": "English", "language.roughlyenoughitems.japanese": "Japanese", "language.roughlyenoughitems.chinese_simplified": "Chinese Simplified", |
