port 1.19.2 changes

This commit is contained in:
Relentless 2023-04-18 23:55:06 +02:00
parent 2c1f4a5641
commit 84d5a94630
No known key found for this signature in database
GPG key ID: 50C5FD225130D790
35 changed files with 666 additions and 250 deletions

View file

@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog],
and this project adheres to [Semantic Versioning].
## [0.4.0] - 2023-04-18
### WARNING
This update features a new config option inside the `unify.json` called `tagOwnerships`.<br>
Since it's a top-level option, this won't reset your config and all other options should be preserved. However,
an automatic backup will be created in case something goes wrong.
### Added
- tag ownerships
- a new milestone feature allowing tags to be converted to other tags
- this allows unifying inconsistent tags like `forge:coals` and `forge:gems/coal`
- you can read more about it in the [wiki][tag-ownerships]
### Changed
- improved stone strata lookup speed
### Fixed
- some Mekanism recipes not being unified
- log spam on multiple preferred tags
- JEI indicator not showing anymore with new JEI versions
<!-- Links -->
[tag-ownerships]: https://github.com/AlmostReliable/almostunified/wiki/Unification-Config#tag-ownerships
## [0.3.8] - 2023-04-06
### Added
@ -22,5 +46,6 @@ Initial 1.19.4 release!
[semantic versioning]: https://semver.org/spec/v2.0.0.html
<!-- Versions -->
[0.4.0]: https://github.com/AlmostReliable/almostunified/releases/tag/v1.19.4-0.4.0-beta
[0.3.8]: https://github.com/AlmostReliable/almostunified/releases/tag/v1.19.4-0.3.8-beta
[0.3.7]: https://github.com/AlmostReliable/almostunified/releases/tag/v1.19.4-0.3.7-beta

View file

