port 1.20.1 changes

This commit is contained in:
Relentless 2023-08-10 21:23:04 +02:00
parent 64297f40b9
commit ae6c979827
No known key found for this signature in database
GPG key ID: 50C5FD225130D790
43 changed files with 904 additions and 451 deletions

View file

@ -7,9 +7,21 @@ on:
- '1.19.2'
tags-ignore:
- '**'
paths:
- 'gradle/**'
- '**.java'
- '**.kts'
- '**.properties'
- '**/build.yml'
pull_request:
branches:
- '1.19.2'
paths:
- 'gradle/**'
- '**.java'
- '**.kts'
- '**.properties'
- '**/build.yml'
env:
JAVA_DIST: 'zulu'

View file

@ -97,7 +97,7 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Build Information" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- JAR files: $(find output -maxdepth 1 -type f -name '*.jar' | wc -c)" >> $GITHUB_STEP_SUMMARY
echo "- JAR files: $(find output -maxdepth 1 -type f -name '*.jar' | wc -l)" >> $GITHUB_STEP_SUMMARY
echo "- Folder size: $(du -sh output | cut -f1)" >> $GITHUB_STEP_SUMMARY
echo "- Archive size: $(du -sh build.tar.gz | cut -f1)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
@ -198,7 +198,9 @@ jobs:
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
changelog-file: output/changelog.md
loaders: forge
loaders: |
forge
neoforge
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
java: ${{ env.JAVA_VERSION }}
@ -232,7 +234,9 @@ jobs:
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
changelog-file: output/changelog.md
loaders: forge
loaders: |
forge
neoforge
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
java: ${{ env.JAVA_VERSION }}

View file