@ -29,8 +29,8 @@ dependencies {
modCompileOnly("me.shedaniel:RoughlyEnoughItems-api:$reiVersion") // required for common rei plugin
compileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // required to disable rei compat layer on jei plugin
testCompileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // don't question this, it's required for compiling
modCompileOnlyApi("mezz.jei:jei-$minecraftVersion-common:$jeiVersion") // required for common jei plugin
modCompileOnly("mezz.jei:jei-$minecraftVersion-gui:$jeiVersion") // required for jei mixin
modCompileOnly("mezz.jei:jei-$minecraftVersion-lib:$jeiVersion") // required for common jei plugin and mixin
modCompileOnly("mezz.jei:jei-$minecraftVersion-common-api:$jeiVersion") // required for common jei plugin and mixin
// The Fabric loader is required here to use the @Environment annotations and to get the mixin dependencies.
// Do NOT use other classes from the Fabric loader!

View file

@ -1,7 +1,10 @@
package com.almostreliable.unified;
import com.almostreliable.unified.config.Config;
import com.almostreliable.unified.config.ServerConfigs;
import com.almostreliable.unified.config.StartupConfig;
import com.almostreliable.unified.utils.TagOwnerships;
import com.google.common.base.Preconditions;
import net.minecraft.tags.TagManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -15,6 +18,8 @@ public final class AlmostUnified {
@Nullable private static AlmostUnifiedRuntime RUNTIME;
@Nullable private static TagManager TAG_MANGER;
@Nullable private static StartupConfig STARTUP_CONFIG;
@Nullable private static ServerConfigs SERVER_CONFIGS;
@Nullable private static TagOwnerships TAG_OWNERSHIPS;
public static StartupConfig getStartupConfig() {
if (STARTUP_CONFIG == null) {
@ -34,15 +39,22 @@ public final class AlmostUnified {
return RUNTIME;
}
public static void reloadRuntime() {
if (TAG_MANGER == null) {
throw new IllegalStateException("Internal error. TagManager was not updated correctly");
public static void onTagManagerReload(TagManager tagManager) {
TAG_MANGER = tagManager;
SERVER_CONFIGS = ServerConfigs.load();
var uc = SERVER_CONFIGS.getUnifyConfig();
TAG_OWNERSHIPS = new TagOwnerships(uc.bakeTags(), uc.getTagOwnerships());
}
RUNTIME = AlmostUnifiedRuntimeImpl.create(TAG_MANGER);
public static void onReloadRecipeManager() {
Preconditions.checkNotNull(TAG_MANGER, "TagManager was not loaded correctly");
Preconditions.checkNotNull(SERVER_CONFIGS, "ServerConfigs were not loaded correctly");
RUNTIME = AlmostUnifiedRuntimeImpl.create(TAG_MANGER, SERVER_CONFIGS);
}
public static void updateTagManager(TagManager tm) {
TAG_MANGER = tm;
public static TagOwnerships getTagOwnerships() {
Preconditions.checkNotNull(TAG_OWNERSHIPS, "TagOwnerships were not loaded correctly");
return TAG_OWNERSHIPS;
}
}

View file

@ -5,21 +5,25 @@ import com.almostreliable.unified.config.Config;
import com.almostreliable.unified.config.UnifyConfig;
import com.almostreliable.unified.utils.ReplacementMap;
import com.almostreliable.unified.utils.TagMap;
import com.almostreliable.unified.utils.TagOwnerships;
import com.almostreliable.unified.utils.UnifyTag;
import com.google.gson.JsonElement;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
// TODO: Implement sync so it's not just a fallback
public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
@Nullable private static AlmostUnifiedFallbackRuntime INSTANCE;
@Nullable private UnifyConfig config;
@Nullable private ReplacementMap replacementMap;
@Nullable private TagMap filteredTagMap;
@Nullable private ReplacementMap replacementMap;
@Nullable private TagOwnerships tagOwnerships;
public static AlmostUnifiedFallbackRuntime getInstance() {
if (INSTANCE == null) {
@ -32,8 +36,9 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
public void reload() {
config = null;
replacementMap = null;
filteredTagMap = null;
replacementMap = null;
tagOwnerships = null;
build();
}
@ -46,11 +51,12 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
}
public void build() {
var config = getConfig();
List<UnifyTag<Item>> unifyTags = config.bakeTags();
filteredTagMap = TagMap.create(unifyTags).filtered($ -> true, config::includeItem);
StoneStrataHandler stoneStrataHandler = getStoneStrataHandler(config);
replacementMap = new ReplacementMap(Objects.requireNonNull(filteredTagMap), stoneStrataHandler, config);
var uc = getConfig();
Set<UnifyTag<Item>> unifyTags = uc.bakeTags();
filteredTagMap = TagMap.create(unifyTags).filtered($ -> true, uc::includeItem);
StoneStrataHandler stoneStrataHandler = getStoneStrataHandler(uc);
replacementMap = new ReplacementMap(filteredTagMap, stoneStrataHandler, uc);
tagOwnerships = new TagOwnerships(unifyTags, uc.getTagOwnerships());
}
private static StoneStrataHandler getStoneStrataHandler(UnifyConfig config) {

View file

@ -65,7 +65,7 @@ public class AlmostUnifiedLookupImpl implements AlmostUnifiedLookup {
.getRuntime()
.getFilteredTagMap()
.map(tagMap -> tagMap
.getItems(asUnifyTag)
.getItemsByTag(asUnifyTag)
.stream()
.flatMap(rl -> BuiltInRegistries.ITEM.getOptional(rl).stream())
.collect(Collectors.toSet()))

View file

@ -1,9 +1,9 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.StoneStrataHandler;
import com.almostreliable.unified.config.Config;
import com.almostreliable.unified.config.DebugConfig;
import com.almostreliable.unified.config.DuplicationConfig;
import com.almostreliable.unified.config.ServerConfigs;
import com.almostreliable.unified.config.UnifyConfig;
import com.almostreliable.unified.recipe.RecipeDumper;
import com.almostreliable.unified.recipe.RecipeTransformer;
@ -11,20 +11,17 @@ import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
import com.almostreliable.unified.utils.FileUtils;
import com.almostreliable.unified.utils.ReplacementMap;
import com.almostreliable.unified.utils.TagMap;
import com.almostreliable.unified.utils.UnifyTag;
import com.google.gson.JsonElement;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagManager;
import net.minecraft.world.item.Item;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
private final RecipeHandlerFactory recipeHandlerFactory;
private final TagMap filteredTagMap;
private final DuplicationConfig dupConfig;
@ -32,32 +29,48 @@ public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
private final DebugConfig debugConfig;
private final ReplacementMap replacementMap;
private AlmostUnifiedRuntimeImpl(RecipeHandlerFactory recipeHandlerFactory, TagMap tagMap, DuplicationConfig dupConfig, UnifyConfig unifyConfig, DebugConfig debugConfig) {
private AlmostUnifiedRuntimeImpl(
RecipeHandlerFactory recipeHandlerFactory,
TagMap filteredTagMap,
ReplacementMap replacementMap,
DuplicationConfig dupConfig,
UnifyConfig unifyConfig,
DebugConfig debugConfig
) {
this.recipeHandlerFactory = recipeHandlerFactory;
this.filteredTagMap = filteredTagMap;
this.replacementMap = replacementMap;
this.dupConfig = dupConfig;
this.unifyConfig = unifyConfig;
this.debugConfig = debugConfig;
List<UnifyTag<Item>> allowedTags = unifyConfig.bakeTags();
this.filteredTagMap = tagMap.filtered(allowedTags::contains, unifyConfig::includeItem);
StoneStrataHandler stoneStrataHandler = StoneStrataHandler.create(unifyConfig.getStoneStrata(),
AlmostUnifiedPlatform.INSTANCE.getStoneStrataTags(unifyConfig.getStoneStrata()),
tagMap);
this.replacementMap = new ReplacementMap(filteredTagMap, stoneStrataHandler, unifyConfig);
}
public static AlmostUnifiedRuntimeImpl create(TagManager tagManager) {
Objects.requireNonNull(tagManager);
public static AlmostUnifiedRuntimeImpl create(TagManager tagManager, ServerConfigs serverConfigs) {
createGitIgnoreIfNotExists();
DuplicationConfig dupConfig = Config.load(DuplicationConfig.NAME, new DuplicationConfig.Serializer());
UnifyConfig unifyConfig = Config.load(UnifyConfig.NAME, new UnifyConfig.Serializer());
DebugConfig debugConfig = Config.load(DebugConfig.NAME, new DebugConfig.Serializer());
DuplicationConfig dupConfig = serverConfigs.getDupConfig();
UnifyConfig unifyConfig = serverConfigs.getUnifyConfig();
DebugConfig debugConfig = serverConfigs.getDebugConfig();
RecipeHandlerFactory factory = new RecipeHandlerFactory();
AlmostUnifiedPlatform.INSTANCE.bindRecipeHandlers(factory);
TagMap tagMap = TagMap.create(tagManager);
return new AlmostUnifiedRuntimeImpl(factory, tagMap, dupConfig, unifyConfig, debugConfig);
var allowedTags = unifyConfig.bakeTags();
TagMap globalTagMap = TagMap.create(tagManager);
TagMap filteredTagMap = globalTagMap.filtered(allowedTags::contains, unifyConfig::includeItem);
StoneStrataHandler stoneStrataHandler = StoneStrataHandler.create(unifyConfig.getStoneStrata(),
AlmostUnifiedPlatform.INSTANCE.getStoneStrataTags(unifyConfig.getStoneStrata()), globalTagMap);
var replacementMap = new ReplacementMap(filteredTagMap, stoneStrataHandler, unifyConfig);
return new AlmostUnifiedRuntimeImpl(
factory,
filteredTagMap,
replacementMap,
dupConfig,
unifyConfig,
debugConfig
);
}
private static void createGitIgnoreIfNotExists() {

View file

@ -14,13 +14,18 @@ public class StoneStrataHandler {
private final List<String> stoneStrata;
private final Pattern tagMatcher;
private final TagMap stoneStrataTagMap;
// don't clear the caches, so they are available for the runtime and KubeJS binding
// the runtime holding this handler is automatically yeeted on reload
private final Map<UnifyTag<?>, Boolean> stoneStrataTagCache;
private final Map<ResourceLocation, String> stoneStrataCache;
private StoneStrataHandler(List<String> stoneStrata, Pattern tagMatcher, TagMap stoneStrataTagMap) {
this.stoneStrata = createSortedStoneStrata(stoneStrata);
this.tagMatcher = tagMatcher;
this.stoneStrataTagMap = stoneStrataTagMap;
this.stoneStrataTagCache = new HashMap<>();
this.stoneStrataCache = new HashMap<>();
}
/**
@ -46,16 +51,27 @@ public class StoneStrataHandler {
}
/**
* Returns the stone strata from given item. Method works on the requirement that it's an item which has a stone strata.
* Use {@link #isStoneStrataTag(UnifyTag)} to fill this requirement.
* Returns the stone strata from the given item. Assumes that the item has a stone strata tag.
* Use {@link #isStoneStrataTag(UnifyTag)} to ensure this requirement.
*
* @param item The item to get the stone strata from.
* @return The stone strata of the item. Clean stone strata returns an empty string for later sorting as a
* fallback variant.
*/
public String getStoneStrata(ResourceLocation item) {
return stoneStrataCache.computeIfAbsent(item, this::computeStoneStrata);
}
/**
* Implementation logic for {@link #getStoneStrata(ResourceLocation)}.
*
* @param item The item to get the stone strata from.
* @return The stone strata of the item. Clean stone strata returns an empty string for later sorting as a
* fallback variant.
*/
private String computeStoneStrata(ResourceLocation item) {
String strata = stoneStrataTagMap
.getTags(item)
.getTagsByItem(item)
.stream()
.findFirst()
.map(UnifyTag::location)
@ -88,8 +104,4 @@ public class StoneStrataHandler {
public boolean isStoneStrataTag(UnifyTag<Item> tag) {
return stoneStrataTagCache.computeIfAbsent(tag, t -> tagMatcher.matcher(t.location().toString()).matches());
}
public void clearCache() {
stoneStrataTagCache.clear();
}
}

View file

@ -29,6 +29,8 @@ public final class RecipeConstants {
// mekanism
public static final String MAIN_INPUT = "mainInput";
public static final String MAIN_OUTPUT = "mainOutput";
public static final String ITEM_INPUT = "itemInput";
public static final String ITEM_OUTPUT = "itemOutput";
public static final String SECONDARY_OUTPUT = "secondaryOutput";
// modern industrialization

View file

@ -48,6 +48,7 @@ public class AlmostJEI implements IModPlugin {
var recipeId = recipeCategory.getRegistryName(recipe);
if (recipeId == null) return;
var link = CRTLookup.getLink(recipeId);
if (link == null) return;

View file

@ -5,12 +5,15 @@ import com.almostreliable.unified.AlmostUnifiedRuntime;
import com.almostreliable.unified.utils.ReplacementMap;
import com.almostreliable.unified.utils.TagMap;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@ -18,33 +21,51 @@ public class HideHelper {
public static Collection<ItemStack> createHidingList(AlmostUnifiedRuntime runtime) {
ReplacementMap repMap = runtime.getReplacementMap().orElse(null);
TagMap filteredTagMap = runtime.getFilteredTagMap().orElse(null);
TagMap tagMap = runtime.getFilteredTagMap().orElse(null);
if (repMap == null || filteredTagMap == null) return new ArrayList<>();
if (repMap == null || tagMap == null) return new ArrayList<>();
return filteredTagMap.getTags().stream().map(unifyTag -> {
Collection<ResourceLocation> itemsByTag = filteredTagMap.getItems(unifyTag);
Set<ResourceLocation> hidingList = new HashSet<>();
for (var unifyTag : tagMap.getTags()) {
var itemsByTag = tagMap.getItemsByTag(unifyTag);
// avoid hiding single entries and tags that only contain the same namespace for all items
long namespaces = itemsByTag.stream().map(ResourceLocation::getNamespace).distinct().count();
if (namespaces <= 1) return new ArrayList<ItemStack>();
if (namespaces <= 1) continue;
Set<ResourceLocation> replacements = itemsByTag
.stream()
.map(item -> getReplacementForItem(repMap, item))
.collect(Collectors.toSet());
List<ResourceLocation> toHide = itemsByTag.stream().filter(rl -> !replacements.contains(rl)).toList();
Set<ResourceLocation> replacements = new HashSet<>();
for (ResourceLocation item : itemsByTag) {
replacements.add(getReplacementForItem(repMap, item));
}
if (!toHide.isEmpty()) {
AlmostUnified.LOG.info("Hiding {}/{} items for tag {} -> {}",
Set<ResourceLocation> toHide = new HashSet<>();
for (ResourceLocation item : itemsByTag) {
if (!replacements.contains(item)) {
toHide.add(item);
}
}
if (toHide.isEmpty()) continue;
AlmostUnified.LOG.info(
"[AutoHiding] Hiding {}/{} items for tag '#{}' -> {}",
toHide.size(),
itemsByTag.size(),
unifyTag.location(),
toHide);
toHide
);
hidingList.addAll(toHide);
}
return toHide.stream().flatMap(rl -> BuiltInRegistries.ITEM.getOptional(rl).stream()).map(ItemStack::new).toList();
}).flatMap(Collection::stream).toList();
hidingList.addAll(getRefItems(repMap));
return hidingList
.stream()
.flatMap(rl -> BuiltInRegistries.ITEM.getOptional(rl).stream())
.map(ItemStack::new)
.collect(Collectors.toList());
}
/**
@ -61,4 +82,41 @@ public class HideHelper {
if (replacement == null) return item;
return replacement;
}
/**
* Returns a set of all items that are contained in the reference tags.
*
* @return A set of all items that are contained in the reference tags.
*/
private static Set<ResourceLocation> getRefItems(ReplacementMap repMap) {
Set<ResourceLocation> hidingList = new HashSet<>();
for (var ref : AlmostUnified.getTagOwnerships().getRefs()) {
var owner = AlmostUnified.getTagOwnerships().getOwnerByTag(ref);
assert owner != null;
var dominantItem = repMap.getPreferredItemForTag(owner, $ -> true);
TagKey<Item> asTagKey = TagKey.create(Registries.ITEM, ref.location());
Set<ResourceLocation> refItems = new HashSet<>();
BuiltInRegistries.ITEM.getTagOrEmpty(asTagKey).forEach(holder -> {
ResourceLocation item = BuiltInRegistries.ITEM.getKey(holder.value());
if (item.equals(dominantItem)) return; // don't hide if the item is a dominant one
refItems.add(item);
});
if (refItems.isEmpty()) continue;
AlmostUnified.LOG.info(
"[AutoHiding] Hiding reference tag '#{}' of owner tag '#{}' -> {}",
ref.location(),
owner.location(),
refItems
);
hidingList.addAll(refItems);
}
return hidingList;
}
}

View file

@ -52,4 +52,6 @@ public final class RecipeIndicator {
GuiComponent.blit(poseStack, 0, 0, 0, 0, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE);
poseStack.popPose();
}
public record RenderEntry(int pX, int pY) {}
}

View file

@ -40,7 +40,7 @@ public class DebugConfig extends Config {
.stream()
.sorted(Comparator.comparing(t -> t.location().toString()))
.map(t -> StringUtils.rightPad(t.location().toString(), 40) + " => " + tagMap
.getItems(t)
.getItemsByTag(t)
.stream()
.map(ResourceLocation::toString)
.sorted()

View file

@ -0,0 +1,33 @@
package com.almostreliable.unified.config;
public class ServerConfigs {
private final UnifyConfig unifyConfig;
private final DuplicationConfig dupConfig;
private final DebugConfig debugConfig;
public static ServerConfigs load() {
UnifyConfig unifyConfig = Config.load(UnifyConfig.NAME, new UnifyConfig.Serializer());
DuplicationConfig dupConfig = Config.load(DuplicationConfig.NAME, new DuplicationConfig.Serializer());
DebugConfig debugConfig = Config.load(DebugConfig.NAME, new DebugConfig.Serializer());
return new ServerConfigs(unifyConfig, dupConfig, debugConfig);
}
private ServerConfigs(UnifyConfig unifyConfig, DuplicationConfig dupConfig, DebugConfig debugConfig) {
this.unifyConfig = unifyConfig;
this.dupConfig = dupConfig;
this.debugConfig = debugConfig;
}
public UnifyConfig getUnifyConfig() {
return unifyConfig;
}
public DuplicationConfig getDupConfig() {
return dupConfig;
}
public DebugConfig getDebugConfig() {
return debugConfig;
}
}

View file

@ -9,6 +9,7 @@ import com.google.gson.JsonPrimitive;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -21,19 +22,35 @@ public class UnifyConfig extends Config {
private final List<String> unbakedTags;
private final List<String> materials;
private final Map<ResourceLocation, String> priorityOverrides;
private final Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships;
private final Set<UnifyTag<Item>> ignoredTags;
private final Set<Pattern> ignoredItems;
private final Set<Pattern> ignoredRecipeTypes;
private final Set<Pattern> ignoredRecipes;
private final boolean hideJeiRei;
private final Map<ResourceLocation, Boolean> ignoredRecipeTypesCache;
public UnifyConfig(List<String> modPriorities, List<String> stoneStrata, List<String> unbakedTags, List<String> materials, Map<ResourceLocation, String> priorityOverrides, Set<UnifyTag<Item>> ignoredTags, Set<Pattern> ignoredItems, Set<Pattern> ignoredRecipeTypes, Set<Pattern> ignoredRecipes, boolean hideJeiRei) {
private final Map<ResourceLocation, Boolean> ignoredRecipeTypesCache;
@Nullable private Set<UnifyTag<Item>> bakedTagsCache;
public UnifyConfig(
List<String> modPriorities,
List<String> stoneStrata,
List<String> unbakedTags,
List<String> materials,
Map<ResourceLocation, String> priorityOverrides,
Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships,
Set<UnifyTag<Item>> ignoredTags,
Set<Pattern> ignoredItems,
Set<Pattern> ignoredRecipeTypes,
Set<Pattern> ignoredRecipes,
boolean hideJeiRei
) {
this.modPriorities = modPriorities;
this.stoneStrata = stoneStrata;
this.unbakedTags = unbakedTags;
this.materials = materials;
this.priorityOverrides = priorityOverrides;
this.tagOwnerships = tagOwnerships;
this.ignoredTags = ignoredTags;
this.ignoredItems = ignoredItems;
this.ignoredRecipeTypes = ignoredRecipeTypes;
@ -50,8 +67,12 @@ public class UnifyConfig extends Config {
return Collections.unmodifiableList(stoneStrata);
}
public List<UnifyTag<Item>> bakeTags() {
List<UnifyTag<Item>> result = new ArrayList<>();
public Set<UnifyTag<Item>> bakeTags() {
if (bakedTagsCache != null) {
return bakedTagsCache;
}
Set<UnifyTag<Item>> result = new HashSet<>();
for (String tag : unbakedTags) {
for (String material : materials) {
@ -59,15 +80,17 @@ public class UnifyConfig extends Config {
ResourceLocation asRL = ResourceLocation.tryParse(replace);
if (asRL == null) {
AlmostUnified.LOG.warn("Could not bake tag <{}> with material <{}>", tag, material);
} else {
continue;
}
UnifyTag<Item> t = UnifyTag.item(asRL);
if (!ignoredTags.contains(t)) {
result.add(t);
}
}
}
}
bakedTagsCache = result;
return result;
}
@ -81,6 +104,10 @@ public class UnifyConfig extends Config {
return Collections.unmodifiableMap(priorityOverrides);
}
public Map<ResourceLocation, Set<ResourceLocation>> getTagOwnerships() {
return Collections.unmodifiableMap(tagOwnerships);
}
public boolean includeItem(ResourceLocation item) {
for (Pattern pattern : ignoredItems) {
if (pattern.matcher(item.toString()).matches()) {
@ -125,6 +152,7 @@ public class UnifyConfig extends Config {
public static final String TAGS = "tags";
public static final String MATERIALS = "materials";
public static final String PRIORITY_OVERRIDES = "priorityOverrides";
public static final String TAG_OWNERSHIPS = "tagOwnerships";
public static final String IGNORED_TAGS = "ignoredTags";
public static final String IGNORED_ITEMS = "ignoredItems";
public static final String IGNORED_RECIPE_TYPES = "ignoredRecipeTypes";
@ -149,6 +177,18 @@ public class UnifyConfig extends Config {
entry -> entry.getValue().getAsString(),
(a, b) -> b,
HashMap::new)), new HashMap<>());
Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships = safeGet(() -> json
.getAsJsonObject(TAG_OWNERSHIPS)
.entrySet()
.stream()
.collect(Collectors.toMap(
entry -> new ResourceLocation(entry.getKey()),
entry -> JsonUtils.toList(entry.getValue().getAsJsonArray())
.stream()
.map(ResourceLocation::new)
.collect(Collectors.toSet()),
(a, b) -> b,
HashMap::new)), new HashMap<>());
Set<UnifyTag<Item>> ignoredTags = safeGet(() -> JsonUtils
.toList(json.getAsJsonArray(IGNORED_TAGS))
.stream()
@ -166,6 +206,7 @@ public class UnifyConfig extends Config {
tags,
materials,
priorityOverrides,
tagOwnerships,
ignoredTags,
ignoredItems,
ignoredRecipeTypes,
@ -186,6 +227,12 @@ public class UnifyConfig extends Config {
priorityOverrides.add(tag.toString(), new JsonPrimitive(mod));
});
json.add(PRIORITY_OVERRIDES, priorityOverrides);
JsonObject tagOwnerships = new JsonObject();
config.tagOwnerships.forEach((parent, child) -> {
tagOwnerships.add(parent.toString(),
JsonUtils.toArray(child.stream().map(ResourceLocation::toString).toList()));
});
json.add(TAG_OWNERSHIPS, tagOwnerships);
json.add(IGNORED_TAGS,
JsonUtils.toArray(config.ignoredTags
.stream()

View file

@ -0,0 +1,31 @@
package com.almostreliable.unified.mixin;
import com.almostreliable.unified.compat.AlmostJEI;
import com.almostreliable.unified.compat.RecipeIndicator;
import com.mojang.blaze3d.vertex.PoseStack;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.library.gui.recipes.RecipeLayout;
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;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(RecipeLayout.class)
public abstract class JeiRecipeLayoutMixin<R> {
@Shadow(remap = false) @Final
private IRecipeCategory<R> recipeCategory;
@Shadow(remap = false) @Final
private R recipe;
@Inject(method = "drawRecipe", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;pushPose()V", ordinal = 1), locals = LocalCapture.CAPTURE_FAILHARD)
private void unified$catchLayoutInfo(PoseStack stack, int mouseX, int mouseY, CallbackInfo ci, IDrawable background, int mX, int mY, int x, int y) {
var posX = x - RecipeIndicator.RENDER_SIZE;
var posY = y - RecipeIndicator.RENDER_SIZE;
AlmostJEI.handleIndicator(stack, mX, mY, posX, posY, recipeCategory, recipe);
}
}

View file

@ -1,84 +0,0 @@
package com.almostreliable.unified.mixin;
import com.almostreliable.unified.compat.AlmostJEI;
import com.almostreliable.unified.compat.RecipeIndicator;
import com.almostreliable.unified.utils.Utils;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import mezz.jei.api.gui.IRecipeLayoutDrawable;
import mezz.jei.common.util.ImmutableRect2i;
import mezz.jei.gui.recipes.RecipeTransferButton;
import mezz.jei.gui.recipes.RecipesGui;
import net.minecraft.client.renderer.Rect2i;
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.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@Mixin(RecipesGui.class)
public abstract class JeiRecipesGuiMixin {
private static final int BORDER_PADDING = 2;
@Shadow(remap = false) @Final
private List<RecipeTransferButton> recipeTransferButtons;
@Inject(
method = "drawLayouts",
at = @At(value = "INVOKE", target = "Lmezz/jei/api/gui/IRecipeLayoutDrawable;drawRecipe(Lcom/mojang/blaze3d/vertex/PoseStack;II)V", shift = At.Shift.AFTER),
locals = LocalCapture.CAPTURE_FAILHARD,
remap = false
)
private void unified$drawIndicator(
PoseStack poseStack, int mX, int mY, CallbackInfoReturnable<Optional<IRecipeLayoutDrawable<?>>> cir,
IRecipeLayoutDrawable<?> hoveredLayout, Iterator<?> i, IRecipeLayoutDrawable<?> recipeLayout
) {
RenderSystem.disableBlend();
RenderSystem.disableDepthTest();
var buttonArea = recipeLayout.getRecipeTransferButtonArea();
RecipeTransferButton transferButton = null;
for (var button : recipeTransferButtons) {
if (!button.visible || !buttonsMatch(buttonArea, button.getArea())) continue;
transferButton = button;
}
int posX;
int posY;
if (transferButton == null) {
var layoutArea = recipeLayout.getRect();
posX = layoutArea.getX() + layoutArea.getWidth() - RecipeIndicator.RENDER_SIZE / 2;
posY = layoutArea.getY() + layoutArea.getHeight() - RecipeIndicator.RENDER_SIZE / 2 + BORDER_PADDING;
} else {
posX = buttonArea.getX() + BORDER_PADDING;
posY = buttonArea.getY() - RecipeIndicator.RENDER_SIZE - 2 + BORDER_PADDING;
}
AlmostJEI.handleIndicator(
poseStack,
mX,
mY,
posX,
posY,
Utils.cast(recipeLayout.getRecipeCategory()),
recipeLayout.getRecipe()
);
RenderSystem.enableBlend();
RenderSystem.enableDepthTest();
}
private static boolean buttonsMatch(Rect2i a, ImmutableRect2i b) {
return a.getX() == b.getX() &&
a.getY() == b.getY() &&
a.getWidth() == b.getWidth() &&
a.getHeight() == b.getHeight();
}
}

View file

@ -20,7 +20,7 @@ public class RecipeManagerMixin {
@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("HEAD"))
private void runTransformation(Map<ResourceLocation, JsonElement> recipes, ResourceManager resourceManager, ProfilerFiller profiler, CallbackInfo ci) {
try {
AlmostUnified.reloadRuntime();
AlmostUnified.onReloadRecipeManager();
AlmostUnified.getRuntime().run(recipes, AlmostUnified.getStartupConfig().isServerOnly());
} catch (Exception e) {
AlmostUnified.LOG.error(e.getMessage(), e);

View file

@ -1,25 +0,0 @@
package com.almostreliable.unified.mixin;
import com.almostreliable.unified.AlmostUnified;
import net.minecraft.commands.Commands;
import net.minecraft.core.RegistryAccess;
import net.minecraft.server.ReloadableServerResources;
import net.minecraft.tags.TagManager;
import net.minecraft.world.flag.FeatureFlagSet;
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(ReloadableServerResources.class)
public class ReloadableServerResourcesMixin {
@Shadow @Final private TagManager tagManager;
@Inject(method = "<init>", at = @At("RETURN"))
private void yoinkServerResources(RegistryAccess.Frozen frozen, FeatureFlagSet featureFlagSet, Commands.CommandSelection commandSelection, int i, CallbackInfo ci) {
AlmostUnified.updateTagManager(tagManager);
}
}

View file

@ -0,0 +1,36 @@
package com.almostreliable.unified.mixin;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.utils.Utils;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagLoader;
import net.minecraft.world.item.Item;
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.CallbackInfoReturnable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Mixin(TagLoader.class)
public class TagLoaderMixin {
@Shadow @Final private String directory;
@Inject(method = "build(Ljava/util/Map;)Ljava/util/Map;", at = @At("RETURN"))
private <T> void onCreateLoadResult(Map<ResourceLocation, List<TagLoader.EntryWithSource>> map, CallbackInfoReturnable<Map<ResourceLocation, Collection<T>>> cir) {
if (directory.equals("tags/items")) {
try {
Map<ResourceLocation, Collection<Holder<Item>>> rawTags = Utils.cast(cir.getReturnValue());
AlmostUnified.getTagOwnerships().applyOwnershipToRawTags(rawTags);
} catch (Exception e) {
AlmostUnified.LOG.error("Error applying tag ownerships to raw tags", e);
}
}
}
}

View file

@ -0,0 +1,23 @@
package com.almostreliable.unified.mixin;
import com.almostreliable.unified.AlmostUnified;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.tags.TagManager;
import net.minecraft.util.profiling.ProfilerFiller;
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.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@Mixin(TagManager.class)
public class TagManagerMixin {
@Inject(method = "reload", at = @At("HEAD"))
private void onReload(PreparableReloadListener.PreparationBarrier preparationBarrier, ResourceManager resourceManager, ProfilerFiller profilerFiller, ProfilerFiller profilerFiller2, Executor executor, Executor executor2, CallbackInfoReturnable<CompletableFuture<Void>> cir) {
AlmostUnified.onTagManagerReload((TagManager) (Object) this);
}
}

View file

@ -1,5 +1,6 @@
package com.almostreliable.unified.recipe;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.api.recipe.RecipeConstants;
import com.almostreliable.unified.api.recipe.RecipeContext;
import com.almostreliable.unified.utils.JsonUtils;
@ -64,20 +65,28 @@ public class RecipeContextImpl implements RecipeContext {
return null;
}
JsonElement copy = element.deepCopy();
tryReplacingItemInIngredient(copy);
tryCreateIngredientReplacement(copy);
return element.equals(copy) ? null : copy;
}
private void tryReplacingItemInIngredient(@Nullable JsonElement element) {
private void tryCreateIngredientReplacement(@Nullable JsonElement element) {
if (element instanceof JsonArray array) {
for (JsonElement e : array) {
tryReplacingItemInIngredient(e);
tryCreateIngredientReplacement(e);
}
}
if (element instanceof JsonObject object) {
tryReplacingItemInIngredient(object.get(RecipeConstants.VALUE));
tryReplacingItemInIngredient(object.get(RecipeConstants.INGREDIENT));
tryCreateIngredientReplacement(object.get(RecipeConstants.VALUE));
tryCreateIngredientReplacement(object.get(RecipeConstants.INGREDIENT));
if (object.get(RecipeConstants.TAG) instanceof JsonPrimitive primitive) {
UnifyTag<Item> tag = Utils.toItemTag(primitive.getAsString());
UnifyTag<Item> ownershipTag = AlmostUnified.getTagOwnerships().getOwnerByTag(tag);
if (ownershipTag != null) {
object.add(RecipeConstants.TAG, new JsonPrimitive(ownershipTag.location().toString()));
}
}
if (object.get(RecipeConstants.ITEM) instanceof JsonPrimitive primitive) {
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());

View file

@ -78,7 +78,6 @@ public class RecipeTransformer {
unifyConfig.clearCache();
duplicationConfig.clearCache();
replacementMap.getStoneStrataHandler().clearCache();
if (tracker != null) recipes.putAll(tracker.compute());
return result;

View file

@ -7,9 +7,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
public class ReplacementMap {
@ -17,26 +15,29 @@ public class ReplacementMap {
private final TagMap tagMap;
private final UnifyConfig unifyConfig;
private final StoneStrataHandler stoneStrataHandler;
private final Set<ResourceLocation> warnings;
public ReplacementMap(TagMap tagMap, StoneStrataHandler stoneStrataHandler, UnifyConfig unifyConfig) {
this.tagMap = tagMap;
this.unifyConfig = unifyConfig;
this.stoneStrataHandler = stoneStrataHandler;
this.warnings = new HashSet<>();
}
@Nullable
public UnifyTag<Item> getPreferredTagForItem(ResourceLocation item) {
Collection<UnifyTag<Item>> tags = tagMap.getTags(item);
Collection<UnifyTag<Item>> tags = tagMap.getTagsByItem(item);
if (tags.isEmpty()) {
return null;
}
if (tags.size() > 1) {
if (tags.size() > 1 && !warnings.contains(item)) {
AlmostUnified.LOG.warn(
"Item '{}' has multiple preferred tags '{}' for recipe replacement. This needs to be manually fixed by the user.",
item,
tags.stream().map(UnifyTag::location).toList());
warnings.add(item);
}
return tags.iterator().next();
@ -60,7 +61,7 @@ public class ReplacementMap {
@Nullable
public ResourceLocation getPreferredItemForTag(UnifyTag<Item> tag, Predicate<ResourceLocation> itemFilter) {
List<ResourceLocation> items = tagMap
.getItems(tag)
.getItemsByTag(tag)
.stream()
.filter(itemFilter)
// Helps us to get the clean stone variant first in case of a stone strata tag
@ -103,8 +104,4 @@ public class ReplacementMap {
}
return null;
}
public StoneStrataHandler getStoneStrataHandler() {
return stoneStrataHandler;
}
}

View file

@ -13,12 +13,25 @@ import java.util.*;
import java.util.function.Predicate;
public class TagMap {
private final Map<UnifyTag<Item>, Set<ResourceLocation>> tagsToItems = new HashMap<>();
private final Map<ResourceLocation, Set<UnifyTag<Item>>> itemsToTags = new HashMap<>();
protected TagMap() {}
public static TagMap create(Collection<UnifyTag<Item>> unifyTags) {
/**
* Creates a tag map from a set of unify tags.
* <p>
* This should only be used for client-side tag maps or for tests.<br>
* It requires the registry to be loaded in order to validate the tags
* and fetch the holder from it.
* <p>
* For the server, use {@link #create(TagManager)} instead.
*
* @param unifyTags The unify tags.
* @return A new tag map.
*/
public static TagMap create(Set<UnifyTag<Item>> unifyTags) {
TagMap tagMap = new TagMap();
unifyTags.forEach(ut -> {
@ -33,43 +46,41 @@ public class TagMap {
}
/**
* Creates a {@link TagMap} from a vanilla {@link TagManager}.
* Creates a tag map from the vanilla {@link TagManager}.
* <p>
* This should only be used on the server.<br>
* It will fetch all tags and items from the manager and store them. This tag map should later
* be filtered by using {@link #filtered(Predicate, Predicate)}.
* <p>
* For the client, use {@link #create(Set)} instead.
*
* @param tagManager The vanilla tag manager.
* @return A new {@link TagMap}.
* @return A new tag map.
*/
public static TagMap create(TagManager tagManager) {
Objects.requireNonNull(tagManager, "Requires a non-null tag manager");
var tags = tagManager
.getResult()
.stream()
.filter(result -> result.key() == Registries.ITEM)
.findFirst()
.map(TagManager.LoadResult::tags)
.orElseThrow(() -> new IllegalStateException("No item tag result found"));
var tags = unpackTagManager(tagManager);
TagMap tagMap = new TagMap();
for (var entry : tags.entrySet()) {
UnifyTag<Item> tag = UnifyTag.item(entry.getKey());
var holderTag = entry.getValue();
for (Holder<?> holder : holderTag) {
UnifyTag<Item> unifyTag = UnifyTag.item(entry.getKey());
for (Holder<?> holder : entry.getValue()) {
holder
.unwrapKey()
.map(ResourceKey::location)
.filter(BuiltInRegistries.ITEM::containsKey)
.ifPresent(itemId -> tagMap.put(tag, itemId));
.ifPresent(itemId -> tagMap.put(unifyTag, itemId));
}
}
return tagMap;
}
/**
* Creates a filtered {@link TagMap}.
* Creates a filtered tag map copy.
*
* @param tagFilter A filter to determine which tags to include.
* @param itemFilter A filter to determine which items to include.
* @return A new {@link TagMap}.
* @return A filtered copy of this tag map.
*/
public TagMap filtered(Predicate<UnifyTag<Item>> tagFilter, Predicate<ResourceLocation> itemFilter) {
TagMap tagMap = new TagMap();
@ -84,19 +95,6 @@ public class TagMap {
return tagMap;
}
protected void put(UnifyTag<Item> tag, ResourceLocation item) {
tagsToItems.computeIfAbsent(tag, k -> new HashSet<>()).add(item);
itemsToTags.computeIfAbsent(item, k -> new HashSet<>()).add(tag);
}
public Collection<ResourceLocation> getItems(UnifyTag<Item> tag) {
return Collections.unmodifiableSet(tagsToItems.getOrDefault(tag, Collections.emptySet()));
}
public Collection<UnifyTag<Item>> getTags(ResourceLocation items) {
return Collections.unmodifiableSet(itemsToTags.getOrDefault(items, Collections.emptySet()));
}
public int tagSize() {
return tagsToItems.size();
}
@ -105,7 +103,47 @@ public class TagMap {
return itemsToTags.size();
}
public Collection<UnifyTag<Item>> getTags() {
public Set<ResourceLocation> getItemsByTag(UnifyTag<Item> tag) {
return Collections.unmodifiableSet(tagsToItems.getOrDefault(tag, Collections.emptySet()));
}
public Set<UnifyTag<Item>> getTagsByItem(ResourceLocation items) {
return Collections.unmodifiableSet(itemsToTags.getOrDefault(items, Collections.emptySet()));
}
public Set<UnifyTag<Item>> getTags() {
return Collections.unmodifiableSet(tagsToItems.keySet());
}
/**
* Helper function to build a relationship between a tag and an item.
* <p>
* If the entries don't exist in the internal maps yet, they will be created. That means
* it needs to be checked whether the tag or item is valid before calling this method.
*
* @param tag The tag.
* @param item The item.
*/
protected void put(UnifyTag<Item> tag, ResourceLocation item) {
tagsToItems.computeIfAbsent(tag, k -> new HashSet<>()).add(item);
itemsToTags.computeIfAbsent(item, k -> new HashSet<>()).add(tag);
}
/**
* Helper function to fetch all item tags and their item holders from the tag manager.
*
* @param tagManager The tag manager.
* @return A map of all item tags and their item holders.
*/
private static Map<ResourceLocation, Collection<Holder<Item>>> unpackTagManager(TagManager tagManager) {
var tags = tagManager
.getResult()
.stream()
.filter(result -> result.key() == Registries.ITEM)
.findFirst()
.map(TagManager.LoadResult::tags)
.orElseThrow(() -> new IllegalStateException("No item tag result found"));
return Utils.cast(tags);
}
}

View file

@ -0,0 +1,144 @@
package com.almostreliable.unified.utils;
import com.almostreliable.unified.AlmostUnified;
import com.google.common.collect.*;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class TagOwnerships {
/**
* A map holding relationships between reference tags and their owner tags.
* <p>
* Example:<br>
* If the map contains the entry {@code minecraft:logs -> minecraft:planks},
* any recipes where the tag {@code minecraft:logs} is being used, it will
* replace the tag with {@code minecraft:planks}.
* <p>
* Map Key = Tag to replace<br>
* Map Value = Tag to replace with
*/
private final Map<UnifyTag<Item>, UnifyTag<Item>> refsToOwner;
private final Multimap<UnifyTag<Item>, UnifyTag<Item>> ownerToRefs;
/**
* Creates a new TagOwnerships instance that contains immutable maps of all tag ownership relationships.
* <p>
* It is ensured that all owner tags are present in the {@code unifyTags} set, and that all reference tags
* aren't present in the {@code unifyTags} set.
*
* @param unifyTags The set of all unify tags in use.
* @param tagOwnershipConfig The map of all tag ownership relationships.
*/
public TagOwnerships(Set<UnifyTag<Item>> unifyTags, Map<ResourceLocation, Set<ResourceLocation>> tagOwnershipConfig) {
ImmutableMap.Builder<UnifyTag<Item>, UnifyTag<Item>> refsToOwnerBuilder = ImmutableMap.builder();
ImmutableMultimap.Builder<UnifyTag<Item>, UnifyTag<Item>> ownerToRefsBuilder = ImmutableMultimap.builder();
tagOwnershipConfig.forEach((rawOwner, rawRefs) -> {
for (ResourceLocation rawRef : rawRefs) {
UnifyTag<Item> owner = UnifyTag.item(rawOwner);
UnifyTag<Item> ref = UnifyTag.item(rawRef);
if (!unifyTags.contains(owner)) {
AlmostUnified.LOG.warn(
"[TagOwnerships] Owner tag '#{}' is not present in the unify tag list!",
owner.location()
);
continue;
}
if (unifyTags.contains(ref)) {
AlmostUnified.LOG.warn(
"[TagOwnerships] Reference tag '#{}' of owner tag '#{}' is present in the unify tag list!",
ref.location(),
owner.location()
);
continue;
}
refsToOwnerBuilder.put(ref, owner);
ownerToRefsBuilder.put(owner, ref);
}
});
this.refsToOwner = refsToOwnerBuilder.build();
this.ownerToRefs = ownerToRefsBuilder.build();
}
/**
* Applies tag ownerships to the provided raw tags.
* <p>
* The raw tags are then processed by the game and actual tags are created.
*
* @param rawTags The raw tags to apply ownerships to.
*/
public void applyOwnershipToRawTags(Map<ResourceLocation, Collection<Holder<Item>>> rawTags) {
Multimap<ResourceLocation, ResourceLocation> changedTags = HashMultimap.create();
ownerToRefs.asMap().forEach((owner, refs) -> {
var rawHolders = rawTags.get(owner.location());
if (rawHolders == null) {
AlmostUnified.LOG.warn("[TagOwnerships] Owner tag '#{}' does not exist!", owner.location());
return;
}
ImmutableSet.Builder<Holder<Item>> holders = ImmutableSet.builder();
holders.addAll(rawHolders);
boolean changed = false;
for (UnifyTag<Item> ref : refs) {
var refHolders = rawTags.get(ref.location());
if (refHolders == null) {
AlmostUnified.LOG.warn(
"[TagOwnerships] Reference tag '#{}' of owner tag '#{}' does not exist!",
ref.location(),
owner.location()
);
continue;
}
for (Holder<Item> holder : refHolders) {
holders.add(holder);
holder.unwrapKey().ifPresent(key -> changedTags.put(ref.location(), key.location()));
changed = true;
}
}
if (changed) {
rawTags.put(owner.location(), holders.build());
}
});
if (!changedTags.isEmpty()) {
changedTags.asMap().forEach((tag, items) -> {
AlmostUnified.LOG.info("[TagOwnerships] Modified tag '#{}', added {}", tag, items);
});
}
}
/**
* Gets the owner tag for the provided reference tag.
*
* @param tag The reference tag to get the owner for.
* @return The owner tag, or null if the provided tag is not a reference tag.
*/
@Nullable
public UnifyTag<Item> getOwnerByTag(UnifyTag<Item> tag) {
return refsToOwner.get(tag);
}
/**
* Gets all reference tags for all owner tags.
*
* @return A set of all reference tags.
*/
public Set<UnifyTag<Item>> getRefs() {
return refsToOwner.keySet();
}
}

View file

@ -6,11 +6,12 @@
"plugin": "com.almostreliable.unified.mixin.AlmostMixinPlugin",
"mixins": [
"RecipeManagerMixin",
"ReloadableServerResourcesMixin"
"TagLoaderMixin",
"TagManagerMixin"
],
"client": [
"ClientPacketListenerMixin",
"JeiRecipesGuiMixin"
"JeiRecipeLayoutMixin"
],
"injectors": {
"defaultRequire": 1

View file

@ -41,6 +41,7 @@ public final class TestUtils {
Defaults.getTags(Platform.FORGE),
TEST_MOD_PRIORITIES,
new HashMap<>(),
new HashMap<>(),
new HashSet<>(),
new HashSet<>(),
new HashSet<>(),
@ -104,7 +105,11 @@ public final class TestUtils {
}
public static StoneStrataHandler createTestStrataHandler() {
return StoneStrataHandler.create(List.of(), Set.of(), TagMap.create(List.of()));
return StoneStrataHandler.create(
List.of(),
Set.of(),
TagMap.create(Set.of())
);
}
public static RecipeTransformer basicTransformer(Consumer<RecipeHandlerFactory> consumer) {

View file

@ -44,12 +44,12 @@ public class TagMapTests {
tagMap.put(bronzeOreTag, TestUtils.mod4RL("bronze_ore"));
tagMap.put(bronzeOreTag, TestUtils.mod5RL("bronze_ore"));
assertEquals(tagMap.getItems(bronzeOreTag).size(), 5);
assertEquals(tagMap.getTags(TestUtils.mod1RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTags(TestUtils.mod2RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTags(TestUtils.mod3RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTags(TestUtils.mod4RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTags(TestUtils.mod5RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getItemsByTag(bronzeOreTag).size(), 5);
assertEquals(tagMap.getTagsByItem(TestUtils.mod1RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByItem(TestUtils.mod2RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByItem(TestUtils.mod3RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByItem(TestUtils.mod4RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByItem(TestUtils.mod5RL("bronze_ore")).size(), 1);
tagMap.put(UnifyTag.item(new ResourceLocation("forge:ores/invar")), TestUtils.mod1RL("invar_ore"));

View file

@ -33,7 +33,7 @@ dependencies {
modCompileOnly("me.shedaniel:RoughlyEnoughItems-api-fabric:$reiVersion") // required for common rei plugin
compileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // required to disable rei compat layer on jei plugin
testCompileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // don't question this, it's required for compiling
modCompileOnly("mezz.jei:jei-$minecraftVersion-fabric:$jeiVersion") // required for common jei plugin and mixin
modCompileOnly("mezz.jei:jei-$minecraftVersion-fabric-api:$jeiVersion") // required for common jei plugin and mixin
when (fabricRecipeViewer) { // runtime only
"rei" -> modLocalRuntime("me.shedaniel:RoughlyEnoughItems-fabric:$reiVersion")
"jei" -> modLocalRuntime("mezz.jei:jei-$minecraftVersion-fabric:$jeiVersion")

View file

@ -32,5 +32,9 @@
"fabric": ">=${fabricApiVersion}",
"minecraft": ">=${minecraftVersion}",
"java": ">=17"
},
"suggests": {
"jei": ">=${jeiVersion}",
"roughlyenoughitems": ">=${reiVersion}"
}
}

View file

@ -44,7 +44,7 @@ dependencies {
modCompileOnly("me.shedaniel:RoughlyEnoughItems-forge:$reiVersion") // required for common rei plugin | api does not work here
compileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // required to disable rei compat layer on jei plugin
testCompileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // don't question this, it's required for compiling
modCompileOnly("mezz.jei:jei-$minecraftVersion-forge:$jeiVersion") {
modCompileOnly("mezz.jei:jei-$minecraftVersion-forge-api:$jeiVersion") {
isTransitive = false
} // required for common jei plugin and mixin, transitivity is off because it breaks the forge runtime
when (forgeRecipeViewer) { // runtime only

View file

@ -4,11 +4,15 @@ import com.almostreliable.unified.api.recipe.RecipeConstants;
import com.almostreliable.unified.api.recipe.RecipeUnifier;
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
import java.util.List;
public class MekanismRecipeUnifier implements RecipeUnifier {
@Override
public void collectUnifier(RecipeUnifierBuilder builder) {
builder.put(RecipeConstants.MAIN_INPUT, (json, ctx) -> ctx.createIngredientReplacement(json));
builder.put(RecipeConstants.MAIN_OUTPUT, (json, ctx) -> ctx.createResultReplacement(json));
builder.put(RecipeConstants.SECONDARY_OUTPUT, (json, ctx) -> ctx.createResultReplacement(json));
List.of(RecipeConstants.MAIN_INPUT, RecipeConstants.ITEM_INPUT)
.forEach(key -> builder.put(key, (json, ctx) -> ctx.createIngredientReplacement(json)));
List.of(RecipeConstants.MAIN_OUTPUT, RecipeConstants.ITEM_OUTPUT, RecipeConstants.SECONDARY_OUTPUT)
.forEach(key -> builder.put(key, (json, ctx) -> ctx.createResultReplacement(json)));
}
}

View file

@ -24,3 +24,24 @@ mandatory = true
versionRange = "[${minecraftVersion},)"
ordering = "NONE"
side = "BOTH"
[[dependencies.${modId}]]
modId = "jei"
mandatory = false
versionRange = "[${jeiVersion},)"
ordering = "BEFORE"
side = "BOTH"
[[dependencies.${modId}]]
modId = "roughlyenoughitems"
mandatory = false
versionRange = "[${reiVersion},)"
ordering = "BEFORE"
side = "BOTH"
[[dependencies.${modId}]]
modId = "rei_plugin_compatibilities"
mandatory = false
versionRange = "[9.0.43,)"
ordering = "BEFORE"
side = "BOTH"

View file

@ -18,6 +18,7 @@ val modAuthor: String by project
val githubRepo: String by project
val githubUser: String by project
val sharedRunDir: String by project
val jeiVersion: String by project
val reiVersion: String by project
plugins {
@ -156,6 +157,7 @@ subprojects {
"fabricApiVersion" to fabricApiVersion,
"forgeVersion" to forgeVersion,
"forgeFMLVersion" to forgeVersion.substringBefore("."), // Only use major version as FML error message sucks. The error message for wrong Forge version is way better.
"jeiVersion" to jeiVersion,
"reiVersion" to reiVersion,
"githubUser" to githubUser,
"githubRepo" to githubRepo

View file

@ -11,7 +11,7 @@ junitVersion = 5.9.0
minecraftVersion = 1.19.4
# Mod
modVersion = 0.3.8
modVersion = 0.4.0
modPackage = com.almostreliable.unified
modId = almostunified
modName = AlmostUnified
@ -20,7 +20,7 @@ modDescription = Unify all resources.
# Mod Dependencies
reiVersion = 11.0.597
jeiVersion = 13.1.0.2
jeiVersion = 13.1.0.3
# Fabric Settings
fabricLoaderVersion = 0.14.18