@ -1,18 +1,15 @@
val enabledPlatforms: String by project
val fabricLoaderVersion: String by project
val minecraftVersion: String by project
val modPackage: String by project
val modId: String by project
val modName: String by project
val modPackage: String by project
val reiVersion: String by project
val jeiVersion: String by project
val kubejsVersion: String by project
val mappingsChannel: String by project
val mappingsVersion: String by project
val junitVersion: String by project
val minecraftVersion: String by project
val fabricLoaderVersion: String by project
val jeiVersion: String by project
val reiVersion: String by project
plugins {
id("com.github.gmazzo.buildconfig") version ("4.0.4")
id("com.github.gmazzo.buildconfig") version "4.0.4"
}
architectury {
@ -20,25 +17,27 @@ architectury {
}
loom {
if (project.findProperty("enableAccessWidener") == "true") { // Optional property for `gradle.properties` to enable access wideners.
if (project.findProperty("enableAccessWidener") == "true") { // optional property for `gradle.properties`
accessWidenerPath.set(file("src/main/resources/$modId.accesswidener"))
println("Access widener enabled for project ${project.name}. Access widener path: ${loom.accessWidenerPath.get()}")
}
}
dependencies {
// loader
// required here for the @Environment annotations and the mixin dependencies
// Do NOT use other classes from the Fabric loader!
/**
* loader
* required here for the @Environment annotations and the mixin dependencies
* do NOT use other classes from the Fabric loader
*/
modImplementation("net.fabricmc:fabric-loader:$fabricLoaderVersion")
// compile time mods
modCompileOnly("dev.latvian.mods:kubejs:$kubejsVersion") // required for common kubejs plugin | common has remapping issues
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
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
modCompileOnly("mezz.jei:jei-$minecraftVersion-common-api:$jeiVersion") // required for jei plugin
modCompileOnly("mezz.jei:jei-$minecraftVersion-lib:$jeiVersion") // required for jei mixin
modCompileOnly("me.shedaniel:RoughlyEnoughItems-api:$reiVersion") // required for rei plugin
// compile time dependencies
compileOnly("me.shedaniel:REIPluginCompatibilities-forge-annotations:9.+") // required to disable rei compat layer
// tests
testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
@ -53,7 +52,6 @@ buildConfig {
useJavaOutput()
}
// TODO reactivate when specific mod is not annoying anymore
//tasks {
// withType<Test> {
// useJUnitPlatform()

View file

@ -3,13 +3,21 @@ 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.config.UnifyConfig;
import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
import com.almostreliable.unified.utils.TagOwnerships;
import com.almostreliable.unified.utils.TagReloadHandler;
import com.google.common.base.Preconditions;
import net.minecraft.tags.TagManager;
import com.google.gson.JsonElement;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
@SuppressWarnings("UtilityClassWithoutPrivateConstructor")
public final class AlmostUnified {
@ -18,16 +26,6 @@ public final class AlmostUnified {
@Nullable private static AlmostUnifiedRuntime RUNTIME;
@Nullable private static StartupConfig STARTUP_CONFIG;
@Nullable private static ServerConfigs SERVER_CONFIGS;
@Nullable private static TagManager TAG_MANAGER;
@Nullable private static TagOwnerships TAG_OWNERSHIPS;
public static StartupConfig getStartupConfig() {
if (STARTUP_CONFIG == null) {
STARTUP_CONFIG = Config.load(StartupConfig.NAME, new StartupConfig.Serializer());
}
return STARTUP_CONFIG;
}
public static boolean isRuntimeLoaded() {
return RUNTIME != null;
@ -40,18 +38,59 @@ public final class AlmostUnified {
return RUNTIME;
}
public static void onTagManagerReload(TagManager tagManager) {
SERVER_CONFIGS = ServerConfigs.load();
TAG_MANAGER = tagManager;
var unifyConfig = SERVER_CONFIGS.getUnifyConfig();
TAG_OWNERSHIPS = new TagOwnerships(unifyConfig.bakeTags(), unifyConfig.getTagOwnerships());
public static StartupConfig getStartupConfig() {
if (STARTUP_CONFIG == null) {
STARTUP_CONFIG = Config.load(StartupConfig.NAME, new StartupConfig.Serializer());
}
return STARTUP_CONFIG;
}
public static void onReloadRecipeManager() {
Preconditions.checkNotNull(SERVER_CONFIGS, "ServerConfigs were not loaded correctly");
Preconditions.checkNotNull(TAG_MANAGER, "TagManager was not loaded correctly");
Preconditions.checkNotNull(TAG_OWNERSHIPS, "TagOwnerships were not loaded correctly");
public static void onTagLoaderReload(Map<ResourceLocation, Collection<Holder<Item>>> tags) {
RecipeHandlerFactory recipeHandlerFactory = new RecipeHandlerFactory();
AlmostUnifiedPlatform.INSTANCE.bindRecipeHandlers(recipeHandlerFactory);
RUNTIME = AlmostUnifiedRuntimeImpl.create(SERVER_CONFIGS, TAG_MANAGER, TAG_OWNERSHIPS);
ServerConfigs serverConfigs = ServerConfigs.load();
UnifyConfig unifyConfig = serverConfigs.getUnifyConfig();
TagOwnerships tagOwnerships = new TagOwnerships(
unifyConfig.bakeAndValidateTags(tags),
unifyConfig.getTagOwnerships()
);
tagOwnerships.applyOwnerships(tags);
ReplacementData replacementData = loadReplacementData(tags, unifyConfig, tagOwnerships);
RUNTIME = new AlmostUnifiedRuntimeImpl(
serverConfigs,
replacementData.filteredTagMap(),
replacementData.replacementMap(),
recipeHandlerFactory
);
}
public static void onRecipeManagerReload(Map<ResourceLocation, JsonElement> recipes) {
Preconditions.checkNotNull(RUNTIME, "AlmostUnifiedRuntime was not loaded correctly");
RUNTIME.run(recipes, getStartupConfig().isServerOnly());
}
/**
* Loads the required data for the replacement logic.
* <p>
* This method applies tag inheritance and rebuilds the replacement data if the
* inheritance mutates the tags.
*
* @param tags The vanilla tag map provided by the TagManager
* @param unifyConfig The mod config to use for unifying
* @param tagOwnerships The tag ownerships to apply
* @return The loaded data
*/
private static ReplacementData loadReplacementData(Map<ResourceLocation, Collection<Holder<Item>>> tags, UnifyConfig unifyConfig, TagOwnerships tagOwnerships) {
ReplacementData replacementData = ReplacementData.load(tags, unifyConfig, tagOwnerships);
var needsRebuild = TagReloadHandler.applyInheritance(unifyConfig, replacementData);
if (needsRebuild) {
return ReplacementData.load(tags, unifyConfig, tagOwnerships);
}
return replacementData;
}
}

View file

@ -22,8 +22,7 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
@Nullable private static AlmostUnifiedFallbackRuntime INSTANCE;
@Nullable private UnifyConfig unifyConfig;
@Nullable private TagMap filteredTagMap;
@Nullable private TagOwnerships tagOwnerships;
@Nullable private TagMap<Item> filteredTagMap;
@Nullable private ReplacementMap replacementMap;
public static AlmostUnifiedFallbackRuntime getInstance() {
@ -38,23 +37,22 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
public void reload() {
unifyConfig = null;
filteredTagMap = null;
tagOwnerships = null;
replacementMap = null;
build();
}
public void build() {
private void build() {
unifyConfig = Config.load(UnifyConfig.NAME, new UnifyConfig.Serializer());
Set<UnifyTag<Item>> unifyTags = unifyConfig.bakeTags();
filteredTagMap = TagMap.create(unifyTags).filtered($ -> true, unifyConfig::includeItem);
StoneStrataHandler stoneStrataHandler = createStoneStrataHandler(unifyConfig);
tagOwnerships = new TagOwnerships(unifyTags, unifyConfig.getTagOwnerships());
TagOwnerships tagOwnerships = new TagOwnerships(unifyTags, unifyConfig.getTagOwnerships());
replacementMap = new ReplacementMap(unifyConfig, filteredTagMap, stoneStrataHandler, tagOwnerships);
}
private static StoneStrataHandler createStoneStrataHandler(UnifyConfig config) {
Set<UnifyTag<Item>> stoneStrataTags = AlmostUnifiedPlatform.INSTANCE.getStoneStrataTags(config.getStoneStrata());
TagMap stoneStrataTagMap = TagMap.create(stoneStrataTags);
TagMap<Item> stoneStrataTagMap = TagMap.create(stoneStrataTags);
return StoneStrataHandler.create(config.getStoneStrata(), stoneStrataTags, stoneStrataTagMap);
}
@ -64,7 +62,7 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
}
@Override
public Optional<TagMap> getFilteredTagMap() {
public Optional<TagMap<Item>> getFilteredTagMap() {
return Optional.ofNullable(filteredTagMap);
}
@ -77,9 +75,4 @@ public class AlmostUnifiedFallbackRuntime implements AlmostUnifiedRuntime {
public Optional<UnifyConfig> getUnifyConfig() {
return Optional.ofNullable(unifyConfig);
}
@Override
public Optional<TagOwnerships> getTagOwnerships() {
return Optional.ofNullable(tagOwnerships);
}
}

View file

@ -8,8 +8,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.ItemLike;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.util.Set;
import java.util.stream.Collectors;
@ -64,7 +64,7 @@ public class AlmostUnifiedLookupImpl implements AlmostUnifiedLookup {
.getRuntime()
.getFilteredTagMap()
.map(tagMap -> tagMap
.getItemsByTag(asUnifyTag)
.getEntriesByTag(asUnifyTag)
.stream()
.flatMap(rl -> Registry.ITEM.getOptional(rl).stream())
.collect(Collectors.toSet()))

View file

@ -6,11 +6,12 @@ import net.minecraft.world.item.Item;
import java.nio.file.Path;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
public interface AlmostUnifiedPlatform {
AlmostUnifiedPlatform INSTANCE = PlatformLoader.load(AlmostUnifiedPlatform.class);
AlmostUnifiedPlatform INSTANCE = load(AlmostUnifiedPlatform.class);
/**
* Gets the current platform
@ -27,13 +28,6 @@ public interface AlmostUnifiedPlatform {
*/
boolean isModLoaded(String modId);
/**
* Check if the game is currently in a development environment.
*
* @return True if in a development environment, false otherwise.
*/
boolean isDevelopmentEnvironment();
boolean isClient();
Path getConfigPath();
@ -43,4 +37,17 @@ public interface AlmostUnifiedPlatform {
void bindRecipeHandlers(RecipeHandlerFactory factory);
Set<UnifyTag<Item>> getStoneStrataTags(List<String> stoneStrataIds);
static <T> T load(Class<T> clazz) {
T loadedService = ServiceLoader.load(clazz)
.findFirst()
.orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName()));
AlmostUnified.LOG.debug("Loaded {} for service {}", loadedService, clazz);
return loadedService;
}
enum Platform {
FORGE,
FABRIC;
}
}

View file

@ -3,9 +3,9 @@ package com.almostreliable.unified;
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.google.gson.JsonElement;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import java.util.Map;
import java.util.Optional;
@ -14,11 +14,9 @@ public interface AlmostUnifiedRuntime {
void run(Map<ResourceLocation, JsonElement> recipes, boolean skipClientTracking);
Optional<TagMap> getFilteredTagMap();
Optional<TagMap<Item>> getFilteredTagMap();
Optional<ReplacementMap> getReplacementMap();
Optional<UnifyConfig> getUnifyConfig();
Optional<TagOwnerships> getTagOwnerships();
}

View file

@ -1,6 +1,5 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.StoneStrataHandler;
import com.almostreliable.unified.config.DebugConfig;
import com.almostreliable.unified.config.DuplicationConfig;
import com.almostreliable.unified.config.ServerConfigs;
@ -8,16 +7,12 @@ import com.almostreliable.unified.config.UnifyConfig;
import com.almostreliable.unified.recipe.RecipeDumper;
import com.almostreliable.unified.recipe.RecipeTransformer;
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.TagOwnerships;
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.Map;
import java.util.Optional;
@ -26,62 +21,28 @@ public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
private final UnifyConfig unifyConfig;
private final DuplicationConfig duplicationConfig;
private final DebugConfig debugConfig;
private final TagMap filteredTagMap;
private final TagOwnerships tagOwnerships;
private final TagMap<Item> tagMap;
private final ReplacementMap replacementMap;
private final RecipeHandlerFactory recipeHandlerFactory;
private AlmostUnifiedRuntimeImpl(
UnifyConfig unifyConfig,
DuplicationConfig duplicationConfig,
DebugConfig debugConfig,
TagMap filteredTagMap,
TagOwnerships tagOwnerships,
ReplacementMap replacementMap,
AlmostUnifiedRuntimeImpl(
ServerConfigs configs,
TagMap<Item> tagMap,
ReplacementMap repMap,
RecipeHandlerFactory recipeHandlerFactory
) {
this.unifyConfig = unifyConfig;
this.duplicationConfig = duplicationConfig;
this.debugConfig = debugConfig;
this.filteredTagMap = filteredTagMap;
this.tagOwnerships = tagOwnerships;
this.replacementMap = replacementMap;
this.unifyConfig = configs.getUnifyConfig();
this.duplicationConfig = configs.getDupConfig();
this.debugConfig = configs.getDebugConfig();
this.tagMap = tagMap;
this.replacementMap = repMap;
this.recipeHandlerFactory = recipeHandlerFactory;
}
public static AlmostUnifiedRuntimeImpl create(ServerConfigs serverConfigs, TagManager tagManager, TagOwnerships tagOwnerships) {
createGitIgnoreIfNotExists();
UnifyConfig unifyConfig = serverConfigs.getUnifyConfig();
DuplicationConfig duplicationConfig = serverConfigs.getDupConfig();
DebugConfig debugConfig = serverConfigs.getDebugConfig();
var unifyTags = unifyConfig.bakeTags();
TagMap globalTagMap = TagMap.create(tagManager);
TagMap filteredTagMap = globalTagMap.filtered(unifyTags::contains, unifyConfig::includeItem);
StoneStrataHandler stoneStrataHandler = StoneStrataHandler.create(unifyConfig.getStoneStrata(),
AlmostUnifiedPlatform.INSTANCE.getStoneStrataTags(unifyConfig.getStoneStrata()), globalTagMap);
var replacementMap = new ReplacementMap(unifyConfig, filteredTagMap, stoneStrataHandler, tagOwnerships);
RecipeHandlerFactory recipeHandlerFactory = new RecipeHandlerFactory();
AlmostUnifiedPlatform.INSTANCE.bindRecipeHandlers(recipeHandlerFactory);
return new AlmostUnifiedRuntimeImpl(
unifyConfig,
duplicationConfig,
debugConfig,
filteredTagMap,
tagOwnerships,
replacementMap,
recipeHandlerFactory
);
}
@Override
public void run(Map<ResourceLocation, JsonElement> recipes, boolean skipClientTracking) {
debugConfig.logRecipes(recipes, "recipes_before_unification.txt");
debugConfig.logUnifyTagDump(filteredTagMap);
debugConfig.logUnifyTagDump(tagMap);
long startTime = System.currentTimeMillis();
RecipeTransformer.Result result = new RecipeTransformer(
@ -96,20 +57,9 @@ public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
debugConfig.logRecipes(recipes, "recipes_after_unification.txt");
}
private static void createGitIgnoreIfNotExists() {
Path path = AlmostUnifiedPlatform.INSTANCE.getConfigPath();
if (!(Files.exists(path) && Files.isDirectory(path))) {
FileUtils.write(
AlmostUnifiedPlatform.INSTANCE.getConfigPath(),
".gitignore",
sb -> sb.append(DebugConfig.NAME).append(".json").append("\n")
);
}
}
@Override
public Optional<TagMap> getFilteredTagMap() {
return Optional.of(filteredTagMap);
public Optional<TagMap<Item>> getFilteredTagMap() {
return Optional.of(tagMap);
}
@Override
@ -121,9 +71,4 @@ public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
public Optional<UnifyConfig> getUnifyConfig() {
return Optional.of(unifyConfig);
}
@Override
public Optional<TagOwnerships> getTagOwnerships() {
return Optional.of(tagOwnerships);
}
}

View file

@ -1,6 +0,0 @@
package com.almostreliable.unified;
public enum Platform {
FORGE,
FABRIC
}

View file

@ -1,14 +0,0 @@
package com.almostreliable.unified;
import java.util.ServiceLoader;
@SuppressWarnings("UtilityClassWithoutPrivateConstructor")
public final class PlatformLoader {
static <T> T load(Class<T> clazz) {
final T loadedService = ServiceLoader.load(clazz)
.findFirst()
.orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName()));
AlmostUnified.LOG.debug("Loaded {} for service {}", loadedService, clazz);
return loadedService;
}
}

View file

@ -0,0 +1,42 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.StoneStrataHandler;
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 net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import java.util.Collection;
import java.util.Map;
/**
* Holder class for storing all the data needed for replacements in recipes.
*
* @param globalTagMap The global tag map, containing all tags.
* @param filteredTagMap The filtered tag map, containing only the tags that will be used for replacing. Determined by the unify config.
* @param stoneStrataHandler The stone strata handler, used for replacing stone strata.
* @param replacementMap The replacement map, used for replacing items.
*/
public record ReplacementData(TagMap<Item> globalTagMap, TagMap<Item> filteredTagMap,
StoneStrataHandler stoneStrataHandler,
ReplacementMap replacementMap) {
public static ReplacementData load(Map<ResourceLocation, Collection<Holder<Item>>> tags, UnifyConfig unifyConfig, TagOwnerships tagOwnerships) {
var globalTagMap = TagMap.createFromItemTags(tags);
var unifyTags = unifyConfig.bakeAndValidateTags(tags);
var filteredTagMap = globalTagMap.filtered(unifyTags::contains, unifyConfig::includeItem);
var stoneStrataHandler = StoneStrataHandler.create(
unifyConfig.getStoneStrata(),
AlmostUnifiedPlatform.INSTANCE.getStoneStrataTags(unifyConfig.getStoneStrata()),
globalTagMap
);
var replacementMap = new ReplacementMap(unifyConfig, filteredTagMap, stoneStrataHandler, tagOwnerships);
return new ReplacementData(globalTagMap, filteredTagMap, stoneStrataHandler, replacementMap);
}
}

View file

@ -13,6 +13,7 @@ public final class ModConstants {
public static final String ARS_ELEMENTAL = "ars_elemental";
public static final String ARS_NOUVEAU = "ars_nouveau";
public static final String ARS_SCALAES = "ars_scalaes";
public static final String CYCLIC = "cyclic";
public static final String IMMERSIVE_ENGINEERING = "immersiveengineering";
public static final String MEKANISM = "mekanism";
public static final String MODERN_INDUSTRIALIZATION = "modern_industrialization";

View file

@ -9,18 +9,18 @@ import net.minecraft.world.item.Item;
import java.util.*;
import java.util.regex.Pattern;
public class StoneStrataHandler {
public final class StoneStrataHandler {
private final List<String> stoneStrata;
private final Pattern tagMatcher;
private final TagMap stoneStrataTagMap;
private final TagMap<Item> 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) {
private StoneStrataHandler(List<String> stoneStrata, Pattern tagMatcher, TagMap<Item> stoneStrataTagMap) {
this.stoneStrata = createSortedStoneStrata(stoneStrata);
this.tagMatcher = tagMatcher;
this.stoneStrataTagMap = stoneStrataTagMap;
@ -41,8 +41,8 @@ public class StoneStrataHandler {
return stoneStrata.stream().sorted(Comparator.comparingInt(String::length).reversed()).toList();
}
public static StoneStrataHandler create(List<String> stoneStrataIds, Set<UnifyTag<Item>> stoneStrataTags, TagMap tagMap) {
TagMap stoneStrataTagMap = tagMap.filtered(stoneStrataTags::contains, item -> true);
public static StoneStrataHandler create(List<String> stoneStrataIds, Set<UnifyTag<Item>> stoneStrataTags, TagMap<Item> tagMap) {
var stoneStrataTagMap = tagMap.filtered(stoneStrataTags::contains, item -> true);
Pattern tagMatcher = Pattern.compile(switch (AlmostUnifiedPlatform.INSTANCE.getPlatform()) {
case FORGE -> "forge:ores/.+";
case FABRIC -> "(c:ores/.+|c:.+_ores)";
@ -71,7 +71,7 @@ public class StoneStrataHandler {
*/
private String computeStoneStrata(ResourceLocation item) {
String strata = stoneStrataTagMap
.getTagsByItem(item)
.getTagsByEntry(item)
.stream()
.findFirst()
.map(UnifyTag::location)

View file

@ -42,5 +42,8 @@ public final class RecipeConstants {
public static final String ITEM_INPUTS = "item_inputs";
public static final String ITEM_OUTPUTS = "item_outputs";
// cyclic
public static final String BONUS = "bonus";
private RecipeConstants() {}
}

View file

@ -3,7 +3,8 @@ package com.almostreliable.unified.compat;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.AlmostUnifiedRuntime;
import com.almostreliable.unified.utils.ReplacementMap;
import com.almostreliable.unified.utils.TagMap;
import com.almostreliable.unified.utils.TagOwnerships;
import com.almostreliable.unified.utils.Utils;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@ -16,22 +17,23 @@ import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class HideHelper {
public final class HideHelper {
private HideHelper() {}
public static Collection<ItemStack> createHidingList(AlmostUnifiedRuntime runtime) {
ReplacementMap repMap = runtime.getReplacementMap().orElse(null);
TagMap tagMap = runtime.getFilteredTagMap().orElse(null);
var tagMap = runtime.getFilteredTagMap().orElse(null);
if (repMap == null || tagMap == null) return new ArrayList<>();
Set<ResourceLocation> hidingList = new HashSet<>();
for (var unifyTag : tagMap.getTags()) {
var itemsByTag = tagMap.getItemsByTag(unifyTag);
var itemsByTag = tagMap.getEntriesByTag(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) continue;
// avoid handling single entries and tags that only contain the same namespace for all items
if (Utils.allSameNamespace(itemsByTag)) continue;
Set<ResourceLocation> replacements = new HashSet<>();
for (ResourceLocation item : itemsByTag) {
@ -89,8 +91,9 @@ public class HideHelper {
*/
private static Set<ResourceLocation> getRefItems(ReplacementMap repMap) {
Set<ResourceLocation> hidingList = new HashSet<>();
TagOwnerships ownerships = repMap.getTagOwnerships();
AlmostUnified.getRuntime().getTagOwnerships().ifPresent(ownerships -> ownerships.getRefs().forEach(ref -> {
ownerships.getRefs().forEach(ref -> {
var owner = ownerships.getOwnerByTag(ref);
assert owner != null;
@ -114,7 +117,7 @@ public class HideHelper {
);
hidingList.addAll(refItems);
}));
});
return hidingList;
}

View file

@ -52,6 +52,4 @@ 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

@ -6,6 +6,7 @@ import com.almostreliable.unified.utils.TagMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.apache.commons.lang3.StringUtils;
import java.util.Comparator;
@ -29,7 +30,7 @@ public class DebugConfig extends Config {
this.dumpRecipes = dumpRecipes;
}
public void logUnifyTagDump(TagMap tagMap) {
public void logUnifyTagDump(TagMap<Item> tagMap) {
if (!dumpTagMap) {
return;
}
@ -40,7 +41,7 @@ public class DebugConfig extends Config {
.stream()
.sorted(Comparator.comparing(t -> t.location().toString()))
.map(t -> StringUtils.rightPad(t.location().toString(), 40) + " => " + tagMap
.getItemsByTag(t)
.getEntriesByTag(t)
.stream()
.map(ResourceLocation::toString)
.sorted()

View file

@ -1,6 +1,6 @@
package com.almostreliable.unified.config;
import com.almostreliable.unified.Platform;
import com.almostreliable.unified.AlmostUnifiedPlatform;
import com.almostreliable.unified.utils.JsonCompare;
import net.minecraft.resources.ResourceLocation;
@ -70,7 +70,7 @@ public final class Defaults {
private Defaults() {}
public static List<String> getModPriorities(Platform platform) {
public static List<String> getModPriorities(AlmostUnifiedPlatform.Platform platform) {
return switch (platform) {
case FORGE -> List.of(
"minecraft",
@ -93,7 +93,7 @@ public final class Defaults {
};
}
public static List<String> getTags(Platform platform) {
public static List<String> getTags(AlmostUnifiedPlatform.Platform platform) {
return switch (platform) {
case FORGE -> List.of(
"forge:nuggets/{material}",
@ -129,17 +129,17 @@ public final class Defaults {
};
}
public static List<String> getIgnoredRecipeTypes(Platform platform) {
public static List<String> getIgnoredRecipeTypes(AlmostUnifiedPlatform.Platform platform) {
return switch (platform) {
default -> List.of("cucumber:shaped_tag");
};
}
public static JsonCompare.CompareSettings getDefaultDuplicateRules(Platform platform) {
public static JsonCompare.CompareSettings getDefaultDuplicateRules(AlmostUnifiedPlatform.Platform platform) {
JsonCompare.CompareSettings result = new JsonCompare.CompareSettings();
result.ignoreField(switch (platform) {
case FORGE -> "conditions";
case FABRIC -> "fabric:conditions";
case FABRIC -> "fabric:load_conditions";
});
result.ignoreField("group");
result.addRule("cookingtime", new JsonCompare.HigherRule());
@ -148,11 +148,11 @@ public final class Defaults {
return result;
}
public static LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> getDefaultDuplicateOverrides(Platform platform) {
public static LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> getDefaultDuplicateOverrides(AlmostUnifiedPlatform.Platform platform) {
JsonCompare.CompareSettings result = new JsonCompare.CompareSettings();
result.ignoreField(switch (platform) {
case FORGE -> "conditions";
case FABRIC -> "fabric:conditions";
case FABRIC -> "fabric:load_conditions";
});
result.ignoreField("group");
result.ignoreField("pattern");

View file

@ -1,5 +1,11 @@
package com.almostreliable.unified.config;
import com.almostreliable.unified.AlmostUnifiedPlatform;
import com.almostreliable.unified.utils.FileUtils;
import java.nio.file.Files;
import java.nio.file.Path;
public class ServerConfigs {
private final UnifyConfig unifyConfig;
@ -7,6 +13,7 @@ public class ServerConfigs {
private final DebugConfig debugConfig;
public static ServerConfigs load() {
createGitIgnoreIfNotExists();
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());
@ -19,6 +26,17 @@ public class ServerConfigs {
this.debugConfig = debugConfig;
}
private static void createGitIgnoreIfNotExists() {
Path path = AlmostUnifiedPlatform.INSTANCE.getConfigPath();
if (!(Files.exists(path) && Files.isDirectory(path))) {
FileUtils.write(
AlmostUnifiedPlatform.INSTANCE.getConfigPath(),
".gitignore",
sb -> sb.append(DebugConfig.NAME).append(".json").append("\n")
);
}
}
public UnifyConfig getUnifyConfig() {
return unifyConfig;
}

View file

@ -5,11 +5,15 @@ import com.almostreliable.unified.AlmostUnifiedPlatform;
import com.almostreliable.unified.utils.JsonUtils;
import com.almostreliable.unified.utils.UnifyTag;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -22,6 +26,10 @@ public class UnifyConfig extends Config {
private final List<String> materials;
private final Map<ResourceLocation, String> priorityOverrides;
private final Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships;
private final Enum<TagInheritanceMode> itemTagInheritanceMode;
private final Map<ResourceLocation, Set<Pattern>> itemTagInheritance;
private final Enum<TagInheritanceMode> blockTagInheritanceMode;
private final Map<ResourceLocation, Set<Pattern>> blockTagInheritance;
private final Set<UnifyTag<Item>> ignoredTags;
private final Set<Pattern> ignoredItems;
private final Set<Pattern> ignoredRecipeTypes;
@ -38,6 +46,10 @@ public class UnifyConfig extends Config {
List<String> materials,
Map<ResourceLocation, String> priorityOverrides,
Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships,
Enum<TagInheritanceMode> itemTagInheritanceMode,
Map<ResourceLocation, Set<Pattern>> itemTagInheritance,
Enum<TagInheritanceMode> blockTagInheritanceMode,
Map<ResourceLocation, Set<Pattern>> blockTagInheritance,
Set<UnifyTag<Item>> ignoredTags,
Set<Pattern> ignoredItems,
Set<Pattern> ignoredRecipeTypes,
@ -50,6 +62,10 @@ public class UnifyConfig extends Config {
this.materials = materials;
this.priorityOverrides = priorityOverrides;
this.tagOwnerships = tagOwnerships;
this.itemTagInheritanceMode = itemTagInheritanceMode;
this.itemTagInheritance = itemTagInheritance;
this.blockTagInheritanceMode = blockTagInheritanceMode;
this.blockTagInheritance = blockTagInheritance;
this.ignoredTags = ignoredTags;
this.ignoredItems = ignoredItems;
this.ignoredRecipeTypes = ignoredRecipeTypes;
@ -67,11 +83,20 @@ public class UnifyConfig extends Config {
}
public Set<UnifyTag<Item>> bakeTags() {
return bakeTags($ -> true);
}
public Set<UnifyTag<Item>> bakeAndValidateTags(Map<ResourceLocation, Collection<Holder<Item>>> tags) {
return bakeTags(tags::containsKey);
}
private Set<UnifyTag<Item>> bakeTags(Predicate<ResourceLocation> tagValidator) {
if (bakedTagsCache != null) {
return bakedTagsCache;
}
Set<UnifyTag<Item>> result = new HashSet<>();
Set<UnifyTag<Item>> wrongTags = new HashSet<>();
for (String tag : unbakedTags) {
for (String material : materials) {
@ -83,12 +108,24 @@ public class UnifyConfig extends Config {
}
UnifyTag<Item> t = UnifyTag.item(asRL);
if (!ignoredTags.contains(t)) {
result.add(t);
if (ignoredTags.contains(t)) continue;
if (!tagValidator.test(asRL)) {
wrongTags.add(t);
continue;
}
result.add(t);
}
}
if (!wrongTags.isEmpty()) {
AlmostUnified.LOG.warn(
"The following tags are invalid and will be ignored: {}",
wrongTags.stream().map(UnifyTag::location).collect(Collectors.toList())
);
}
bakedTagsCache = result;
return result;
}
@ -107,6 +144,45 @@ public class UnifyConfig extends Config {
return Collections.unmodifiableMap(tagOwnerships);
}
public boolean shouldInheritItemTag(UnifyTag<Item> itemTag, Set<UnifyTag<Item>> dominantTags) {
var patterns = itemTagInheritance.get(itemTag.location());
boolean result = checkPatterns(dominantTags, patterns);
// noinspection SimplifiableConditionalExpression
return itemTagInheritanceMode == TagInheritanceMode.ALLOW ? result : !result;
}
public boolean shouldInheritBlockTag(UnifyTag<Block> itemTag, Set<UnifyTag<Item>> dominantTags) {
var patterns = blockTagInheritance.get(itemTag.location());
boolean result = checkPatterns(dominantTags, patterns);
// noinspection SimplifiableConditionalExpression
return blockTagInheritanceMode == TagInheritanceMode.ALLOW ? result : !result;
}
/**
* Checks all patterns against all dominant tags.
* <p>
* This implementation works based on the assumption that the mode is {@link TagInheritanceMode#ALLOW}.
* Flip the result if the mode is {@link TagInheritanceMode#DENY}.
*
* @param dominantTags The tags of the dominant item to check.
* @param patterns The patterns to check against.
* @param <T> The type of the dominant tags.
* @return Whether the dominant tags match any of the patterns.
*/
private static <T> boolean checkPatterns(Set<UnifyTag<T>> dominantTags, @Nullable Set<Pattern> patterns) {
if (patterns == null) return false;
for (var pattern : patterns) {
for (var dominantTag : dominantTags) {
if (pattern.matcher(dominantTag.location().toString()).matches()) {
return true;
}
}
}
return false;
}
public boolean includeItem(ResourceLocation item) {
for (Pattern pattern : ignoredItems) {
if (pattern.matcher(item.toString()).matches()) {
@ -152,6 +228,10 @@ public class UnifyConfig extends Config {
public static final String MATERIALS = "materials";
public static final String PRIORITY_OVERRIDES = "priorityOverrides";
public static final String TAG_OWNERSHIPS = "tagOwnerships";
public static final String ITEM_TAG_INHERITANCE_MODE = "itemTagInheritanceMode";
public static final String ITEM_TAG_INHERITANCE = "itemTagInheritance";
public static final String BLOCK_TAG_INHERITANCE_MODE = "blockTagInheritanceMode";
public static final String BLOCK_TAG_INHERITANCE = "blockTagInheritance";
public static final String IGNORED_TAGS = "ignoredTags";
public static final String IGNORED_ITEMS = "ignoredItems";
public static final String IGNORED_RECIPE_TYPES = "ignoredRecipeTypes";
@ -168,6 +248,7 @@ public class UnifyConfig extends Config {
List<String> tags = safeGet(() -> JsonUtils.toList(json.getAsJsonArray(TAGS)), Defaults.getTags(platform));
List<String> materials = safeGet(() -> JsonUtils.toList(json.getAsJsonArray(MATERIALS)),
Defaults.MATERIALS);
Map<ResourceLocation, String> priorityOverrides = safeGet(() -> json.getAsJsonObject(PRIORITY_OVERRIDES)
.entrySet()
.stream()
@ -176,6 +257,7 @@ 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()
@ -188,6 +270,17 @@ public class UnifyConfig extends Config {
.collect(Collectors.toSet()),
(a, b) -> b,
HashMap::new)), new HashMap<>());
Enum<TagInheritanceMode> itemTagInheritanceMode = deserializeTagInheritanceMode(json,
ITEM_TAG_INHERITANCE_MODE);
Map<ResourceLocation, Set<Pattern>> itemTagInheritance = deserializePatternsForLocations(json,
ITEM_TAG_INHERITANCE);
Enum<TagInheritanceMode> blockTagInheritanceMode = deserializeTagInheritanceMode(json,
BLOCK_TAG_INHERITANCE_MODE);
Map<ResourceLocation, Set<Pattern>> blockTagInheritance = deserializePatternsForLocations(json,
BLOCK_TAG_INHERITANCE);
Set<UnifyTag<Item>> ignoredTags = safeGet(() -> JsonUtils
.toList(json.getAsJsonArray(IGNORED_TAGS))
.stream()
@ -206,6 +299,10 @@ public class UnifyConfig extends Config {
materials,
priorityOverrides,
tagOwnerships,
itemTagInheritanceMode,
itemTagInheritance,
blockTagInheritanceMode,
blockTagInheritance,
ignoredTags,
ignoredItems,
ignoredRecipeTypes,
@ -214,6 +311,43 @@ public class UnifyConfig extends Config {
);
}
private TagInheritanceMode deserializeTagInheritanceMode(JsonObject json, String key) {
return safeGet(() -> TagInheritanceMode.valueOf(json
.getAsJsonPrimitive(key)
.getAsString().toUpperCase()), TagInheritanceMode.ALLOW);
}
/**
* Deserializes a list of patterns from a json object with a base key. Example json:
* <pre>
* {
* "baseKey": {
* "location1": [ pattern1, pattern2 ],
* "location2": [ pattern3, pattern4 ]
* }
* }
* </pre>
*
* @param rawConfigJson The raw config json
* @param baseKey The base key
* @return The deserialized patterns separated by location
*/
private Map<ResourceLocation, Set<Pattern>> unsafeDeserializePatternsForLocations(JsonObject rawConfigJson, String baseKey) {
JsonObject json = rawConfigJson.getAsJsonObject(baseKey);
return json
.keySet()
.stream()
.collect(Collectors.toMap(
ResourceLocation::new,
key -> deserializePatterns(json, key, List.of()),
(a, b) -> b,
HashMap::new));
}
private Map<ResourceLocation, Set<Pattern>> deserializePatternsForLocations(JsonObject rawConfigJson, String baseKey) {
return safeGet(() -> unsafeDeserializePatternsForLocations(rawConfigJson, baseKey), new HashMap<>());
}
@Override
public JsonObject serialize(UnifyConfig config) {
JsonObject json = new JsonObject();
@ -232,6 +366,20 @@ public class UnifyConfig extends Config {
JsonUtils.toArray(child.stream().map(ResourceLocation::toString).toList()));
});
json.add(TAG_OWNERSHIPS, tagOwnerships);
JsonObject itemTagInheritance = new JsonObject();
config.itemTagInheritance.forEach((tag, patterns) -> {
itemTagInheritance.add(tag.toString(),
JsonUtils.toArray(patterns.stream().map(Pattern::toString).toList()));
});
json.add(ITEM_TAG_INHERITANCE_MODE, new JsonPrimitive(config.itemTagInheritanceMode.toString()));
json.add(ITEM_TAG_INHERITANCE, itemTagInheritance);
JsonObject blockTagInheritance = new JsonObject();
config.blockTagInheritance.forEach((tag, patterns) -> {
blockTagInheritance.add(tag.toString(),
JsonUtils.toArray(patterns.stream().map(Pattern::toString).toList()));
});
json.add(BLOCK_TAG_INHERITANCE_MODE, new JsonPrimitive(config.blockTagInheritanceMode.toString()));
json.add(BLOCK_TAG_INHERITANCE, blockTagInheritance);
json.add(IGNORED_TAGS,
JsonUtils.toArray(config.ignoredTags
.stream()
@ -245,4 +393,9 @@ public class UnifyConfig extends Config {
return json;
}
}
public enum TagInheritanceMode {
ALLOW,
DENY
}
}

View file

@ -20,8 +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.onReloadRecipeManager();
AlmostUnified.getRuntime().run(recipes, AlmostUnified.getStartupConfig().isServerOnly());
AlmostUnified.onRecipeManagerReload(recipes);
} catch (Exception e) {
AlmostUnified.LOG.error(e.getMessage(), e);
}

View file

@ -1,11 +1,13 @@
package com.almostreliable.unified.mixin.runtime;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.utils.TagReloadHandler;
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 net.minecraft.world.level.block.Block;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -26,10 +28,20 @@ public class TagLoaderMixin {
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.getRuntime().getTagOwnerships().ifPresent(to -> to.applyOwnershipToRawTags(rawTags));
Map<ResourceLocation, Collection<Holder<Item>>> tags = Utils.cast(cir.getReturnValue());
TagReloadHandler.initItemTags(tags);
TagReloadHandler.run();
} catch (Exception e) {
AlmostUnified.LOG.error("Error applying tag ownerships to raw tags", e);
AlmostUnified.LOG.error(e.getMessage(), e);
}
}
if (directory.equals("tags/blocks")) {
try {
Map<ResourceLocation, Collection<Holder<Block>>> tags = Utils.cast(cir.getReturnValue());
TagReloadHandler.initBlockTags(tags);
TagReloadHandler.run();
} catch (Exception e) {
AlmostUnified.LOG.error(e.getMessage(), e);
}
}
}

View file

@ -1,23 +0,0 @@
package com.almostreliable.unified.mixin.runtime;
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,6 +1,5 @@
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;
@ -83,11 +82,10 @@ public class RecipeContextImpl implements RecipeContext {
if (object.get(RecipeConstants.TAG) instanceof JsonPrimitive primitive) {
UnifyTag<Item> tag = Utils.toItemTag(primitive.getAsString());
AlmostUnified.getRuntime()
.getTagOwnerships()
.map(o -> o.getOwnerByTag(tag))
.ifPresent(ownerTag -> object.addProperty(RecipeConstants.TAG,
ownerTag.location().toString()));
var ownerTag = replacementMap.getTagOwnerships().getOwnerByTag(tag);
if (ownerTag != null) {
object.addProperty(RecipeConstants.TAG, ownerTag.location().toString());
}
}
if (object.get(RecipeConstants.ITEM) instanceof JsonPrimitive primitive) {

View file

@ -17,12 +17,12 @@ import java.util.function.Predicate;
public class ReplacementMap {
private final UnifyConfig unifyConfig;
private final TagMap tagMap;
private final TagMap<Item> tagMap;
private final StoneStrataHandler stoneStrataHandler;
private final TagOwnerships tagOwnerships;
private final Set<ResourceLocation> warnings;
public ReplacementMap(UnifyConfig unifyConfig, TagMap tagMap, StoneStrataHandler stoneStrataHandler, TagOwnerships tagOwnerships) {
public ReplacementMap(UnifyConfig unifyConfig, TagMap<Item> tagMap, StoneStrataHandler stoneStrataHandler, TagOwnerships tagOwnerships) {
this.tagMap = tagMap;
this.unifyConfig = unifyConfig;
this.stoneStrataHandler = stoneStrataHandler;
@ -32,7 +32,7 @@ public class ReplacementMap {
@Nullable
public UnifyTag<Item> getPreferredTagForItem(ResourceLocation item) {
Collection<UnifyTag<Item>> tags = tagMap.getTagsByItem(item);
Collection<UnifyTag<Item>> tags = tagMap.getTagsByEntry(item);
if (tags.isEmpty()) {
return null;
@ -71,7 +71,7 @@ public class ReplacementMap {
if (tagToLookup == null) tagToLookup = tag;
List<ResourceLocation> items = tagMap
.getItemsByTag(tagToLookup)
.getEntriesByTag(tagToLookup)
.stream()
.filter(itemFilter)
// Helps us to get the clean stone variant first in case of a stone strata tag
@ -143,4 +143,8 @@ public class ReplacementMap {
}
return null;
}
public TagOwnerships getTagOwnerships() {
return tagOwnerships;
}
}

View file

@ -5,33 +5,34 @@ import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.tags.TagManager;
import net.minecraft.tags.TagLoader;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.*;
import java.util.function.Predicate;
public class TagMap {
public class TagMap<T> {
private final Map<UnifyTag<Item>, Set<ResourceLocation>> tagsToItems = new HashMap<>();
private final Map<ResourceLocation, Set<UnifyTag<Item>>> itemsToTags = new HashMap<>();
private final Map<UnifyTag<T>, Set<ResourceLocation>> tagsToEntries = new HashMap<>();
private final Map<ResourceLocation, Set<UnifyTag<T>>> entriesToTags = new HashMap<>();
protected TagMap() {}
/**
* Creates a tag map from a set of unify tags.
* Creates an item tag map from a set of item 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.
* For the server, use {@link #createFromItemTags(Map)} instead.
*
* @param unifyTags The unify tags.
* @return A new tag map.
*/
public static TagMap create(Set<UnifyTag<Item>> unifyTags) {
TagMap tagMap = new TagMap();
public static TagMap<Item> create(Set<UnifyTag<Item>> unifyTags) {
TagMap<Item> tagMap = new TagMap<>();
unifyTags.forEach(ut -> {
TagKey<Item> asTagKey = TagKey.create(Registry.ITEM_REGISTRY, ut.location());
@ -45,104 +46,115 @@ public class TagMap {
}
/**
* Creates a tag map from the vanilla {@link TagManager}.
* Creates an item tag map from the vanilla item tag collection passed by the {@link TagLoader}.
* <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)}.
* 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 tag map.
* @param tags The vanilla item tag collection.
* @return A new item tag map.
*/
public static TagMap create(TagManager tagManager) {
var tags = unpackTagManager(tagManager);
TagMap tagMap = new TagMap();
public static TagMap<Item> createFromItemTags(Map<ResourceLocation, Collection<Holder<Item>>> tags) {
TagMap<Item> tagMap = new TagMap<>();
for (var entry : tags.entrySet()) {
UnifyTag<Item> unifyTag = UnifyTag.item(entry.getKey());
for (Holder<?> holder : entry.getValue()) {
holder
.unwrapKey()
.map(ResourceKey::location)
.filter(Registry.ITEM::containsKey)
.ifPresent(itemId -> tagMap.put(unifyTag, itemId));
}
fillEntries(tagMap, entry.getValue(), unifyTag, Registry.ITEM);
}
return tagMap;
}
/**
* Creates a block tag map from the vanilla block tag collection passed by the {@link TagLoader}.
* <p>
* This should only be used on the server.
*
* @param tags The vanilla block tag collection.
* @return A new block tag map.
*/
public static TagMap<Block> createFromBlockTags(Map<ResourceLocation, Collection<Holder<Block>>> tags) {
TagMap<Block> tagMap = new TagMap<>();
for (var entry : tags.entrySet()) {
UnifyTag<Block> unifyTag = UnifyTag.block(entry.getKey());
fillEntries(tagMap, entry.getValue(), unifyTag, Registry.BLOCK);
}
return tagMap;
}
/**
* Unwrap all holders, verify them and put them into the tag map.
*
* @param tagMap The tag map to fill.
* @param holders The holders to unwrap.
* @param unifyTag The unify tag to use.
* @param registry The registry to use.
*/
private static <T> void fillEntries(TagMap<T> tagMap, Collection<Holder<T>> holders, UnifyTag<T> unifyTag, Registry<T> registry) {
for (var holder : holders) {
holder
.unwrapKey()
.map(ResourceKey::location)
.filter(registry::containsKey)
.ifPresent(id -> tagMap.put(unifyTag, id));
}
}
/**
* 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.
* @param tagFilter A filter to determine which tags to include.
* @param entryFilter A filter to determine which entries to include.
* @return A filtered copy of this tag map.
*/
public TagMap filtered(Predicate<UnifyTag<Item>> tagFilter, Predicate<ResourceLocation> itemFilter) {
TagMap tagMap = new TagMap();
public TagMap<T> filtered(Predicate<UnifyTag<T>> tagFilter, Predicate<ResourceLocation> entryFilter) {
TagMap<T> tagMap = new TagMap<>();
tagsToItems.forEach((tag, items) -> {
tagsToEntries.forEach((tag, items) -> {
if (!tagFilter.test(tag)) {
return;
}
items.stream().filter(itemFilter).forEach(item -> tagMap.put(tag, item));
items.stream().filter(entryFilter).forEach(item -> tagMap.put(tag, item));
});
return tagMap;
}
public int tagSize() {
return tagsToItems.size();
return tagsToEntries.size();
}
public int itemSize() {
return itemsToTags.size();
return entriesToTags.size();
}
public Set<ResourceLocation> getItemsByTag(UnifyTag<Item> tag) {
return Collections.unmodifiableSet(tagsToItems.getOrDefault(tag, Collections.emptySet()));
public Set<ResourceLocation> getEntriesByTag(UnifyTag<T> tag) {
return Collections.unmodifiableSet(tagsToEntries.getOrDefault(tag, Collections.emptySet()));
}
public Set<UnifyTag<Item>> getTagsByItem(ResourceLocation items) {
return Collections.unmodifiableSet(itemsToTags.getOrDefault(items, Collections.emptySet()));
public Set<UnifyTag<T>> getTagsByEntry(ResourceLocation entry) {
return Collections.unmodifiableSet(entriesToTags.getOrDefault(entry, Collections.emptySet()));
}
public Set<UnifyTag<Item>> getTags() {
return Collections.unmodifiableSet(tagsToItems.keySet());
public Set<UnifyTag<T>> getTags() {
return Collections.unmodifiableSet(tagsToEntries.keySet());
}
/**
* Helper function to build a relationship between a tag and an item.
* Helper function to build a relationship between a tag and an entry.
* <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.
* it needs to be checked whether the tag or entry is valid before calling this method.
*
* @param tag The tag.
* @param item The item.
* @param tag The tag.
* @param entry The entry.
*/
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() == Registry.ITEM_REGISTRY)
.findFirst()
.map(TagManager.LoadResult::tags)
.orElseThrow(() -> new IllegalStateException("No item tag result found"));
return Utils.cast(tags);
protected void put(UnifyTag<T> tag, ResourceLocation entry) {
tagsToEntries.computeIfAbsent(tag, k -> new HashSet<>()).add(entry);
entriesToTags.computeIfAbsent(entry, k -> new HashSet<>()).add(tag);
}
}

View file

@ -78,7 +78,7 @@ public class TagOwnerships {
*
* @param rawTags The raw tags to apply ownerships to.
*/
public void applyOwnershipToRawTags(Map<ResourceLocation, Collection<Holder<Item>>> rawTags) {
public void applyOwnerships(Map<ResourceLocation, Collection<Holder<Item>>> rawTags) {
Multimap<ResourceLocation, ResourceLocation> changedTags = HashMultimap.create();
ownerToRefs.asMap().forEach((owner, refs) -> {
@ -105,7 +105,7 @@ public class TagOwnerships {
for (Holder<Item> holder : refHolders) {
holders.add(holder);
holder.unwrapKey().ifPresent(key -> changedTags.put(ref.location(), key.location()));
holder.unwrapKey().ifPresent(key -> changedTags.put(owner.location(), key.location()));
changed = true;
}
}

View file

@ -0,0 +1,217 @@
package com.almostreliable.unified.utils;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.ReplacementData;
import com.almostreliable.unified.config.UnifyConfig;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class TagReloadHandler {
private static final Object LOCK = new Object();
private static Map<ResourceLocation, Collection<Holder<Item>>> RAW_ITEM_TAGS;
private static Map<ResourceLocation, Collection<Holder<Block>>> RAW_BLOCK_TAGS;
private TagReloadHandler() {}
public static void initItemTags(Map<ResourceLocation, Collection<Holder<Item>>> rawItemTags) {
synchronized (LOCK) {
RAW_ITEM_TAGS = rawItemTags;
}
}
public static void initBlockTags(Map<ResourceLocation, Collection<Holder<Block>>> rawBlockTags) {
synchronized (LOCK) {
RAW_BLOCK_TAGS = rawBlockTags;
}
}
public static void run() {
if (RAW_ITEM_TAGS == null || RAW_BLOCK_TAGS == null) {
return;
}
AlmostUnified.onTagLoaderReload(RAW_ITEM_TAGS);
RAW_ITEM_TAGS = null;
RAW_BLOCK_TAGS = null;
}
public static boolean applyInheritance(UnifyConfig unifyConfig, ReplacementData replacementData) {
Preconditions.checkNotNull(RAW_ITEM_TAGS, "Item tags were not loaded correctly");
Preconditions.checkNotNull(RAW_BLOCK_TAGS, "Block tags were not loaded correctly");
Multimap<ResourceLocation, ResourceLocation> changedItemTags = HashMultimap.create();
Multimap<ResourceLocation, ResourceLocation> changedBlockTags = HashMultimap.create();
var relations = resolveRelations(replacementData.filteredTagMap(), replacementData.replacementMap());
if (relations.isEmpty()) return false;
var blockTagMap = TagMap.createFromBlockTags(RAW_BLOCK_TAGS);
var globalTagMap = replacementData.globalTagMap();
for (TagRelation relation : relations) {
var dominant = relation.dominant;
var dominantItemHolder = findDominantItemHolder(relation);
var dominantBlockHolder = findDominantBlockHolder(blockTagMap, dominant);
var dominantItemTags = globalTagMap.getTagsByEntry(dominant);
for (var item : relation.items) {
if (dominantItemHolder != null) {
var changed = applyItemTags(unifyConfig, globalTagMap, dominantItemHolder, dominantItemTags, item);
changedItemTags.putAll(dominant, changed);
}
if (dominantBlockHolder != null) {
var changed = applyBlockTags(unifyConfig, blockTagMap, dominantBlockHolder, dominantItemTags, item);
changedBlockTags.putAll(dominant, changed);
}
}
}
if (!changedBlockTags.isEmpty()) {
changedBlockTags.asMap().forEach((dominant, tags) -> {
AlmostUnified.LOG.info("[TagInheritance] Added '{}' to block tags {}", dominant, tags);
});
}
if (!changedItemTags.isEmpty()) {
changedItemTags.asMap().forEach((dominant, tags) -> {
AlmostUnified.LOG.info("[TagInheritance] Added '{}' to item tags {}", dominant, tags);
});
return true;
}
return false;
}
private static Set<TagRelation> resolveRelations(TagMap<Item> filteredTagMap, ReplacementMap repMap) {
Set<TagRelation> relations = new HashSet<>();
for (var unifyTag : filteredTagMap.getTags()) {
var itemsByTag = filteredTagMap.getEntriesByTag(unifyTag);
// avoid handling single entries and tags that only contain the same namespace for all items
if (Utils.allSameNamespace(itemsByTag)) continue;
ResourceLocation dominant = repMap.getPreferredItemForTag(unifyTag, $ -> true);
if (dominant == null || !Registry.ITEM.containsKey(dominant)) continue;
Set<ResourceLocation> items = getValidatedItems(itemsByTag, dominant);
if (items.isEmpty()) continue;
relations.add(new TagRelation(unifyTag.location(), dominant, items));
}
return relations;
}
/**
* Returns a set of all items that are not the dominant item and are valid by checking if they are registered.
*
* @param itemIds The set of all items that are in the tag
* @param dominant The dominant item
* @return A set of all items that are not the dominant item and are valid
*/
private static Set<ResourceLocation> getValidatedItems(Set<ResourceLocation> itemIds, ResourceLocation dominant) {
Set<ResourceLocation> result = new HashSet<>(itemIds.size());
for (ResourceLocation id : itemIds) {
if (!id.equals(dominant) && Registry.ITEM.containsKey(id)) {
result.add(id);
}
}
return result;
}
@SuppressWarnings("StaticVariableUsedBeforeInitialization")
@Nullable
private static Holder<Item> findDominantItemHolder(TagRelation relation) {
var tagHolders = RAW_ITEM_TAGS.get(relation.tag);
if (tagHolders == null) return null;
return findDominantHolder(tagHolders, relation.dominant);
}
@SuppressWarnings("StaticVariableUsedBeforeInitialization")
@Nullable
private static Holder<Block> findDominantBlockHolder(TagMap<Block> tagMap, ResourceLocation dominant) {
var blockTags = tagMap.getTagsByEntry(dominant);
if (blockTags.isEmpty()) return null;
var tagHolders = RAW_BLOCK_TAGS.get(blockTags.iterator().next().location());
if (tagHolders == null) return null;
return findDominantHolder(tagHolders, dominant);
}
@Nullable
private static <T> Holder<T> findDominantHolder(Collection<Holder<T>> holders, ResourceLocation dominant) {
for (var tagHolder : holders) {
var holderKey = tagHolder.unwrapKey();
if (holderKey.isPresent() && holderKey.get().location().equals(dominant)) {
return tagHolder;
}
}
return null;
}
private static Set<ResourceLocation> applyItemTags(UnifyConfig unifyConfig, TagMap<Item> globalTagMap, Holder<Item> dominantItemHolder, Set<UnifyTag<Item>> dominantItemTags, ResourceLocation item) {
var itemTags = globalTagMap.getTagsByEntry(item);
Set<ResourceLocation> changed = new HashSet<>();
for (var itemTag : itemTags) {
if (!unifyConfig.shouldInheritItemTag(itemTag, dominantItemTags)) continue;
if (tryUpdatingRawTags(dominantItemHolder, itemTag, RAW_ITEM_TAGS)) {
changed.add(itemTag.location());
}
}
return changed;
}
private static Set<ResourceLocation> applyBlockTags(UnifyConfig unifyConfig, TagMap<Block> blockTagMap, Holder<Block> dominantBlockHolder, Set<UnifyTag<Item>> dominantItemTags, ResourceLocation item) {
var blockTags = blockTagMap.getTagsByEntry(item);
Set<ResourceLocation> changed = new HashSet<>();
for (var blockTag : blockTags) {
if (!unifyConfig.shouldInheritBlockTag(blockTag, dominantItemTags)) continue;
if (tryUpdatingRawTags(dominantBlockHolder, blockTag, RAW_BLOCK_TAGS)) {
changed.add(blockTag.location());
}
}
return changed;
}
private static <T> boolean tryUpdatingRawTags(Holder<T> dominantHolder, UnifyTag<T> tag, Map<ResourceLocation, Collection<Holder<T>>> rawTags) {
var tagHolders = rawTags.get(tag.location());
if (tagHolders == null) return false;
if (tagHolders.contains(dominantHolder)) return false; // already present, no need to add it again
ImmutableSet.Builder<Holder<T>> newHolders = ImmutableSet.builder();
newHolders.addAll(tagHolders);
newHolders.add(dominantHolder);
rawTags.put(tag.location(), newHolders.build());
return true;
}
private record TagRelation(ResourceLocation tag, ResourceLocation dominant, Set<ResourceLocation> items) {}
}

View file

@ -2,12 +2,17 @@ package com.almostreliable.unified.utils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
public record UnifyTag<T>(Class<T> boundType, ResourceLocation location) {
public static UnifyTag<Item> item(ResourceLocation location) {
return new UnifyTag<>(Item.class, location);
}
public static UnifyTag<Block> block(ResourceLocation location) {
return new UnifyTag<>(Block.class, location);
}
@Override
public String toString() {
return "UnifyTag[" + boundType.getSimpleName().toLowerCase() + " / " + location + "]";

View file

@ -10,6 +10,7 @@ import net.minecraft.util.FormattedCharSequence;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
@ -56,4 +57,23 @@ public final class Utils {
}
screen.renderTooltip(stack, formattedTooltip, mX, mY);
}
/**
* Checks if all ids have the same namespace
*
* @param ids set of ids
* @return true if all ids have the same namespace
*/
public static boolean allSameNamespace(Set<ResourceLocation> ids) {
if (ids.size() <= 1) return true;
var it = ids.iterator();
var namespace = it.next().getNamespace();
while (it.hasNext()) {
if (!it.next().getNamespace().equals(namespace)) return false;
}
return true;
}
}

View file

@ -7,7 +7,6 @@
"mixins": [
"runtime.RecipeManagerMixin",
"runtime.TagLoaderMixin",
"runtime.TagManagerMixin",
"unifier.ArmorItemMixin",
"unifier.TieredItemMixin"
],

View file

@ -38,10 +38,14 @@ public final class TestUtils {
public static final UnifyConfig DEFAULT_UNIFY_CONFIG = new UnifyConfig(
Defaults.STONE_STRATA,
Defaults.MATERIALS,
Defaults.getTags(Platform.FORGE),
Defaults.getTags(AlmostUnifiedPlatform.Platform.FORGE),
TEST_MOD_PRIORITIES,
new HashMap<>(),
new HashMap<>(),
UnifyConfig.TagInheritanceMode.ALLOW,
new HashMap<>(),
UnifyConfig.TagInheritanceMode.ALLOW,
new HashMap<>(),
new HashSet<>(),
new HashSet<>(),
new HashSet<>(),
@ -55,11 +59,13 @@ public final class TestUtils {
private TestUtils() {}
public static JsonCompare.CompareSettings getDefaultCompareSettings() {
return Defaults.getDefaultDuplicateRules(Platform.FORGE);
return Defaults.getDefaultDuplicateRules(AlmostUnifiedPlatform.Platform.FORGE);
}
public static JsonCompare.CompareSettings getDefaultShapedCompareSettings() {
return Defaults.getDefaultDuplicateOverrides(Platform.FORGE).get(new ResourceLocation("crafting_shaped"));
return Defaults
.getDefaultDuplicateOverrides(AlmostUnifiedPlatform.Platform.FORGE)
.get(new ResourceLocation("crafting_shaped"));
}
public static final ResourceKey<Registry<Item>> FAKE_ITEM_REGISTRY = FakeResourceKeyRegistry.create("item");

View file

@ -9,8 +9,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class TagMapTests {
public static TagMap testTagMap() {
TagMap tagMap = new TagMap();
public static TagMap<Item> testTagMap() {
TagMap<Item> tagMap = new TagMap<>();
UnifyTag<Item> bronzeOreTag = UnifyTag.item(new ResourceLocation("forge:ores/bronze"));
UnifyTag<Item> invarOreTag = UnifyTag.item(new ResourceLocation("forge:ores/invar"));
UnifyTag<Item> tinOreTag = UnifyTag.item(new ResourceLocation("forge:ores/tin"));
@ -36,7 +36,7 @@ public class TagMapTests {
@Test
public void simpleCheck() {
TagMap tagMap = new TagMap();
TagMap<Item> tagMap = new TagMap<>();
UnifyTag<Item> bronzeOreTag = UnifyTag.item(new ResourceLocation("forge:ores/bronze"));
tagMap.put(bronzeOreTag, TestUtils.mod1RL("bronze_ore"));
tagMap.put(bronzeOreTag, TestUtils.mod2RL("bronze_ore"));
@ -44,12 +44,12 @@ public class TagMapTests {
tagMap.put(bronzeOreTag, TestUtils.mod4RL("bronze_ore"));
tagMap.put(bronzeOreTag, TestUtils.mod5RL("bronze_ore"));
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);
assertEquals(tagMap.getEntriesByTag(bronzeOreTag).size(), 5);
assertEquals(tagMap.getTagsByEntry(TestUtils.mod1RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByEntry(TestUtils.mod2RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByEntry(TestUtils.mod3RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByEntry(TestUtils.mod4RL("bronze_ore")).size(), 1);
assertEquals(tagMap.getTagsByEntry(TestUtils.mod5RL("bronze_ore")).size(), 1);
tagMap.put(UnifyTag.item(new ResourceLocation("forge:ores/invar")), TestUtils.mod1RL("invar_ore"));

View file

@ -2,12 +2,11 @@ val minecraftVersion: String by project
val fabricLoaderVersion: String by project
val fabricApiVersion: String by project
val fabricRecipeViewer: String by project
val reiVersion: String by project
val jeiVersion: String by project
val kubejsVersion: String by project
val reiVersion: String by project
plugins {
id("com.github.johnrengelman.shadow") version ("8.1.1")
id("com.github.johnrengelman.shadow") version "8.1.1"
}
architectury {
@ -16,7 +15,7 @@ architectury {
}
loom {
if (project.findProperty("enableAccessWidener") == "true") { // Optional property for `gradle.properties` to enable access wideners.
if (project.findProperty("enableAccessWidener") == "true") { // optional property for `gradle.properties`
accessWidenerPath.set(project(":Common").loom.accessWidenerPath)
println("Access widener enabled for project ${project.name}. Access widener path: ${loom.accessWidenerPath.get()}")
}
@ -24,6 +23,7 @@ loom {
val common by configurations
val shadowCommon by configurations
dependencies {
// loader
modImplementation("net.fabricmc:fabric-loader:$fabricLoaderVersion")
@ -34,23 +34,26 @@ dependencies {
shadowCommon(project(":Common", "transformProductionFabric")) { isTransitive = false }
// compile time mods
modCompileOnly("dev.latvian.mods:kubejs-fabric:$kubejsVersion") // required for common kubejs plugin
modCompileOnly("mezz.jei:jei-$minecraftVersion-fabric-api:$jeiVersion") // required for common jei plugin
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-api:$jeiVersion") // required for common jei plugin and mixin
// runtime dependencies
modLocalRuntime("dev.latvian.mods:kubejs-fabric:$kubejsVersion") {
exclude("net.fabricmc", "fabric-loader")
}
modLocalRuntime(
when (fabricRecipeViewer) {
"rei" -> "me.shedaniel:RoughlyEnoughItems-fabric:$reiVersion"
"jei" -> "mezz.jei:jei-$minecraftVersion-fabric:$jeiVersion"
"rei" -> "me.shedaniel:RoughlyEnoughItems-fabric:$reiVersion"
else -> throw GradleException("Invalid fabricRecipeViewer value: $fabricRecipeViewer")
}
) {
exclude("net.fabricmc", "fabric-loader")
)
}
/**
* force the fabric loader and api versions that are defined in the project
* some mods ship another version which crashes the runtime
*/
configurations.all {
resolutionStrategy {
force("net.fabricmc:fabric-loader:$fabricLoaderVersion")
force("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion+$minecraftVersion")
}
}

View file

@ -28,11 +28,6 @@ public class AlmostUnifiedPlatformFabric implements AlmostUnifiedPlatform {
return FabricLoader.getInstance().isModLoaded(modId);
}
@Override
public boolean isDevelopmentEnvironment() {
return FabricLoader.getInstance().isDevelopmentEnvironment();
}
@Override
public boolean isClient() {
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT;

View file

@ -1,15 +1,15 @@
val minecraftVersion: String by project
val forgeVersion: String by project
val junitVersion: String by project
val modId: String by project
val junitVersion: String by project
val forgeVersion: String by project
val forgeRecipeViewer: String by project
val reiVersion: String by project
val jeiVersion: String by project
val kubejsVersion: String by project
val reiVersion: String by project
val extraModsPrefix = "extra-mods"
plugins {
id("com.github.johnrengelman.shadow") version ("8.1.1")
id("com.github.johnrengelman.shadow") version "8.1.1"
}
architectury {
@ -18,7 +18,7 @@ architectury {
}
loom {
if (project.findProperty("enableAccessWidener") == "true") { // Optional property for `gradle.properties` to enable access wideners.
if (project.findProperty("enableAccessWidener") == "true") { // optional property for `gradle.properties`
accessWidenerPath.set(project(":Common").loom.accessWidenerPath)
forge {
convertAccessWideners.set(true)
@ -32,9 +32,17 @@ loom {
}
}
repositories {
flatDir {
name = extraModsPrefix
dir(file("$extraModsPrefix-$minecraftVersion"))
}
}
val common by configurations
val shadowCommon by configurations
val commonTests: SourceSetOutput = project(":Common").sourceSets["test"].output
dependencies {
// loader
forge("net.minecraftforge:forge:$minecraftVersion-$forgeVersion")
@ -44,22 +52,35 @@ dependencies {
shadowCommon(project(":Common", "transformProductionForge")) { isTransitive = false }
// compile time mods
modCompileOnly("dev.latvian.mods:kubejs-forge:$kubejsVersion") // required for common kubejs plugin
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-api:$jeiVersion") { // required for common jei plugin and mixin
modCompileOnly("mezz.jei:jei-$minecraftVersion-forge-api:$jeiVersion") { // required for common jei plugin
isTransitive = false // prevents breaking the forge runtime
}
modCompileOnly("me.shedaniel:RoughlyEnoughItems-forge:$reiVersion") // required for common rei plugin
// runtime mods
modLocalRuntime("dev.latvian.mods:kubejs-forge:$kubejsVersion")
when (forgeRecipeViewer) {
"rei" -> modLocalRuntime("me.shedaniel:RoughlyEnoughItems-forge:$reiVersion")
"jei" -> modLocalRuntime("mezz.jei:jei-$minecraftVersion-forge:$jeiVersion") { isTransitive = false }
"rei" -> modLocalRuntime("me.shedaniel:RoughlyEnoughItems-forge:$reiVersion")
else -> throw GradleException("Invalid forgeRecipeViewer value: $forgeRecipeViewer")
}
/**
* helps to load mods in development through an extra directory
* sadly, this does not support transitive dependencies
*/
fileTree("$extraModsPrefix-$minecraftVersion") { include("**/*.jar") }
.forEach { f ->
val sepIndex = f.nameWithoutExtension.lastIndexOf('-')
if (sepIndex == -1) {
throw IllegalArgumentException("Invalid mod name: '${f.nameWithoutExtension}'. Expected format: 'modName-version.jar'")
}
val mod = f.nameWithoutExtension.substring(0, sepIndex)
val version = f.nameWithoutExtension.substring(sepIndex + 1)
println("Extra mod ${f.nameWithoutExtension} detected.")
"modLocalRuntime"("extra-mods:$mod:$version")
}
// tests
testImplementation(project(":Common"))
testImplementation(commonTests)

View file

@ -1,10 +1,7 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.ModConstants;
import com.almostreliable.unified.compat.AdAstraRecipeUnifier;
import com.almostreliable.unified.compat.ArsNouveauRecipeUnifier;
import com.almostreliable.unified.compat.ImmersiveEngineeringRecipeUnifier;
import com.almostreliable.unified.compat.MekanismRecipeUnifier;
import com.almostreliable.unified.compat.*;
import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
import com.almostreliable.unified.utils.UnifyTag;
import com.google.auto.service.AutoService;
@ -38,11 +35,6 @@ public class AlmostUnifiedPlatformForge implements AlmostUnifiedPlatform {
return ModList.get().isLoaded(modId);
}
@Override
public boolean isDevelopmentEnvironment() {
return !FMLLoader.isProduction();
}
@Override
public boolean isClient() {
return FMLLoader.getDist() == Dist.CLIENT;
@ -67,6 +59,7 @@ public class AlmostUnifiedPlatformForge implements AlmostUnifiedPlatform {
ModConstants.ARS_NOUVEAU,
ModConstants.ARS_SCALAES
).forEach(modId -> factory.registerForMod(modId, new ArsNouveauRecipeUnifier()));
factory.registerForMod(ModConstants.CYCLIC, new CyclicRecipeUnifier());
factory.registerForMod(ModConstants.IMMERSIVE_ENGINEERING, new ImmersiveEngineeringRecipeUnifier());
factory.registerForMod(ModConstants.MEKANISM, new MekanismRecipeUnifier());
}

View file

@ -0,0 +1,13 @@
package com.almostreliable.unified.compat;
import com.almostreliable.unified.api.recipe.RecipeConstants;
import com.almostreliable.unified.api.recipe.RecipeUnifier;
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
public class CyclicRecipeUnifier implements RecipeUnifier {
@Override
public void collectUnifier(RecipeUnifierBuilder builder) {
builder.put(RecipeConstants.BONUS, (json, ctx) -> ctx.createResultReplacement(json));
}
}

View file

@ -39,13 +39,6 @@ versionRange = "[${reiVersion},)"
ordering = "BEFORE"
side = "BOTH"
[[dependencies."${modId}"]]
modId = "rei_plugin_compatibilities"
mandatory = false
versionRange = "[9.0.43,)"
ordering = "BEFORE"
side = "BOTH"
[[dependencies."${modId}"]]
modId = "kubejs"
mandatory = false

View file

@ -5,63 +5,49 @@ import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.task.RemapJarTask
val license: String by project
val fabricLoaderVersion: String by project
val fabricApiVersion: String by project
val forgeVersion: String by project
val minecraftVersion: String by project
val modPackage: String by project
val modVersion: String by project
val modId: String by project
val modName: String by project
val modDescription: String by project
val modAuthor: String by project
val githubRepo: String by project
val githubUser: String by project
val sharedRunDir: String by project
val autoServiceVersion: String by project
val parchmentVersion: String by project
val fabricApiVersion: String by project
val forgeVersion: String by project
val jeiVersion: String by project
val reiVersion: String by project
val kubejsVersion: String by project
val githubRepo: String by project
val githubUser: String by project
plugins {
id("architectury-plugin") version "3.4.+"
id("dev.architectury.loom") version "1.3.+" apply false
id("io.github.juuxel.loom-vineflower") version "1.11.0" apply false
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
java
`maven-publish`
id("architectury-plugin") version ("3.4.+")
id("io.github.juuxel.loom-quiltflower") version "1.10.0" apply false
id("dev.architectury.loom") version ("1.2.+") apply false
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
}
architectury {
minecraft = minecraftVersion
}
val extraModsPrefix = "extra-mods"
/**
* configurations for all projects including the root project
*/
allprojects {
apply(plugin = "java")
apply(plugin = "architectury-plugin")
apply(plugin = "maven-publish")
repositories {
mavenLocal()
mavenCentral()
maven("https://maven.parchmentmc.org") // Parchment
maven("https://maven.shedaniel.me") // REI
maven("https://maven.blamejared.com/") // JEI
maven("https://maven.saps.dev/minecraft") // KubeJS
flatDir {
name = extraModsPrefix
dir(file("$extraModsPrefix-$minecraftVersion"))
}
}
tasks {
withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(17)
}
withType<GenerateModuleMetadata> {
enabled = false
}
}
extensions.configure<JavaPluginExtension> {
@ -70,24 +56,35 @@ allprojects {
}
}
/**
* configurations for all projects except the root project
*/
subprojects {
apply(plugin = "java")
apply(plugin = "architectury-plugin")
apply(plugin = "dev.architectury.loom")
apply(plugin = "io.github.juuxel.loom-vineflower")
apply(plugin = "maven-publish")
apply(plugin = "io.github.juuxel.loom-quiltflower")
base.archivesName.set("$modId-${project.name.lowercase()}")
version = "$minecraftVersion-$modVersion"
base {
archivesName.set("$modId-${project.name.lowercase()}")
version = "$minecraftVersion-$modVersion"
}
repositories {
maven("https://maven.parchmentmc.org") // Parchment
maven("https://maven.shedaniel.me") // REI
maven("https://maven.blamejared.com/") // JEI
mavenLocal()
}
val loom = project.extensions.getByName<LoomGradleExtensionAPI>("loom")
loom.silentMojangMappingsLicense()
/**
* General dependencies used for all subprojects, e.g. mappings or the Minecraft version.
*/
dependencies {
/**
* Kotlin accessor methods are not generated in this gradle, they can be accessed through quoted names.
* Minecraft
* Kotlin accessor methods are not generated in this gradle
* they can be accessed through quoted names instead
*/
"minecraft"("com.mojang:minecraft:$minecraftVersion")
"mappings"(loom.layered {
@ -96,61 +93,16 @@ subprojects {
})
/**
* Helps to load mods in development through an extra directory. Sadly this does not support transitive dependencies. :-(
*/
fileTree("$extraModsPrefix-$minecraftVersion") { include("**/*.jar") }
.forEach { f ->
val sepIndex = f.nameWithoutExtension.lastIndexOf('-')
if (sepIndex == -1) {
throw IllegalArgumentException("Invalid mod name: '${f.nameWithoutExtension}'. Expected format: 'modName-version.jar'")
}
val mod = f.nameWithoutExtension.substring(0, sepIndex)
val version = f.nameWithoutExtension.substring(sepIndex + 1)
println("Extra mod ${f.nameWithoutExtension} detected.")
"modLocalRuntime"("extra-mods:$mod:$version")
}
/**
* Non-Minecraft dependencies
* non-Minecraft dependencies
*/
compileOnly("com.google.auto.service:auto-service:$autoServiceVersion")
annotationProcessor("com.google.auto.service:auto-service:$autoServiceVersion")
}
/**
* Maven publishing
*/
publishing {
publications {
val mpm = project.properties["maven-publish-method"] as String
println("[Publish Task] Publishing method for project '${project.name}: $mpm")
register(mpm, MavenPublication::class) {
artifactId = base.archivesName.get()
from(components["java"])
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
maven("file://${System.getenv("local_maven")}")
}
}
/**
* Disabling the runtime transformer from Architectury here.
* When the runtime transformer should be enabled again, remove this block and add the following to the respective subproject:
*
* configurations {
* "developmentFabric" { extendsFrom(configurations["common"]) } // or "developmentForge" for Forge
* }
*/
architectury {
compileOnly()
}
tasks {
/**
* Resource processing for defined targets. This will replace `${key}` with the specified values from the map below.
* resource processing for defined targets
* will replace `${key}` with the specified values from the map below
*/
processResources {
val resourceTargets = listOf("META-INF/mods.toml", "pack.mcmeta", "fabric.mod.json")
@ -165,15 +117,16 @@ subprojects {
"modDescription" to modDescription,
"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.
// use major version for FML only because wrong Forge version error message
// is way better than FML error message
"forgeFMLVersion" to forgeVersion.substringBefore("."),
"jeiVersion" to jeiVersion,
"reiVersion" to reiVersion,
"kubejsVersion" to kubejsVersion,
"githubUser" to githubUser,
"githubRepo" to githubRepo
)
println("[Process Resources] Replacing properties in resources: ")
println("[Process Resources] Replacing resource properties for project '${project.name}': ")
replaceProperties.forEach { (key, value) -> println("\t -> $key = $value") }
inputs.properties(replaceProperties)
@ -182,10 +135,46 @@ subprojects {
}
}
}
/**
* Maven publishing
*/
publishing {
publications {
val mpm = project.properties["maven-publish-method"] as String
println("[Publish Task] Publishing method for project '${project.name}': $mpm")
register(mpm, MavenPublication::class) {
artifactId = base.archivesName.get()
from(components["java"])
}
}
/**
* information on how to set up publishing
* https://docs.gradle.org/current/userguide/publishing_maven.html
*/
repositories {
maven("file://${System.getenv("local_maven")}")
}
}
/**
* disabling the runtime transformer from Architectury
* if the runtime transformer should be enabled again, remove this block and
* add the following to the respective subproject:
*
* configurations {
* "developmentFabric" { extendsFrom(configurations["common"]) }
* "developmentForge" { extendsFrom(configurations["common"]) }
* }
*/
architectury {
compileOnly()
}
}
/**
* Subproject configurations and tasks only applied to subprojects that are not the common project, e.g. Fabric or Forge.
* configurations for all subprojects except the common project
*/
subprojects {
if (project.path == ":Common") {
@ -197,15 +186,19 @@ subprojects {
extensions.configure<LoomGradleExtensionAPI> {
runs {
forEach {
it.runDir(if (sharedRunDir.toBoolean()) "../run" else "run")
// Allows DCEVM hot-swapping when using the JetBrains Runtime (https://github.com/JetBrains/JetBrainsRuntime).
val dir = "../run/${project.name.lowercase()}_${it.environment}"
println("[${project.name}] Run config '${it.name}' directory set to: $dir")
it.runDir(dir)
// allows DCEVM hot-swapping when using the JBR (https://github.com/JetBrains/JetBrainsRuntime)
it.vmArgs("-XX:+IgnoreUnrecognizedVMOptions", "-XX:+AllowEnhancedClassRedefinition")
}
}
/**
* "main" matches the default mod's name. Since `compileOnly()` is being used in Architectury,
* the local mods for the loaders need to be set up too. Otherwise, they won't recognize :Common.
* "main" matches the default mod name
* since `compileOnly()` is being used in Architectury, the local mods for the
* loaders need to be set up too
* otherwise, they won't recognize :Common.
*/
with(mods.maybeCreate("main")) {
fun Project.sourceSets() = extensions.getByName<SourceSetContainer>("sourceSets")
@ -215,7 +208,7 @@ subprojects {
}
val common by configurations.creating
val shadowCommon by configurations.creating // Don't use shadow from the shadow plugin because IDEA isn't supposed to index this.
val shadowCommon by configurations.creating // don't use shadow from the plugin, IDEA shouldn't index this
configurations {
"compileClasspath" { extendsFrom(common) }
"runtimeClasspath" { extendsFrom(common) }
@ -236,6 +229,7 @@ subprojects {
inputFile.set(named<ShadowJar>("shadowJar").get().archiveFile)
dependsOn("shadowJar")
archiveClassifier.set(null as String?)
injectAccessWidener.set(true)
}
named<Jar>("jar") {

View file

@ -3,9 +3,6 @@ group = com.almostreliable.mods
license = GNU Lesser General Public License v3.0
enabledPlatforms = fabric,forge
enableAccessWidener = false
sharedRunDir = false
extraModsDirectory = extra-mods
junitVersion = 5.9.0
# Minecraft
minecraftVersion = 1.19.2
@ -20,12 +17,12 @@ modDescription = Unify all resources.
# Project Dependencies
autoServiceVersion = 1.1.0
junitVersion = 5.9.0
parchmentVersion = 2022.11.27
# Mod Dependencies
reiVersion = 9.1.580
jeiVersion = 11.6.0.1012
kubejsVersion = 1902.6.0-build.132
reiVersion = 9.1.580
# Fabric Dependencies
fabricLoaderVersion = 0.14.19

View file

@ -1,7 +1,7 @@
pluginManagement {
repositories {
maven("https://maven.fabricmc.net/")
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
maven("https://maven.minecraftforge.net/")
gradlePluginPortal()
}