mirror of
https://github.com/AlmostReliable/almostunified.git
synced 2024-11-24 00:28:04 -05:00
parent
a5146a487e
commit
14044f3b57
243 changed files with 10664 additions and 4728 deletions
|
@ -9,7 +9,7 @@ tab_width = 4
|
|||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_formatter_tags_enabled = true
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = 120
|
||||
ij_wrap_on_typing = false
|
||||
|
@ -348,7 +348,7 @@ ij_kotlin_wrap_elvis_expressions = 1
|
|||
ij_kotlin_wrap_expression_body_functions = 1
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
|
||||
[{**.json,mcmod.info,pack.mcmeta}]
|
||||
[{*.json,mcmod.info,pack.mcmeta}]
|
||||
indent_size = 2
|
||||
max_line_length = 150
|
||||
tab_width = 2
|
||||
|
@ -365,7 +365,7 @@ ij_json_spaces_within_braces = false
|
|||
ij_json_spaces_within_brackets = false
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
[**.md]
|
||||
[*.md]
|
||||
ij_visual_guides = none
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
|
@ -379,11 +379,11 @@ ij_markdown_min_lines_around_block_elements = 1
|
|||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
|
||||
[**.toml]
|
||||
[*.toml]
|
||||
ij_visual_guides = none
|
||||
ij_toml_keep_indents_on_empty_lines = false
|
||||
|
||||
[{**.yml,**.yaml}]
|
||||
[{*.yml,*.yaml}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
|
|
62
.github/workflows/build.yml
vendored
62
.github/workflows/build.yml
vendored
|
@ -1,62 +0,0 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '1.20.1'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths:
|
||||
- 'gradle/**'
|
||||
- '**.java'
|
||||
- '**.kts'
|
||||
- '**.properties'
|
||||
- '**/build.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- '1.20.1'
|
||||
paths:
|
||||
- 'gradle/**'
|
||||
- '**.java'
|
||||
- '**.kts'
|
||||
- '**.properties'
|
||||
- '**/build.yml'
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 17
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
!contains(github.event.head_commit.message, '[skip build]')
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up JDK ${{ env.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
cache: gradle
|
||||
|
||||
- name: Cleanup Gradle Cache
|
||||
run: |
|
||||
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f ~/.gradle/caches/modules-2/gc.properties
|
||||
|
||||
- name: Make Gradle executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew build --stacktrace
|
||||
|
||||
- name: Test
|
||||
run: ./gradlew test --stacktrace
|
265
.github/workflows/release.yml
vendored
265
.github/workflows/release.yml
vendored
|
@ -1,265 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- 'v1.20.1-*.*.*'
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 17
|
||||
MOD_ID: 'almostunified'
|
||||
MOD_NAME: 'AlmostUnified'
|
||||
CURSEFORGE_ID: '633823'
|
||||
MODRINTH_ID: 'sdaSaQEz'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build, collect info, parse changelog
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
JAR_FILE: ${{ steps.collect_info.outputs.JAR_FILE }}
|
||||
MINECRAFT_VERSION: ${{ steps.collect_info.outputs.MINECRAFT_VERSION }}
|
||||
MOD_VERSION: ${{ steps.collect_info.outputs.MOD_VERSION }}
|
||||
RELEASE_TYPE: ${{ steps.collect_info.outputs.RELEASE_TYPE }}
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up JDK ${{ env.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
cache: gradle
|
||||
|
||||
- name: Cleanup Gradle Cache
|
||||
run: |
|
||||
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f ~/.gradle/caches/modules-2/gc.properties
|
||||
|
||||
- name: Make Gradle executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Assemble the JARs
|
||||
run: ./gradlew assemble
|
||||
|
||||
- name: Move JARs to central directory
|
||||
run: |
|
||||
mkdir output
|
||||
mv -f Forge/build/libs/*.jar Fabric/build/libs/*.jar output/
|
||||
rm -f output/*-dev-shadow.jar output/*-sources.jar
|
||||
|
||||
- name: Collect version information
|
||||
id: collect_info
|
||||
run: |
|
||||
shopt -s failglob # print a warning if a glob does not match anything
|
||||
set_var() {
|
||||
echo $1="$2"
|
||||
echo $1="$2" >> $GITHUB_OUTPUT
|
||||
declare -g $1="$2"
|
||||
}
|
||||
set_var JAR_FILE $(eval echo output/${{ env.MOD_ID }}-*-*-*.jar)
|
||||
set_var MINECRAFT_VERSION $(echo ${JAR_FILE%.*} | cut -d- -f3)
|
||||
set_var MOD_VERSION $(echo ${JAR_FILE%.*} | cut -d- -f4)
|
||||
set_var RELEASE_TYPE "$(echo ${GITHUB_REF##*/} | cut -d- -f3)"
|
||||
set_var RELEASE_TYPE "$([[ -z $RELEASE_TYPE ]] && echo release || echo $RELEASE_TYPE)"
|
||||
|
||||
- name: Install changelog parser
|
||||
uses: taiki-e/install-action@parse-changelog
|
||||
|
||||
- name: Parse changelog
|
||||
run: parse-changelog CHANGELOG.md ${{ steps.collect_info.outputs.MOD_VERSION }} > output/changelog.md
|
||||
|
||||
- name: Archive results
|
||||
run: tar -zcvf build.tar.gz output
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
path: build.tar.gz
|
||||
if-no-files-found: error
|
||||
retention-days: 3
|
||||
|
||||
- name: Job Summary
|
||||
run: |
|
||||
echo "# Version Information" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Minecraft Version: ${{ steps.collect_info.outputs.MINECRAFT_VERSION }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Mod Version: ${{ steps.collect_info.outputs.MOD_VERSION }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Release Type: ${{ steps.collect_info.outputs.RELEASE_TYPE }}" >> $GITHUB_STEP_SUMMARY
|
||||
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 -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
|
||||
echo "# Changelog" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
cat output/changelog.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
mr-fabric-release:
|
||||
name: Modrinth Fabric Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
|
||||
- name: Extract build artifacts
|
||||
run: tar -zxvf build.tar.gz
|
||||
|
||||
- name: Release Fabric on Modrinth
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: ${{ env.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
|
||||
files: output/*fabric*.jar
|
||||
name: ${{ env.MOD_NAME }}-Fabric-${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version: ${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}+fabric
|
||||
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
|
||||
changelog-file: output/changelog.md
|
||||
|
||||
loaders: fabric
|
||||
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
|
||||
java: ${{ env.JAVA_VERSION }}
|
||||
|
||||
dependencies: |
|
||||
jei(optional){curseforge:238222}{modrinth:u6dRKJwZ}
|
||||
rei(optional){curseforge:310111}{modrinth:nfn13YXA}
|
||||
|
||||
cf-fabric-release:
|
||||
name: CurseForge Fabric Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
|
||||
- name: Extract build artifacts
|
||||
run: tar -zxvf build.tar.gz
|
||||
|
||||
- name: Release Fabric on CurseForge
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
curseforge-id: ${{ env.CURSEFORGE_ID }}
|
||||
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
|
||||
files: output/*fabric*.jar
|
||||
name: ${{ env.MOD_NAME }}-Fabric-${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version: ${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}+fabric
|
||||
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
|
||||
changelog-file: output/changelog.md
|
||||
|
||||
loaders: fabric
|
||||
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
|
||||
java: ${{ env.JAVA_VERSION }}
|
||||
|
||||
dependencies: |
|
||||
jei(optional){curseforge:238222}{modrinth:u6dRKJwZ}
|
||||
rei(optional){curseforge:310111}{modrinth:nfn13YXA}
|
||||
|
||||
mr-forge-release:
|
||||
name: Modrinth Forge Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
|
||||
- name: Extract build artifacts
|
||||
run: tar -zxvf build.tar.gz
|
||||
|
||||
- name: Release Forge on Modrinth
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: ${{ env.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
|
||||
files: output/*forge*.jar
|
||||
name: ${{ env.MOD_NAME }}-Forge-${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version: ${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}+forge
|
||||
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
|
||||
changelog-file: output/changelog.md
|
||||
|
||||
loaders: |
|
||||
forge
|
||||
neoforge
|
||||
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
|
||||
java: ${{ env.JAVA_VERSION }}
|
||||
|
||||
dependencies: |
|
||||
jei(optional){curseforge:238222}{modrinth:u6dRKJwZ}
|
||||
rei(optional){curseforge:310111}{modrinth:nfn13YXA}
|
||||
|
||||
cf-forge-release:
|
||||
name: CurseForge Forge Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
|
||||
- name: Extract build artifacts
|
||||
run: tar -zxvf build.tar.gz
|
||||
|
||||
- name: Release Forge on CurseForge
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
curseforge-id: ${{ env.CURSEFORGE_ID }}
|
||||
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
|
||||
files: output/*forge*.jar
|
||||
name: ${{ env.MOD_NAME }}-Forge-${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version: ${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}+forge
|
||||
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
|
||||
changelog-file: output/changelog.md
|
||||
|
||||
loaders: |
|
||||
forge
|
||||
neoforge
|
||||
game-versions: ${{ needs.build.outputs.MINECRAFT_VERSION }}
|
||||
java: ${{ env.JAVA_VERSION }}
|
||||
|
||||
dependencies: |
|
||||
jei(optional){curseforge:238222}{modrinth:u6dRKJwZ}
|
||||
rei(optional){curseforge:310111}{modrinth:nfn13YXA}
|
||||
|
||||
github-release:
|
||||
name: GitHub Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
|
||||
- name: Extract build artifacts
|
||||
run: tar -zxvf build.tar.gz
|
||||
|
||||
- name: Release on GitHub
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: output/*.jar
|
||||
name: v${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version: ${{ needs.build.outputs.MINECRAFT_VERSION }}-${{ needs.build.outputs.MOD_VERSION }}
|
||||
version-type: ${{ needs.build.outputs.RELEASE_TYPE }}
|
||||
changelog-file: output/changelog.md
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -5,6 +5,19 @@ 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].
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Introduce data-driven like unification.
|
||||
- `unify.json` moved into sub-folder `unify` to let the user define different unification rules.
|
||||
- Custom tags, tag ownerships and tag inheritance moved into `tags.json`
|
||||
- `material` config inside `unify` moved into `placeholders.json` to let the user define multiple different
|
||||
placeholders
|
||||
- `reiJeiHide` key in `unify` renamed to `recipeViewerHiding`
|
||||
- Plugin system for mods to register their own unifiers
|
||||
- Missing mods used in mod priorities will now be logged
|
||||
- fixed a bug where stone stratas were not correctly identified (Fabric only)
|
||||
- ignore `show_notification` and `category` recipe keys by default
|
||||
|
||||
## [0.7.2] - 2023-11-21
|
||||
|
||||
## Added
|
||||
|
|
|
@ -3,10 +3,10 @@ val minecraftVersion: String by project
|
|||
val modPackage: String by project
|
||||
val modId: String by project
|
||||
val modName: String by project
|
||||
val junitVersion: String by project
|
||||
val fabricLoaderVersion: String by project
|
||||
val jeiVersion: String by project
|
||||
val reiVersion: String by project
|
||||
val emiVersion: String by project
|
||||
|
||||
plugins {
|
||||
id("com.github.gmazzo.buildconfig") version "4.0.4"
|
||||
|
@ -29,18 +29,13 @@ dependencies {
|
|||
* 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")
|
||||
modCompileOnly("net.fabricmc:fabric-loader:$fabricLoaderVersion")
|
||||
|
||||
// compile time mods
|
||||
// compile time
|
||||
modCompileOnly("mezz.jei:jei-$minecraftVersion-common-api:$jeiVersion") // required for jei plugin
|
||||
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")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
|
||||
modCompileOnly("dev.emi:emi-xplat-intermediary:$emiVersion+$minecraftVersion:api") // required for emi plugin
|
||||
}
|
||||
|
||||
buildConfig {
|
||||
|
@ -50,9 +45,3 @@ buildConfig {
|
|||
packageName(modPackage)
|
||||
useJavaOutput()
|
||||
}
|
||||
|
||||
//tasks {
|
||||
// withType<Test> {
|
||||
// useJUnitPlatform()
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
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 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 {
|
||||
|
||||
public static final Logger LOG = LogManager.getLogger(BuildConfig.MOD_NAME);
|
||||
|
||||
@Nullable private static AlmostUnifiedRuntime RUNTIME;
|
||||
@Nullable private static StartupConfig STARTUP_CONFIG;
|
||||
|
||||
public static boolean isRuntimeLoaded() {
|
||||
return RUNTIME != null;
|
||||
}
|
||||
|
||||
public static AlmostUnifiedRuntime getRuntime() {
|
||||
if (RUNTIME == null) {
|
||||
return AlmostUnifiedFallbackRuntime.getInstance();
|
||||
}
|
||||
return RUNTIME;
|
||||
}
|
||||
|
||||
public static StartupConfig getStartupConfig() {
|
||||
if (STARTUP_CONFIG == null) {
|
||||
STARTUP_CONFIG = Config.load(StartupConfig.NAME, new StartupConfig.Serializer());
|
||||
}
|
||||
return STARTUP_CONFIG;
|
||||
}
|
||||
|
||||
public static void onTagLoaderReload(Map<ResourceLocation, Collection<Holder<Item>>> tags) {
|
||||
RecipeHandlerFactory recipeHandlerFactory = new RecipeHandlerFactory();
|
||||
AlmostUnifiedPlatform.INSTANCE.bindRecipeHandlers(recipeHandlerFactory);
|
||||
|
||||
ServerConfigs serverConfigs = ServerConfigs.load();
|
||||
UnifyConfig unifyConfig = serverConfigs.getUnifyConfig();
|
||||
|
||||
TagReloadHandler.applyCustomTags(unifyConfig);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import com.almostreliable.unified.config.Config;
|
||||
import com.almostreliable.unified.config.StartupConfig;
|
||||
import com.almostreliable.unified.core.AlmostUnifiedRuntimeImpl;
|
||||
import com.almostreliable.unified.unification.loot.LootUnification;
|
||||
import com.almostreliable.unified.utils.CustomLogger;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({ "UtilityClassWithoutPrivateConstructor", "StaticVariableUsedBeforeInitialization" })
|
||||
public final class AlmostUnifiedCommon {
|
||||
|
||||
public static final Logger LOGGER = CustomLogger.create();
|
||||
public static final StartupConfig STARTUP_CONFIG = Config.load(StartupConfig.NAME, StartupConfig.SERIALIZER);
|
||||
|
||||
@Nullable private static AlmostUnifiedRuntimeImpl RUNTIME;
|
||||
|
||||
@Nullable
|
||||
public static AlmostUnifiedRuntime getRuntime() {
|
||||
return RUNTIME;
|
||||
}
|
||||
|
||||
public static void onTagLoaderReload(VanillaTagWrapper<Item> itemTags, VanillaTagWrapper<Block> blockTags) {
|
||||
RUNTIME = AlmostUnifiedRuntimeImpl.create(itemTags, blockTags);
|
||||
}
|
||||
|
||||
public static void onRecipeManagerReload(Map<ResourceLocation, JsonElement> recipes, HolderLookup.Provider registries) {
|
||||
Preconditions.checkNotNull(RUNTIME, "AlmostUnifiedRuntime was not loaded correctly");
|
||||
|
||||
RUNTIME.run(recipes);
|
||||
LootUnification.unifyLoot(RUNTIME, registries);
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.StoneStrataHandler;
|
||||
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.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 unifyConfig;
|
||||
@Nullable private TagMap<Item> filteredTagMap;
|
||||
@Nullable private ReplacementMap replacementMap;
|
||||
|
||||
public static AlmostUnifiedFallbackRuntime getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AlmostUnifiedFallbackRuntime();
|
||||
INSTANCE.reload();
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
unifyConfig = null;
|
||||
filteredTagMap = null;
|
||||
replacementMap = null;
|
||||
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 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<Item> stoneStrataTagMap = TagMap.create(stoneStrataTags);
|
||||
return StoneStrataHandler.create(config.getStoneStrata(), stoneStrataTags, stoneStrataTagMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Map<ResourceLocation, JsonElement> recipes, boolean skipClientTracking) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TagMap<Item>> getFilteredTagMap() {
|
||||
return Optional.ofNullable(filteredTagMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ReplacementMap> getReplacementMap() {
|
||||
return Optional.ofNullable(replacementMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UnifyConfig> getUnifyConfig() {
|
||||
return Optional.ofNullable(unifyConfig);
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.AlmostUnifiedLookup;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
import com.google.auto.service.AutoService;
|
||||
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.level.ItemLike;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AutoService(AlmostUnifiedLookup.class)
|
||||
public class AlmostUnifiedLookupImpl implements AlmostUnifiedLookup {
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return AlmostUnified.isRuntimeLoaded();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getReplacementForItem(ItemLike itemLike) {
|
||||
ResourceLocation id = BuiltInRegistries.ITEM.getKey(itemLike.asItem());
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.map(rm -> rm.getReplacementForItem(id))
|
||||
.flatMap(BuiltInRegistries.ITEM::getOptional)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getPreferredItemForTag(TagKey<Item> tag) {
|
||||
UnifyTag<Item> asUnifyTag = UnifyTag.item(tag.location());
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.map(rm -> rm.getPreferredItemForTag(asUnifyTag, $ -> true))
|
||||
.flatMap(BuiltInRegistries.ITEM::getOptional)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagKey<Item> getPreferredTagForItem(ItemLike itemLike) {
|
||||
ResourceLocation id = BuiltInRegistries.ITEM.getKey(itemLike.asItem());
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.map(rm -> rm.getPreferredTagForItem(id))
|
||||
.map(ut -> TagKey.create(Registries.ITEM, ut.location()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Item> getPotentialItems(TagKey<Item> tag) {
|
||||
UnifyTag<Item> asUnifyTag = UnifyTag.item(tag.location());
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getFilteredTagMap()
|
||||
.map(tagMap -> tagMap
|
||||
.getEntriesByTag(asUnifyTag)
|
||||
.stream()
|
||||
.flatMap(rl -> BuiltInRegistries.ITEM.getOptional(rl).stream())
|
||||
.collect(Collectors.toSet()))
|
||||
.orElseGet(Set::of);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TagKey<Item>> getConfiguredTags() {
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getFilteredTagMap()
|
||||
.map(tagMap -> tagMap
|
||||
.getTags()
|
||||
.stream()
|
||||
.map(ut -> TagKey.create(Registries.ITEM, ut.location()))
|
||||
.collect(Collectors.toSet()))
|
||||
.orElseGet(Set::of);
|
||||
}
|
||||
}
|
|
@ -1,17 +1,13 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
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 = load(AlmostUnifiedPlatform.class);
|
||||
AlmostUnifiedPlatform INSTANCE = ServiceLoader.load(AlmostUnifiedPlatform.class)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NullPointerException("Failed to load platform service."));
|
||||
|
||||
/**
|
||||
* Gets the current platform
|
||||
|
@ -32,22 +28,10 @@ public interface AlmostUnifiedPlatform {
|
|||
|
||||
Path getConfigPath();
|
||||
|
||||
Path getLogPath();
|
||||
|
||||
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;
|
||||
}
|
||||
Path getDebugLogPath();
|
||||
|
||||
enum Platform {
|
||||
FORGE,
|
||||
NEO_FORGE,
|
||||
FABRIC
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.config.UnifyConfig;
|
||||
import com.almostreliable.unified.utils.ReplacementMap;
|
||||
import com.almostreliable.unified.utils.TagMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AlmostUnifiedRuntime {
|
||||
|
||||
void run(Map<ResourceLocation, JsonElement> recipes, boolean skipClientTracking);
|
||||
|
||||
Optional<TagMap<Item>> getFilteredTagMap();
|
||||
|
||||
Optional<ReplacementMap> getReplacementMap();
|
||||
|
||||
Optional<UnifyConfig> getUnifyConfig();
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
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;
|
||||
import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
|
||||
import com.almostreliable.unified.utils.ReplacementMap;
|
||||
import com.almostreliable.unified.utils.TagMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
|
||||
|
||||
private final UnifyConfig unifyConfig;
|
||||
private final DuplicationConfig duplicationConfig;
|
||||
private final DebugConfig debugConfig;
|
||||
private final TagMap<Item> tagMap;
|
||||
private final ReplacementMap replacementMap;
|
||||
private final RecipeHandlerFactory recipeHandlerFactory;
|
||||
|
||||
AlmostUnifiedRuntimeImpl(
|
||||
ServerConfigs configs,
|
||||
TagMap<Item> tagMap,
|
||||
ReplacementMap repMap,
|
||||
RecipeHandlerFactory recipeHandlerFactory
|
||||
) {
|
||||
this.unifyConfig = configs.getUnifyConfig();
|
||||
this.duplicationConfig = configs.getDupConfig();
|
||||
this.debugConfig = configs.getDebugConfig();
|
||||
this.tagMap = tagMap;
|
||||
this.replacementMap = repMap;
|
||||
this.recipeHandlerFactory = recipeHandlerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Map<ResourceLocation, JsonElement> recipes, boolean skipClientTracking) {
|
||||
debugConfig.logRecipes(recipes, "recipes_before_unification.txt");
|
||||
debugConfig.logUnifyTagDump(tagMap);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
RecipeTransformer.Result result = new RecipeTransformer(
|
||||
recipeHandlerFactory,
|
||||
replacementMap,
|
||||
unifyConfig,
|
||||
duplicationConfig
|
||||
).transformRecipes(recipes, skipClientTracking);
|
||||
RecipeDumper dumper = new RecipeDumper(result, startTime, System.currentTimeMillis());
|
||||
dumper.dump(debugConfig.dumpOverview, debugConfig.dumpUnification, debugConfig.dumpDuplicates);
|
||||
|
||||
debugConfig.logRecipes(recipes, "recipes_after_unification.txt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TagMap<Item>> getFilteredTagMap() {
|
||||
return Optional.of(tagMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ReplacementMap> getReplacementMap() {
|
||||
return Optional.of(replacementMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UnifyConfig> getUnifyConfig() {
|
||||
return Optional.of(unifyConfig);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import com.almostreliable.unified.api.unification.Placeholders;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The core interface for the Almost Unified API.
|
||||
* <p>
|
||||
* Use this to get an instance of the {@link AlmostUnifiedRuntime} or to look up unification information.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface AlmostUnified {
|
||||
|
||||
/**
|
||||
* The default instance of Almost Unified.
|
||||
* <p>
|
||||
* If unavailable, it will return an empty instance that only returns default values for each method.<br>
|
||||
* This instance is only available on the logical server side.
|
||||
*/
|
||||
@SuppressWarnings("InnerClassReferencedViaSubclass")
|
||||
AlmostUnified INSTANCE = ServiceLoader.load(AlmostUnified.class).findFirst().orElseGet(Empty::new);
|
||||
|
||||
/**
|
||||
* Returns whether the {@link AlmostUnifiedRuntime} is loaded and ready to be used.
|
||||
* <p>
|
||||
* The {@link AlmostUnifiedRuntime} is only available on the logical server side.
|
||||
*
|
||||
* @return true if the {@link AlmostUnifiedRuntime} is loaded, false otherwise
|
||||
*/
|
||||
boolean isRuntimeLoaded();
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link AlmostUnifiedRuntime}.
|
||||
* <p>
|
||||
* The {@link AlmostUnifiedRuntime} is only available on the logical server side. This method returns null if the
|
||||
* runtime is not loaded. To check this beforehand, use {@link #isRuntimeLoaded()}. If you are sure the runtime is
|
||||
* available, you can use {@link #getRuntimeOrThrow()} instead.
|
||||
*
|
||||
* @return the {@link AlmostUnifiedRuntime}, or null if the runtime is not loaded
|
||||
*/
|
||||
@Nullable
|
||||
AlmostUnifiedRuntime getRuntime();
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link AlmostUnifiedRuntime}.
|
||||
* <p>
|
||||
* The {@link AlmostUnifiedRuntime} is only available on the logical server side. This method throws an exception
|
||||
* if the runtime is not loaded. To check this beforehand, use {@link #isRuntimeLoaded()}.
|
||||
*
|
||||
* @return the {@link AlmostUnifiedRuntime}
|
||||
*/
|
||||
AlmostUnifiedRuntime getRuntimeOrThrow();
|
||||
|
||||
/**
|
||||
* Returns all {@link TagKey}s used for the unification process.
|
||||
* <p>
|
||||
* The returned collection will only contain tags that have their {@link Placeholders} replaced and have been
|
||||
* validated. All tags will be unique.
|
||||
*
|
||||
* @return the {@link TagKey}s used for the unification, or empty if no tags are used
|
||||
*/
|
||||
Collection<TagKey<Item>> getTags();
|
||||
|
||||
/**
|
||||
* Returns all item entries for the given {@link TagKey}.
|
||||
* <p>
|
||||
* The returned collection will only contain entries if the provided {@link TagKey} is a configured unification tag.
|
||||
*
|
||||
* @param tag the {@link TagKey} to get the entries for
|
||||
* @return the item entries for the {@link TagKey}, or empty if not found
|
||||
*/
|
||||
Collection<Item> getTagEntries(TagKey<Item> tag);
|
||||
|
||||
/**
|
||||
* Returns the relevant {@link TagKey} for the given {@link ItemLike}
|
||||
* <p>
|
||||
* Since an item can only have a single relevant tag, this method is guaranteed to return a single {@link TagKey} as
|
||||
* long as there exists a configured unification tag that includes the item.
|
||||
*
|
||||
* @param itemLike the {@link ItemLike} to get the relevant {@link TagKey} for
|
||||
* @return the relevant {@link TagKey}, or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
TagKey<Item> getRelevantItemTag(ItemLike itemLike);
|
||||
|
||||
/**
|
||||
* Returns the target item for the given variant {@link ItemLike}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a {@link TagKey}.
|
||||
* It is used to replace the variant items in the unification process.<br>
|
||||
* This method will return null if no configured unification tag exists that includes the given item.
|
||||
* <p>
|
||||
* If the item is part of a stone variant, it will only check items within the same stone variant.
|
||||
*
|
||||
* @param itemLike the variant {@link ItemLike} to get the target item for
|
||||
* @return the target item, or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
Item getVariantItemTarget(ItemLike itemLike);
|
||||
|
||||
/**
|
||||
* Returns the target item for the given {@link TagKey}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a {@link TagKey}.
|
||||
* It is used to replace the variant items in the unification process.<br>
|
||||
* This method will return null the given {@link TagKey} is not a configured unification tag.
|
||||
*
|
||||
* @param tag the {@link TagKey} to get the target item for
|
||||
* @return the target item, or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
Item getTagTargetItem(TagKey<Item> tag);
|
||||
|
||||
class Empty implements AlmostUnified {
|
||||
|
||||
@Override
|
||||
public boolean isRuntimeLoaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AlmostUnifiedRuntime getRuntime() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlmostUnifiedRuntime getRuntimeOrThrow() {
|
||||
throw new IllegalStateException("runtime is not loaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TagKey<Item>> getTags() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Item> getTagEntries(TagKey<Item> tag) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagKey<Item> getRelevantItemTag(ItemLike itemLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getVariantItemTarget(ItemLike itemLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getTagTargetItem(TagKey<Item> tag) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
public interface AlmostUnifiedLookup {
|
||||
|
||||
AlmostUnifiedLookup INSTANCE = ServiceLoader.load(AlmostUnifiedLookup.class).findFirst().orElseGet(Empty::new);
|
||||
|
||||
boolean isLoaded();
|
||||
|
||||
/**
|
||||
* Returns the replacement item for a given {@link ItemLike}. Will return null if no configured
|
||||
* tag exists that includes the item.
|
||||
* <p>
|
||||
* If the item is part of some stone strata, it will only check items within the same stone strata.<br>
|
||||
* => e.g. "modid:deepslate_foo_ore" would not return "prio_modid:foo_ore".
|
||||
*
|
||||
* @param itemLike The item-like to find the replacement for
|
||||
* @return The replacement item or null if there is no replacement
|
||||
*/
|
||||
@Nullable
|
||||
Item getReplacementForItem(ItemLike itemLike);
|
||||
|
||||
/**
|
||||
* Returns the preferred item for a given {@link TagKey}. Will return null if no configured
|
||||
* tag exists that includes the item.
|
||||
* <p>
|
||||
* The preferred item is selected according to mod priorities, but it's possible to set a
|
||||
* fixed override in the config.
|
||||
*
|
||||
* @param tag The tag to find the preferred item for
|
||||
* @return The preferred item or null if there is no preferred item
|
||||
*/
|
||||
@Nullable
|
||||
Item getPreferredItemForTag(TagKey<Item> tag);
|
||||
|
||||
/**
|
||||
* Returns the preferred tag for a given {@link ItemLike} Will return null if no configured
|
||||
* tag exists that includes the item.
|
||||
*
|
||||
* @param itemLike The item-like to find the preferred tag for
|
||||
* @return The preferred tag or null if there is no preferred tag
|
||||
*/
|
||||
@Nullable
|
||||
TagKey<Item> getPreferredTagForItem(ItemLike itemLike);
|
||||
|
||||
/**
|
||||
* Returns all potential items which are part of a given tag.
|
||||
* <p>
|
||||
* Tags are only considered if they are part of the config,
|
||||
* otherwise, an empty set is always returned.
|
||||
*
|
||||
* @param tag The tag to find the potential items for
|
||||
* @return The potential items or an empty set if there are no potential items
|
||||
*/
|
||||
Set<Item> getPotentialItems(TagKey<Item> tag);
|
||||
|
||||
/**
|
||||
* Returns all configured tags.
|
||||
*
|
||||
* @return The configured tags
|
||||
*/
|
||||
Set<TagKey<Item>> getConfiguredTags();
|
||||
|
||||
class Empty implements AlmostUnifiedLookup {
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getReplacementForItem(ItemLike itemLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getPreferredItemForTag(TagKey<Item> tag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagKey<Item> getPreferredTagForItem(ItemLike itemLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Item> getPotentialItems(TagKey<Item> tag) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TagKey<Item>> getConfiguredTags() {
|
||||
return Set.of();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import com.almostreliable.unified.api.unification.Placeholders;
|
||||
import com.almostreliable.unified.api.unification.TagSubstitutions;
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.api.unification.UnificationSettings;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* The runtime is the core of the Almost Unified mod.<br>
|
||||
* It stores all required information about tags, recipes, unification settings, and configs.
|
||||
* <p>
|
||||
* The runtime is reconstructed every time the game reloads. Within the reconstruction process, all configs are reloaded,
|
||||
* plugin unifiers are collected, tag changes are applied, and all lookups are recreated.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface AlmostUnifiedRuntime {
|
||||
|
||||
/**
|
||||
* Returns a composition of all {@link UnificationSettings}s.
|
||||
* <p>
|
||||
* Because {@link UnificationSettings}s include config-specific settings, and are thus not composable, the
|
||||
* composition is returned as a {@link UnificationLookup}.
|
||||
*
|
||||
* @return the {@link UnificationSettings} composition as a {@link UnificationLookup}
|
||||
*/
|
||||
UnificationLookup getUnificationLookup();
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable collection of all {@link UnificationSettings}s.
|
||||
*
|
||||
* @return the {@link UnificationSettings} collection
|
||||
*/
|
||||
Collection<? extends UnificationSettings> getUnificationSettings();
|
||||
|
||||
/**
|
||||
* Returns the {@link UnificationSettings} with the given name.
|
||||
* <p>
|
||||
* The name of a {@link UnificationSettings} is the name of the config file it was created from.
|
||||
*
|
||||
* @param name the name of the {@link UnificationSettings}
|
||||
* @return the {@link UnificationSettings} with the given name or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
UnificationSettings getUnificationSettings(String name);
|
||||
|
||||
/**
|
||||
* Returns the {@link TagSubstitutions} instance.
|
||||
* <p>
|
||||
* {@link TagSubstitutions} are defined in the {@code TagConfig}.
|
||||
*
|
||||
* @return the {@link TagSubstitutions}
|
||||
*/
|
||||
TagSubstitutions getTagSubstitutions();
|
||||
|
||||
/**
|
||||
* Returns the {@link Placeholders} instance.
|
||||
* <p>
|
||||
* {@link Placeholders} are defined in the {@code PlaceholderConfig}.
|
||||
*
|
||||
* @return the {@link Placeholders}
|
||||
*/
|
||||
Placeholders getPlaceholders();
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public final class ModConstants {
|
||||
// recipe viewers
|
||||
public static final String JEI = "jei";
|
||||
public static final String REI = "roughlyenoughitems";
|
||||
|
||||
// custom unify handlers
|
||||
public static final String AD_ASTRA = "ad_astra";
|
||||
public static final String AMETHYST_IMBUEMENT = "amethyst_imbuement";
|
||||
public static final String ARS_CREO = "ars_creo";
|
||||
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 ENDER_IO = "enderio";
|
||||
public static final String GREGTECH_MODERN = "gtceu";
|
||||
public static final String IMMERSIVE_ENGINEERING = "immersiveengineering";
|
||||
public static final String INTEGRATED_DYNAMICS = "integrateddynamics";
|
||||
public static final String MEKANISM = "mekanism";
|
||||
public static final String MODERN_INDUSTRIALIZATION = "modern_industrialization";
|
||||
|
||||
private ModConstants() {}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.utils.TagMap;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class StoneStrataHandler {
|
||||
|
||||
private final List<String> stoneStrata;
|
||||
private final Pattern tagMatcher;
|
||||
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<Item> stoneStrataTagMap) {
|
||||
this.stoneStrata = createSortedStoneStrata(stoneStrata);
|
||||
this.tagMatcher = tagMatcher;
|
||||
this.stoneStrataTagMap = stoneStrataTagMap;
|
||||
this.stoneStrataTagCache = new HashMap<>();
|
||||
this.stoneStrataCache = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stone strata list sorted from longest to shortest.
|
||||
* <p>
|
||||
* This is required to ensure that the longest strata is returned and no sub-matches happen.<br>
|
||||
* Example: "nether" and "blue_nether" would both match "nether" if the list is not sorted.
|
||||
*
|
||||
* @param stoneStrata The stone strata list to sort.
|
||||
* @return The sorted stone strata list.
|
||||
*/
|
||||
private static List<String> createSortedStoneStrata(List<String> stoneStrata) {
|
||||
return stoneStrata.stream().sorted(Comparator.comparingInt(String::length).reversed()).toList();
|
||||
}
|
||||
|
||||
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)";
|
||||
});
|
||||
return new StoneStrataHandler(stoneStrataIds, tagMatcher, stoneStrataTagMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
.getTagsByEntry(item)
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(UnifyTag::location)
|
||||
.map(ResourceLocation::toString)
|
||||
.map(s -> {
|
||||
int i = s.lastIndexOf('/');
|
||||
return i == -1 ? null : s.substring(i + 1);
|
||||
})
|
||||
.orElse(null);
|
||||
|
||||
if (strata != null) {
|
||||
if (strata.equals("stone")) {
|
||||
return "";
|
||||
}
|
||||
return strata;
|
||||
}
|
||||
|
||||
for (String stone : stoneStrata) {
|
||||
if (item.getPath().contains(stone + "_")) {
|
||||
if (stone.equals("stone")) {
|
||||
return "";
|
||||
}
|
||||
return stone;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean isStoneStrataTag(UnifyTag<Item> tag) {
|
||||
return stoneStrataTagCache.computeIfAbsent(tag, t -> tagMatcher.matcher(t.location().toString()).matches());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.almostreliable.unified.api.constant;
|
||||
|
||||
import com.almostreliable.unified.BuildConfig;
|
||||
|
||||
@SuppressWarnings({ "SpellCheckingInspection", "StaticMethodOnlyUsedInOneClass" })
|
||||
public interface ModConstants {
|
||||
|
||||
String ALMOST_UNIFIED = BuildConfig.MOD_ID;
|
||||
|
||||
// recipe viewer mods
|
||||
String JEI = "jei";
|
||||
String REI = "roughlyenoughitems";
|
||||
String EMI = "emi";
|
||||
|
||||
// unify handler mods
|
||||
String AMETHYST_IMBUEMENT = "amethyst_imbuement";
|
||||
String ARS_CREO = "ars_creo";
|
||||
String ARS_ELEMENTAL = "ars_elemental";
|
||||
String ARS_NOUVEAU = "ars_nouveau";
|
||||
String ARS_SCALAES = "ars_scalaes";
|
||||
String CYCLIC = "cyclic";
|
||||
String ENDER_IO = "enderio";
|
||||
String GREGTECH_MODERN = "gtceu";
|
||||
String IMMERSIVE_ENGINEERING = "immersiveengineering";
|
||||
String INTEGRATED_DYNAMICS = "integrateddynamics";
|
||||
String MEKANISM = "mekanism";
|
||||
String MODERN_INDUSTRIALIZATION = "modern_industrialization";
|
||||
String OCCULTISM = "occultism";
|
||||
String PRODUCTIVE_TREES = "productivetrees";
|
||||
String THEURGY = "theurgy";
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.almostreliable.unified.api.constant;
|
||||
|
||||
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||
public interface RecipeConstants {
|
||||
|
||||
// inputs
|
||||
String ITEM = "item";
|
||||
String TAG = "tag";
|
||||
String INPUT = "input";
|
||||
String INPUTS = "inputs";
|
||||
String INGREDIENT = "ingredient";
|
||||
String INGREDIENTS = "ingredients";
|
||||
String INPUT_ITEMS = "inputItems";
|
||||
String CATALYST = "catalyst";
|
||||
|
||||
// outputs
|
||||
String OUTPUT = "output";
|
||||
String OUTPUTS = "outputs";
|
||||
String RESULT = "result";
|
||||
String RESULTS = "results";
|
||||
String OUTPUT_ITEMS = "outputItems";
|
||||
|
||||
// inner keys
|
||||
String VALUE = "value";
|
||||
String BASE = "base";
|
||||
String ID = "id";
|
||||
|
||||
// defaults
|
||||
String[] DEFAULT_INPUT_KEYS = {
|
||||
INPUT,
|
||||
INPUTS,
|
||||
INGREDIENT,
|
||||
INGREDIENTS,
|
||||
INPUT_ITEMS
|
||||
};
|
||||
String[] DEFAULT_INPUT_INNER_KEYS = {
|
||||
VALUE,
|
||||
BASE,
|
||||
INGREDIENT
|
||||
};
|
||||
String[] DEFAULT_OUTPUT_KEYS = {
|
||||
OUTPUT,
|
||||
OUTPUTS,
|
||||
RESULT,
|
||||
RESULTS,
|
||||
OUTPUT_ITEMS
|
||||
};
|
||||
String[] DEFAULT_OUTPUT_INNER_KEYS = {
|
||||
ITEM,
|
||||
INGREDIENT
|
||||
};
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.mixin.unifier;
|
||||
package com.almostreliable.unified.api.constant;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.almostreliable.unified.api.plugin;
|
||||
|
||||
/**
|
||||
* Annotation to use with {@link AlmostUnifiedPlugin} for NeoForge class discovery.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public @interface AlmostUnifiedNeoPlugin {}
|
|
@ -0,0 +1,36 @@
|
|||
package com.almostreliable.unified.api.plugin;
|
||||
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifierRegistry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Implemented by plugins that wish to register their own {@link RecipeUnifier}s.
|
||||
* <p>
|
||||
* NeoForge plugins should attach the {@link AlmostUnifiedNeoPlugin} annotation for discovery.<br>
|
||||
* Fabric plugins should use the {@code almostunified} entrypoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface AlmostUnifiedPlugin {
|
||||
|
||||
/**
|
||||
* Returns the identifier of the plugin.
|
||||
* <p>
|
||||
* If your mod has multiple plugins for different modules, make
|
||||
* sure they are unique.
|
||||
* <p>
|
||||
* If you register a recipe unifier although Almost Unified already
|
||||
* ships a recipe unifier for your recipes, yours will take precedence.
|
||||
*
|
||||
* @return the plugin id
|
||||
*/
|
||||
ResourceLocation getPluginId();
|
||||
|
||||
/**
|
||||
* Allows registration of custom {@link RecipeUnifier}s.
|
||||
*
|
||||
* @param registry the {@link RecipeUnifierRegistry} to register with
|
||||
*/
|
||||
default void registerRecipeUnifiers(RecipeUnifierRegistry registry) {}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.api.recipe;
|
||||
package com.almostreliable.unified.api.plugin;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package com.almostreliable.unified.api.recipe;
|
||||
|
||||
public final class RecipeConstants {
|
||||
// common inputs
|
||||
public static final String ITEM = "item";
|
||||
public static final String TAG = "tag";
|
||||
public static final String INPUT = "input";
|
||||
public static final String INPUTS = "inputs";
|
||||
public static final String INGREDIENT = "ingredient";
|
||||
public static final String INGREDIENTS = "ingredients";
|
||||
public static final String INPUT_ITEMS = "inputItems";
|
||||
public static final String CATALYST = "catalyst";
|
||||
|
||||
// common outputs
|
||||
public static final String OUTPUT = "output";
|
||||
public static final String OUTPUTS = "outputs";
|
||||
public static final String RESULT = "result";
|
||||
public static final String RESULTS = "results";
|
||||
public static final String OUTPUT_ITEMS = "outputItems";
|
||||
|
||||
// inner keys
|
||||
public static final String VALUE = "value";
|
||||
public static final String BASE = "base";
|
||||
|
||||
// ars nouveau
|
||||
public static final String PEDESTAL_ITEMS = "pedestalItems";
|
||||
public static final String REAGENT = "reagent";
|
||||
|
||||
// gregtech modern
|
||||
public static final String TICK_INPUTS = "tickInputs";
|
||||
public static final String TICK_OUTPUTS = "tickOutputs";
|
||||
|
||||
// immersive engineering
|
||||
public static final String INPUT_0 = "input0";
|
||||
public static final String INPUT_1 = "input1";
|
||||
public static final String ADDITIVES = "additives";
|
||||
public static final String SECONDARIES = "secondaries";
|
||||
public static final String SLAG = "slag";
|
||||
|
||||
// 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
|
||||
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() {}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package com.almostreliable.unified.api.recipe;
|
||||
|
||||
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.function.Predicate;
|
||||
|
||||
public interface RecipeContext {
|
||||
|
||||
@Nullable
|
||||
ResourceLocation getReplacementForItem(@Nullable ResourceLocation item);
|
||||
|
||||
@Nullable
|
||||
ResourceLocation getPreferredItemForTag(@Nullable UnifyTag<Item> tag, Predicate<ResourceLocation> filter);
|
||||
|
||||
@Nullable
|
||||
UnifyTag<Item> getPreferredTagForItem(@Nullable ResourceLocation item);
|
||||
|
||||
@Nullable
|
||||
JsonElement createIngredientReplacement(@Nullable JsonElement element);
|
||||
|
||||
@Nullable
|
||||
JsonElement createResultReplacement(@Nullable JsonElement element);
|
||||
|
||||
@Nullable
|
||||
JsonElement createResultReplacement(@Nullable JsonElement element, boolean includeTagCheck, String... lookupKeys);
|
||||
|
||||
ResourceLocation getType();
|
||||
|
||||
boolean hasProperty(String property);
|
||||
|
||||
default String getModId() {
|
||||
return getType().getNamespace();
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.almostreliable.unified.api.recipe;
|
||||
|
||||
public interface RecipeUnifier {
|
||||
void collectUnifier(RecipeUnifierBuilder builder);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.almostreliable.unified.api.recipe;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public interface RecipeUnifierBuilder {
|
||||
|
||||
void forEachObject(String property, BiFunction<JsonObject, RecipeContext, JsonObject> consumer);
|
||||
|
||||
void put(String property, BiFunction<JsonElement, RecipeContext, JsonElement> consumer);
|
||||
|
||||
<T extends JsonElement> void put(String property, Class<T> type, BiFunction<T, RecipeContext, T> consumer);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* Helper for handling mod priorities.
|
||||
* <p>
|
||||
* Mod priorities are used to choose the target items in the unification process.<br>
|
||||
* If a tag contains multiple items from different mods, the priority defines which item is chosen first. Priority
|
||||
* is sorted from highest to lowest. All unlisted mods have less priority than all listed mods.
|
||||
* <p>
|
||||
* Priority overrides allow overriding the priority mod for specific tags.<br>
|
||||
* When a priority override is specified for a tag, the mod priorities will be ignored.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ModPriorities extends Iterable<String> {
|
||||
|
||||
/**
|
||||
* Returns the priority override of the given tag.
|
||||
* <p>
|
||||
* This method returns the mod id if a priority override is configured for the given tag. If you want to resolve
|
||||
* the tag to an item, use {@link #findPriorityOverrideItem(TagKey, List)} or
|
||||
* {@link #findTargetItem(TagKey, List)} instead.
|
||||
*
|
||||
* @param tag the tag to get the priority override for
|
||||
* @return the priority override, or null if no override exists
|
||||
*/
|
||||
@Nullable
|
||||
String getPriorityOverride(TagKey<Item> tag);
|
||||
|
||||
/**
|
||||
* Returns the priority override item of the given tag contained in the list of potential items.
|
||||
* <p>
|
||||
* This method returns the item if a priority override is configured for the given tag. If you want to resolve the
|
||||
* tag to an item by also using the mod priorities, use {@link #findTargetItem(TagKey, List)} instead.
|
||||
*
|
||||
* @param tag the tag to get the priority override item for
|
||||
* @param items the list of potential items, sorted from shortest to longest id
|
||||
* @return the priority override item, or null if no override exists
|
||||
*/
|
||||
@Nullable
|
||||
UnificationEntry<Item> findPriorityOverrideItem(TagKey<Item> tag, List<UnificationEntry<Item>> items);
|
||||
|
||||
/**
|
||||
* Returns the target item of the given tag contained in the list of potential items.
|
||||
* <p>
|
||||
* The item is chosen according to the priority overrides first if available. If no priority override is configured,
|
||||
* the item is chosen according to the mod priorities.
|
||||
* <p>
|
||||
* This method can return null if no override exists, and the potential items only include items with namespaces
|
||||
* that are not part of the mod priorities.
|
||||
*
|
||||
* @param tag the tag to get the target item for
|
||||
* @param items the list of potential items, sorted from shortest to longest id
|
||||
* @return the target item of the given tag, or null if no target item could be found
|
||||
*/
|
||||
@Nullable
|
||||
UnificationEntry<Item> findTargetItem(TagKey<Item> tag, List<UnificationEntry<Item>> items);
|
||||
|
||||
default Stream<String> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Helper for handling placeholders in configs.
|
||||
* <p>
|
||||
* Placeholders are used to replace specific patterns in config values with a set of values to easily cover all possible
|
||||
* combinations of all values. Placeholders are in the format of {@code {placeholder}}.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface Placeholders {
|
||||
|
||||
/**
|
||||
* Applies the placeholders to given string.
|
||||
* <p>
|
||||
* The given string is expected to contain an arbitrary number of placeholders or no placeholders at all.
|
||||
* <p>
|
||||
* This method replaces all contained placeholders with all combinations of possible values. If string doesn't
|
||||
* contain any placeholders, the same string will be returned as the only element in the returned collection.
|
||||
*
|
||||
* @param str the string to apply the placeholders to
|
||||
* @return a collection containing all combinations of applied placeholder values
|
||||
*/
|
||||
Collection<String> apply(String str);
|
||||
|
||||
/**
|
||||
* Returns all placeholders as a collection.
|
||||
*
|
||||
* @return a collection containing all placeholders
|
||||
*/
|
||||
Collection<String> getPlaceholders();
|
||||
|
||||
/**
|
||||
* Returns all possible replacements for given placeholder.
|
||||
* <p>
|
||||
* The possible replacement values are ensured to be unique.
|
||||
*
|
||||
* @param placeholder the placeholder to get replacements for
|
||||
* @return a collection containing all possible replacements for the given placeholder
|
||||
*/
|
||||
Collection<String> getReplacements(String placeholder);
|
||||
|
||||
/**
|
||||
* Passes each placeholder and its possible replacements to the given consumer.
|
||||
*
|
||||
* @param consumer the consumer to pass each placeholder and its possible replacements to
|
||||
*/
|
||||
void forEach(BiConsumer<String, Collection<String>> consumer);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Helper to get the stone variant of an item.
|
||||
* <p>
|
||||
* Upon creation, this lookup will try to fetch the stone variant from the
|
||||
* {@code c:ores_in_ground} tag. If the tag is present, it will always take priority.
|
||||
* <p>
|
||||
* As a fallback, it will lazily try to fetch the stone variant from the item or
|
||||
* the respective block id.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface StoneVariants {
|
||||
|
||||
/**
|
||||
* Returns the stone variant for the given item.
|
||||
* <p>
|
||||
* This assumes that the item has a valid ore tag.<br>
|
||||
* Use {@link #isOreTag(TagKey)} to ensure this requirement.
|
||||
* <p>
|
||||
* If the detected variant is stone, an empty string will be returned.
|
||||
*
|
||||
* @param item the item to get the stone variant from
|
||||
* @return the stone variant of the item
|
||||
*/
|
||||
String getStoneVariant(ResourceLocation item);
|
||||
|
||||
/**
|
||||
* Checks if the given tag is an ore tag.
|
||||
*
|
||||
* @param tag the tag to check
|
||||
* @return true if the tag is an ore tag, false otherwise
|
||||
*/
|
||||
boolean isOreTag(TagKey<Item> tag);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper for tracking tag substitutions.
|
||||
* <p>
|
||||
* The tag substitutions system allows converting tags (replaced tags) to other tags (substitute tags).<br>
|
||||
* The system copies all entries of the replaced tags to their respective substitute tags. After that, it replaces
|
||||
* all occurrences of the replaced tags with their substitute tags in all recipes.
|
||||
* <p>
|
||||
* Example:<br>
|
||||
* If we define the following entry {@code minecraft:logs -> minecraft:planks}, any recipes making use of the tag
|
||||
* {@code minecraft:logs} will use the {@code minecraft:planks} tag instead.
|
||||
* <p>
|
||||
* This can be useful when mods use different tag conventions like {@code c:ingots/iron} and {@code c:iron_ingots}.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface TagSubstitutions {
|
||||
|
||||
/**
|
||||
* Returns the substitute tag for the provided replaced tag.
|
||||
*
|
||||
* @param replacedTag the replaced tag to get the substitute for
|
||||
* @return the substitute tag or null if the provided tag is not a valid configured replaced tag
|
||||
*/
|
||||
@Nullable
|
||||
TagKey<Item> getSubstituteTag(TagKey<Item> replacedTag);
|
||||
|
||||
/**
|
||||
* Returns all replaced tags for the provided substitute tag.
|
||||
*
|
||||
* @param substituteTag the substitute tag to get the replaced tags for
|
||||
* @return a collection of all replaced tags for the provided substitute tag or an empty collection if the
|
||||
* provided tag is not a valid configured substitute tag
|
||||
*/
|
||||
Collection<TagKey<Item>> getReplacedTags(TagKey<Item> substituteTag);
|
||||
|
||||
/**
|
||||
* Returns all valid configured replaced tags for all substitute tags.
|
||||
*
|
||||
* @return a collection of all valid configured replaced tags
|
||||
*/
|
||||
Set<TagKey<Item>> getReplacedTags();
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
|
||||
/**
|
||||
* Helper to abstract a single entry used in unification.
|
||||
* <p>
|
||||
* This helper allows easy access to registry information while also offering utility methods.
|
||||
*
|
||||
* @param <T> the type of the entry
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface UnificationEntry<T> {
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceKey} this entry is bound to in the {@link Registry}.
|
||||
*
|
||||
* @return the {@link ResourceKey} this entry is bound to
|
||||
*/
|
||||
ResourceKey<T> key();
|
||||
|
||||
/**
|
||||
* Returns the id of this entry.
|
||||
* <p>
|
||||
* The id is the {@link ResourceLocation} of the entry in the {@link Registry}.
|
||||
*
|
||||
* @return the id of this entry
|
||||
*/
|
||||
ResourceLocation id();
|
||||
|
||||
/**
|
||||
* Returns the raw value of this entry.
|
||||
*
|
||||
* @return the raw value
|
||||
*/
|
||||
T value();
|
||||
|
||||
/**
|
||||
* Returns the tag of this entry.
|
||||
* <p>
|
||||
* The tag represents the relevant tag used for the unification. Each entry can only have a single unification tag.
|
||||
*
|
||||
* @return the tag
|
||||
*/
|
||||
TagKey<T> tag();
|
||||
|
||||
/**
|
||||
* Returns the value as a {@link Holder.Reference}.
|
||||
*
|
||||
* @return the value holder
|
||||
*/
|
||||
Holder.Reference<T> asHolderOrThrow();
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Interface exposing composable unification information for a single unification config.
|
||||
* <p>
|
||||
* There exists one instance for each config.<br>
|
||||
* A unification lookup only exposes composable information. The composition of all lookups is used to check global
|
||||
* unification information. For example, when retrieving the replacement of a specific item. The composition can't
|
||||
* expose internal information such as mod priorities.
|
||||
* <p>
|
||||
* If a lookup with exposed configuration is required, use {@link UnificationSettings} instead.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface UnificationLookup {
|
||||
|
||||
/**
|
||||
* Returns all {@link TagKey}s used for the unification process of the config this lookup is for.
|
||||
* <p>
|
||||
* The returned collection will only contain tags that have their {@link Placeholders} replaced and have been
|
||||
* validated. All tags will be unique.
|
||||
*
|
||||
* @return the {@link TagKey}s used for the unification, or empty if no tags are used
|
||||
*/
|
||||
Collection<TagKey<Item>> getTags();
|
||||
|
||||
/**
|
||||
* Returns all {@link UnificationEntry}s for the given {@link TagKey}.
|
||||
* <p>
|
||||
* The returned collection will only contain entries if the provided {@link TagKey} is part of the config this
|
||||
* lookup is for.
|
||||
*
|
||||
* @param tag the {@link TagKey} to get the entries for
|
||||
* @return the {@link UnificationEntry}s for the {@link TagKey}, or empty if no {@link UnificationEntry}s are found
|
||||
*/
|
||||
Collection<UnificationEntry<Item>> getTagEntries(TagKey<Item> tag);
|
||||
|
||||
/**
|
||||
* Returns the {@link UnificationEntry} for the given item id.
|
||||
* <p>
|
||||
* If the config this lookup is for doesn't cover the {@link Item}, null is returned.
|
||||
*
|
||||
* @param item the item id to get the {@link UnificationEntry} for
|
||||
* @return the {@link UnificationEntry} for the item id, or null if no {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
UnificationEntry<Item> getItemEntry(ResourceLocation item);
|
||||
|
||||
/**
|
||||
* Returns the {@link UnificationEntry} for the given {@link Item}.
|
||||
* <p>
|
||||
* If the config this lookup is for doesn't cover the {@link Item}, null is returned.
|
||||
*
|
||||
* @param item the {@link Item} to get the {@link UnificationEntry} for
|
||||
* @return the {@link UnificationEntry} for the {@link Item}, or null if no {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getItemEntry(Item item) {
|
||||
return getItemEntry(BuiltInRegistries.ITEM.getKey(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link UnificationEntry} for the given item {@link Holder}.
|
||||
* <p>
|
||||
* If the config this lookup is for doesn't cover the {@link Item}, null is returned.
|
||||
*
|
||||
* @param item the item {@link Holder} to get the {@link UnificationEntry} for
|
||||
* @return the {@link UnificationEntry} for the item {@link Holder}, or null if no {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getItemEntry(Holder<Item> item) {
|
||||
return getItemEntry(item.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relevant {@link TagKey} for the given item id.
|
||||
* <p>
|
||||
* Since an item can only have a single relevant tag, this method is guaranteed to return a single {@link TagKey} as
|
||||
* long as the config this lookup is for covers the {@link Item}.
|
||||
*
|
||||
* @param item the item id to get the relevant {@link TagKey} for
|
||||
* @return the relevant {@link TagKey} for the item id, or null if no relevant {@link TagKey} is found
|
||||
*/
|
||||
@Nullable
|
||||
TagKey<Item> getRelevantItemTag(ResourceLocation item);
|
||||
|
||||
/**
|
||||
* Returns the relevant {@link TagKey} for the given {@link Item}.
|
||||
* <p>
|
||||
* Since an item can only have a single relevant tag, this method is guaranteed to return a single {@link TagKey} as
|
||||
* long as the config this lookup is for covers the {@link Item}.
|
||||
*
|
||||
* @param item the {@link Item} to get the relevant {@link TagKey} for
|
||||
* @return the relevant {@link TagKey} for the {@link Item}, or null if no relevant {@link TagKey} is found
|
||||
*/
|
||||
@Nullable
|
||||
default TagKey<Item> getRelevantItemTag(Item item) {
|
||||
return getRelevantItemTag(BuiltInRegistries.ITEM.getKey(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relevant {@link TagKey} for the given item {@link Holder}.
|
||||
* <p>
|
||||
* Since an item can only have a single relevant tag, this method is guaranteed to return a single {@link TagKey} as
|
||||
* long as the config this lookup is for covers the {@link Item}.
|
||||
*
|
||||
* @param item the item {@link Holder} to get the relevant {@link TagKey} for
|
||||
* @return the relevant {@link TagKey} for the item {@link Holder}, or null if no relevant {@link TagKey} is found
|
||||
*/
|
||||
@Nullable
|
||||
default TagKey<Item> getRelevantItemTag(Holder<Item> item) {
|
||||
return getRelevantItemTag(item.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target item {@link UnificationEntry} for the given variant item id.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a tag. It is used
|
||||
* to replace the variant items in the unification process.<br>
|
||||
* This method will return null if the config this lookup is for doesn't cover a unification tag that includes
|
||||
* the given item.
|
||||
* <p>
|
||||
* If the item is part of a stone variant, it will only check items within the same stone variant.
|
||||
*
|
||||
* @param item the variant item id to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the variant item id, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
UnificationEntry<Item> getVariantItemTarget(ResourceLocation item);
|
||||
|
||||
/**
|
||||
* Returns the target item {@link UnificationEntry} for the given variant {@link Item}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a tag. It is used
|
||||
* to replace the variant items in the unification process.<br>
|
||||
* This method will return null if the config this lookup is for doesn't cover a unification tag that includes
|
||||
* the given item.
|
||||
* <p>
|
||||
* If the item is part of a stone variant, it will only check items within the same stone variant.
|
||||
*
|
||||
* @param item the variant {@link Item} to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the variant {@link Item}, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getVariantItemTarget(Item item) {
|
||||
return getVariantItemTarget(BuiltInRegistries.ITEM.getKey(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target {@link UnificationEntry} for the given variant item {@link Holder}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a tag. It is used
|
||||
* to replace the variant items in the unification process.<br>
|
||||
* This method will return null if the config this lookup is for doesn't cover a unification tag that includes
|
||||
* the given item.
|
||||
* <p>
|
||||
* If the item is part of a stone variant, it will only check items within the same stone variant.
|
||||
*
|
||||
* @param item the variant item {@link Holder} to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the variant item {@link Holder}, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getVariantItemTarget(Holder<Item> item) {
|
||||
return getVariantItemTarget(item.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target {@link UnificationEntry} for the given variant {@link UnificationEntry}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a {@link TagKey}.
|
||||
* It is used to replace the variant items in the unification process.<br>
|
||||
* This method will return null if the config this lookup is for doesn't cover a unification tag that includes
|
||||
* the given item.
|
||||
* <p>
|
||||
* If the item is part of a stone variant, it will only check items within the same stone variant.
|
||||
*
|
||||
* @param item the variant {@link UnificationEntry} to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the variant {@link UnificationEntry}, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getVariantItemTarget(UnificationEntry<Item> item) {
|
||||
return getVariantItemTarget(item.asHolderOrThrow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target {@link UnificationEntry} for the given {@link TagKey} that matches the given filter.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a {@link TagKey}.
|
||||
* It is used to replace the variant items in the unification process.<br>
|
||||
* If the config this lookup is for doesn't cover the {@link TagKey}, null is returned.
|
||||
*
|
||||
* @param tag the {@link TagKey} to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the {@link TagKey}, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
UnificationEntry<Item> getTagTargetItem(TagKey<Item> tag, Predicate<ResourceLocation> itemFilter);
|
||||
|
||||
/**
|
||||
* Returns the target {@link UnificationEntry} for the given {@link TagKey}.
|
||||
* <p>
|
||||
* The target item describes the item with the highest priority among all variant items within a {@link TagKey}.
|
||||
* It is used to replace the variant items in the unification process.<br>
|
||||
* If the config this lookup is for doesn't cover the {@link TagKey}, null is returned.
|
||||
*
|
||||
* @param tag the {@link TagKey} to get the target {@link UnificationEntry} for
|
||||
* @return the target {@link UnificationEntry} for the {@link TagKey}, or null if no target
|
||||
* {@link UnificationEntry} is found
|
||||
*/
|
||||
@Nullable
|
||||
default UnificationEntry<Item> getTagTargetItem(TagKey<Item> tag) {
|
||||
return getTagTargetItem(tag, $ -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link ItemStack} is part of any tags the given {@link Ingredient} points to.
|
||||
* <p>
|
||||
* To check this, this method fetches all unification tags of the {@link Item}s within the given {@link Ingredient}
|
||||
* and checks whether the given {@link ItemStack} is part of them.
|
||||
*
|
||||
* @param ingredient the {@link Ingredient} to check to get the relevant tags for
|
||||
* @param item the {@link ItemStack} to check
|
||||
* @return whether the given {@link ItemStack} is part of any tags the given {@link Ingredient} points to
|
||||
*/
|
||||
boolean isUnifiedIngredientItem(Ingredient ingredient, ItemStack item);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Interface exposing unification information for a single unification config.
|
||||
* <p>
|
||||
* There exists one instance for each config.<br>
|
||||
* Because {@link UnificationLookup}s are not composable, this interface should only be used when specific settings
|
||||
* need to be checked.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface UnificationSettings extends UnificationLookup {
|
||||
|
||||
/**
|
||||
* Returns the name of the unification config these settings are for.
|
||||
* <p>
|
||||
* The name of the config is the file name of the JSON file within the unification subfolder without the file
|
||||
* extension.
|
||||
*
|
||||
* @return the name of the config
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ModPriorities} these settings are based on.
|
||||
*
|
||||
* @return the {@link ModPriorities}
|
||||
*/
|
||||
ModPriorities getModPriorities();
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link StoneVariants} these settings are based on.
|
||||
*
|
||||
* @return the {@link StoneVariants}
|
||||
*/
|
||||
StoneVariants getStoneVariants();
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link RecipeData} should be included in the unification process.
|
||||
* <p>
|
||||
* This method is a quick way to check the recipe id and type.
|
||||
*
|
||||
* @param recipe the recipe to check
|
||||
* @return true if the recipe should be included, false otherwise
|
||||
*/
|
||||
default boolean shouldIncludeRecipe(RecipeData recipe) {
|
||||
return shouldIncludeRecipeType(recipe) && shouldIncludeRecipeId(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given recipe type should be included in the unification process.
|
||||
*
|
||||
* @param type the recipe type to check
|
||||
* @return true if the recipe type should be included, false otherwise
|
||||
*/
|
||||
boolean shouldIncludeRecipeType(ResourceLocation type);
|
||||
|
||||
/**
|
||||
* Returns whether the recipe type of the given {@link RecipeData} should be included in the unification process.
|
||||
*
|
||||
* @param recipe the recipe to check
|
||||
* @return true if the recipe type should be included, false otherwise
|
||||
*/
|
||||
default boolean shouldIncludeRecipeType(RecipeData recipe) {
|
||||
return shouldIncludeRecipeType(recipe.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given recipe id should be included in the unification process.
|
||||
*
|
||||
* @param id the recipe id to check
|
||||
* @return true if the recipe id should be included, false otherwise
|
||||
*/
|
||||
boolean shouldIncludeRecipeId(ResourceLocation id);
|
||||
|
||||
/**
|
||||
* Returns whether the recipe id of the given {@link RecipeData} should be included in the unification process.
|
||||
*
|
||||
* @param recipe the recipe to check
|
||||
* @return true if the recipe id should be included, false otherwise
|
||||
*/
|
||||
default boolean shouldIncludeRecipeId(RecipeData recipe) {
|
||||
return shouldIncludeRecipeId(recipe.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether variant items of this config should be hidden in recipe viewers.
|
||||
*
|
||||
* @return true if variant items should be hidden, false otherwise
|
||||
*/
|
||||
boolean shouldHideVariantItems();
|
||||
|
||||
/**
|
||||
* Returns whether loot tables should be unified with the unification of this config.
|
||||
*
|
||||
* @return true if loot tables should be unified, false otherwise
|
||||
*/
|
||||
boolean shouldUnifyLoot();
|
||||
|
||||
/**
|
||||
* Returns whether the given loot table should be included in the unification process.
|
||||
*
|
||||
* @param table the loot table to check
|
||||
* @return true if the loot table should be included, false otherwise
|
||||
*/
|
||||
boolean shouldIncludeLootTable(ResourceLocation table);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.almostreliable.unified.api.unification.bundled;
|
||||
|
||||
import com.almostreliable.unified.api.constant.RecipeConstants;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeJson;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.UnificationHelper;
|
||||
|
||||
/**
|
||||
* The most basic {@link RecipeUnifier} implementation.
|
||||
* <p>
|
||||
* This {@link RecipeUnifier} will only be used if no other {@link RecipeUnifier} is registered for a recipe and more
|
||||
* specific {@link RecipeUnifier}s such as the {@link ShapedRecipeUnifier} or {@link SmithingRecipeUnifier} can not be
|
||||
* applied.<br>
|
||||
* It targets the most basic and commonly used keys and structures for inputs and outputs.
|
||||
* <p>
|
||||
* Custom {@link RecipeUnifier}s can call {@link GenericRecipeUnifier#unify(UnificationHelper, RecipeJson)} on the
|
||||
* {@link GenericRecipeUnifier#INSTANCE} to apply the defaults.
|
||||
* <p>
|
||||
* For more specific {@link RecipeUnifier} implementations, see {@link ShapedRecipeUnifier} and
|
||||
* {@link SmithingRecipeUnifier}.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class GenericRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
public static final GenericRecipeUnifier INSTANCE = new GenericRecipeUnifier();
|
||||
|
||||
@Override
|
||||
public void unify(UnificationHelper helper, RecipeJson recipe) {
|
||||
unifyInputs(helper, recipe);
|
||||
unifyOutputs(helper, recipe);
|
||||
}
|
||||
|
||||
public void unifyInputs(UnificationHelper helper, RecipeJson recipe) {
|
||||
helper.unifyInputs(recipe, RecipeConstants.DEFAULT_INPUT_KEYS);
|
||||
}
|
||||
|
||||
public void unifyOutputs(UnificationHelper helper, RecipeJson recipe) {
|
||||
helper.unifyOutputs(recipe, RecipeConstants.DEFAULT_OUTPUT_KEYS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.almostreliable.unified.api.unification.bundled;
|
||||
|
||||
import com.almostreliable.unified.api.constant.RecipeConstants;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeData;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeJson;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.UnificationHelper;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* The {@link RecipeUnifier} implementation for shaped crafting recipes.
|
||||
* <p>
|
||||
* This {@link RecipeUnifier} will only be used if no other {@link RecipeUnifier} is registered for a recipe. It targets
|
||||
* vanilla shaped crafting recipes and custom recipe types that use common properties of shaped crafting recipes.<br>
|
||||
* If this {@link RecipeUnifier} can't be applied for a recipe, the {@link GenericRecipeUnifier} will be used as the
|
||||
* last fallback.
|
||||
* <p>
|
||||
* To check if a recipe is applicable for this {@link RecipeUnifier}, use
|
||||
* {@link ShapedRecipeUnifier#isApplicable(RecipeData)}. Custom {@link RecipeUnifier}s can call
|
||||
* {@link ShapedRecipeUnifier#unify(UnificationHelper, RecipeJson)} on the {@link ShapedRecipeUnifier#INSTANCE} to apply
|
||||
* the defaults.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class ShapedRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
public static final RecipeUnifier INSTANCE = new ShapedRecipeUnifier();
|
||||
public static final ResourceLocation SHAPED_TYPE = ResourceLocation.withDefaultNamespace("crafting_shaped");
|
||||
public static final String KEY_PROPERTY = "key";
|
||||
public static final String PATTERN_PROPERTY = "pattern";
|
||||
|
||||
@Override
|
||||
public void unify(UnificationHelper helper, RecipeJson recipe) {
|
||||
GenericRecipeUnifier.INSTANCE.unify(helper, recipe);
|
||||
|
||||
if (recipe.getProperty(KEY_PROPERTY) instanceof JsonObject json) {
|
||||
for (var e : json.entrySet()) {
|
||||
helper.unifyInputElement(e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this {@link RecipeUnifier} can be applied for the given {@link RecipeData}.
|
||||
* <p>
|
||||
* This method checks for the vanilla shaped crafting recipe type. If it's a custom recipe type, it tries to find
|
||||
* common keys for the shaped crafting recipe.
|
||||
*
|
||||
* @param recipe the recipe to check
|
||||
* @return true if the {@link RecipeUnifier} can be applied, false otherwise
|
||||
*/
|
||||
public static boolean isApplicable(RecipeData recipe) {
|
||||
return recipe.getType().equals(SHAPED_TYPE) || hasShapedCraftingLikeStructure(recipe);
|
||||
}
|
||||
|
||||
@SuppressWarnings("FoldExpressionIntoStream")
|
||||
private static boolean hasShapedCraftingLikeStructure(RecipeData recipe) {
|
||||
return recipe.hasProperty(KEY_PROPERTY) &&
|
||||
recipe.hasProperty(PATTERN_PROPERTY) &&
|
||||
recipe.hasProperty(RecipeConstants.RESULT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.almostreliable.unified.api.unification.bundled;
|
||||
|
||||
import com.almostreliable.unified.api.constant.RecipeConstants;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeData;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeJson;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.UnificationHelper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* The {@link RecipeUnifier} implementation for smithing recipes.
|
||||
* <p>
|
||||
* This {@link RecipeUnifier} will only be used if no other {@link RecipeUnifier} is registered for a recipe. It targets
|
||||
* vanilla smithing recipes and custom recipe types that use common properties of smithing recipes.<br>
|
||||
* If this {@link RecipeUnifier} can't be applied for a recipe, the {@link GenericRecipeUnifier} will be used as the
|
||||
* last fallback.
|
||||
* <p>
|
||||
* To check if a recipe is applicable for this {@link RecipeUnifier}, use
|
||||
* {@link SmithingRecipeUnifier#isApplicable(RecipeData)}. Custom {@link RecipeUnifier}s can call
|
||||
* {@link SmithingRecipeUnifier#unify(UnificationHelper, RecipeJson)} on the {@link SmithingRecipeUnifier#INSTANCE} to
|
||||
* apply the defaults.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class SmithingRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
public static final SmithingRecipeUnifier INSTANCE = new SmithingRecipeUnifier();
|
||||
public static final ResourceLocation TRANSFORM_TYPE = ResourceLocation.withDefaultNamespace("smithing_transform");
|
||||
public static final ResourceLocation TRIM_TYPE = ResourceLocation.withDefaultNamespace("smithing_trim");
|
||||
public static final String ADDITION_PROPERTY = "addition";
|
||||
public static final String BASE_PROPERTY = "base";
|
||||
public static final String TEMPLATE_PROPERTY = "template";
|
||||
|
||||
@Override
|
||||
public void unify(UnificationHelper helper, RecipeJson recipe) {
|
||||
GenericRecipeUnifier.INSTANCE.unify(helper, recipe);
|
||||
helper.unifyInputs(recipe, ADDITION_PROPERTY, BASE_PROPERTY, TEMPLATE_PROPERTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this {@link RecipeUnifier} can be applied for the given {@link RecipeData}.
|
||||
* <p>
|
||||
* This method checks for the vanilla smithing recipe type. If it's a custom recipe type, it tries to find common
|
||||
* keys for the smithing recipe.
|
||||
*
|
||||
* @param recipe the recipe to check
|
||||
* @return true if the {@link RecipeUnifier} can be applied, false otherwise
|
||||
*/
|
||||
public static boolean isApplicable(RecipeData recipe) {
|
||||
return recipe.getType().equals(TRANSFORM_TYPE) ||
|
||||
recipe.getType().equals(TRIM_TYPE) ||
|
||||
hasSmithingLikeStructure(recipe);
|
||||
}
|
||||
|
||||
@SuppressWarnings("FoldExpressionIntoStream")
|
||||
private static boolean hasSmithingLikeStructure(RecipeData recipe) {
|
||||
return recipe.hasProperty(ADDITION_PROPERTY) &&
|
||||
recipe.hasProperty(BASE_PROPERTY) &&
|
||||
recipe.hasProperty(RecipeConstants.RESULT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.api.unification.bundled;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.api.unification;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -0,0 +1,33 @@
|
|||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Basic information about a recipe used for determination of the correct {@link RecipeUnifier}.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface RecipeData {
|
||||
|
||||
/**
|
||||
* Returns the recipe id as a {@link ResourceLocation}.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
ResourceLocation getId();
|
||||
|
||||
/**
|
||||
* Returns the recipe type as a {@link ResourceLocation}.
|
||||
*
|
||||
* @return the recipe type
|
||||
*/
|
||||
ResourceLocation getType();
|
||||
|
||||
/**
|
||||
* Checks if the current recipe contains the property with the given key.
|
||||
*
|
||||
* @param key the key of the property to check for
|
||||
* @return true if the recipe contains the property, false otherwise
|
||||
*/
|
||||
boolean hasProperty(String key);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstraction of a recipe JSON to access and override properties.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface RecipeJson extends RecipeData {
|
||||
|
||||
/**
|
||||
* Returns the value of the property with the given key.
|
||||
*
|
||||
* @param key the key to retrieve the property for
|
||||
* @return the property value or null if not present
|
||||
*/
|
||||
@Nullable
|
||||
JsonElement getProperty(String key);
|
||||
|
||||
/**
|
||||
* Sets the property with the given key to the given value.
|
||||
*
|
||||
* @param key the key to set the property for
|
||||
* @param value the value to set
|
||||
*/
|
||||
void setProperty(String key, JsonElement value);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import com.almostreliable.unified.api.plugin.AlmostUnifiedPlugin;
|
||||
import com.almostreliable.unified.api.unification.bundled.GenericRecipeUnifier;
|
||||
|
||||
/**
|
||||
* Implemented on custom recipe unifiers.
|
||||
* <p>
|
||||
* Custom unifiers will tell Almost Unified how to handle specific recipes.<br>
|
||||
* It can provide information about custom recipe keys not covered by the default unifiers and how to
|
||||
* treat them. Whether they support ingredient replacements or just items.<br>
|
||||
* Recipes will be marked as modified automatically through comparison with the original JSON.
|
||||
* <p>
|
||||
* Unifiers can either be registered per recipe type or per mod id. Registering a custom unifier will
|
||||
* disable the default unifiers such as the {@link GenericRecipeUnifier}.
|
||||
* <p>
|
||||
* Registration is handled in {@link RecipeUnifierRegistry} which can be obtained in
|
||||
* {@link AlmostUnifiedPlugin#registerRecipeUnifiers(RecipeUnifierRegistry)}.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface RecipeUnifier {
|
||||
|
||||
/**
|
||||
* Uses of the given {@link UnificationHelper} to unify the given {@link RecipeJson}.
|
||||
* <p>
|
||||
* {@link RecipeJson} is a utility wrapper that allows to easily access recipe information such as the recipe id,
|
||||
* the recipe type and provides methods to check or modify the raw JSON.
|
||||
*
|
||||
* @param helper the helper to aid in the unification
|
||||
* @param recipe the recipe to unify as a {@link RecipeJson}
|
||||
*/
|
||||
void unify(UnificationHelper helper, RecipeJson recipe);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import com.almostreliable.unified.api.unification.bundled.GenericRecipeUnifier;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* The registry holding all {@link RecipeUnifier}s.
|
||||
* <p>
|
||||
* {@link RecipeUnifier}s can be registered per recipe type or per mod id.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface RecipeUnifierRegistry {
|
||||
|
||||
/**
|
||||
* Registers a {@link RecipeUnifier} for a specific recipe type.
|
||||
* <p>
|
||||
* Recipe-type-based recipe unifiers override mod-id-based recipe unifiers.<br>
|
||||
* Registering a custom recipe unifier will always disable the bundled recipe unifiers
|
||||
* like the {@link GenericRecipeUnifier}.
|
||||
*
|
||||
* @param recipeType the recipe type to register the recipe unifier for
|
||||
* @param recipeUnifier the recipe unifier
|
||||
*/
|
||||
void registerForRecipeType(ResourceLocation recipeType, RecipeUnifier recipeUnifier);
|
||||
|
||||
/**
|
||||
* Registers a {@link RecipeUnifier} for a specific mod id.
|
||||
* <p>
|
||||
* Mod-id-based recipe unifiers will only apply if no recipe-type-based recipe unifiers
|
||||
* are registered for the respective recipe.<br>
|
||||
* Registering a custom recipe unifier will always disable the bundled recipe unifiers
|
||||
* like the {@link GenericRecipeUnifier}.
|
||||
*
|
||||
* @param modId the mod id to register the recipe unifier for
|
||||
* @param recipeUnifier the recipe unifier
|
||||
*/
|
||||
void registerForModId(String modId, RecipeUnifier recipeUnifier);
|
||||
|
||||
/**
|
||||
* Retrieves the respective {@link RecipeUnifier} for the given {@link RecipeData}.
|
||||
*
|
||||
* @param recipeData the recipe data
|
||||
* @return the recipe unifier for the given recipe data
|
||||
*/
|
||||
RecipeUnifier getRecipeUnifier(RecipeData recipeData);
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import com.almostreliable.unified.api.constant.RecipeConstants;
|
||||
import com.almostreliable.unified.api.unification.TagSubstitutions;
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.api.unification.bundled.GenericRecipeUnifier;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Helper interface to aid in the unification of recipes.
|
||||
* <p>
|
||||
* This interface provides methods to unify elements within recipes. Unification involves converting elements to tags
|
||||
* or target items<br>
|
||||
* An instance of this interface is passed to {@link RecipeUnifier#unify(UnificationHelper, RecipeJson)} to assist in
|
||||
* the unification process of the given {@link RecipeJson}.
|
||||
* <p>
|
||||
* Implementations of this interface are expected to provide the logic for unification, typically based on predefined
|
||||
* lookup tables or constants. The methods provided by this interface handle various JSON structures, including
|
||||
* {@link JsonObject}s, {@link JsonArray}s, and individual {@link JsonElement}s.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface UnificationHelper {
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link UnificationLookup} this helper is based on.
|
||||
*
|
||||
* @return the {@link UnificationLookup} this helper is based on
|
||||
*/
|
||||
UnificationLookup getUnificationLookup();
|
||||
|
||||
/**
|
||||
* Fetches all entries of the given {@link RecipeJson} under the specified keys and unifies them as inputs.<br>
|
||||
* Entries treated as inputs will be converted to tags if possible.
|
||||
* <p>
|
||||
* The keys refer to top-level entries in the {@link RecipeJson}. This method requires at least one key to be
|
||||
* provided.<br>
|
||||
* To use default keys, refer to {@link RecipeConstants#DEFAULT_INPUT_KEYS} or see {@link GenericRecipeUnifier}.
|
||||
*
|
||||
* @param recipe the {@link RecipeJson} to fetch the input entries from
|
||||
* @param keys the keys of the input entries to unify
|
||||
* @return true if any element was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputs(RecipeJson recipe, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonElement} as an input.<br>
|
||||
* Elements treated as inputs will be converted to tags if possible.
|
||||
* <p>
|
||||
* This method can unify {@link JsonObject}s and {@link JsonArray}s.<br>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_INPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonElement the {@link JsonElement} to unify
|
||||
* @param keys the keys to use
|
||||
* @return true if the {@link JsonElement} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputElement(@Nullable JsonElement jsonElement, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonArray} as an input.<br>
|
||||
* Elements treated as inputs will be converted to tags if possible.
|
||||
* <p>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_INPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonArray the {@link JsonArray} to unify
|
||||
* @param keys the keys to use
|
||||
* @return true if any element of the {@link JsonArray} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputArray(JsonArray jsonArray, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as an input.<br>
|
||||
* Elements treated as inputs will be converted to tags if possible.
|
||||
* <p>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_INPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @param keys the keys to use
|
||||
* @return true if any element of the {@link JsonObject} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputObject(JsonObject jsonObject, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as a tag input.<br>
|
||||
* Tag inputs are only changed if they have an associated {@link TagSubstitutions} entry.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @return true if the tag input was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputTag(JsonObject jsonObject);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as an item input.<br>
|
||||
* The item will be converted to a tag if possible.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @return true if the item input was changed, false otherwise
|
||||
*/
|
||||
boolean unifyInputItem(JsonObject jsonObject);
|
||||
|
||||
/**
|
||||
* Fetches all entries of the given {@link RecipeJson} under the specified keys and unifies them as
|
||||
* outputs.<br>
|
||||
* Entries treated as outputs will be converted to target items. If the entry is a tag, it will be converted
|
||||
* to the target item of the tag.
|
||||
* <p>
|
||||
* The keys refer to top-level entries in the {@link RecipeJson}. This method requires at least one key to be
|
||||
* provided.<br>
|
||||
* To use default keys, refer to {@link RecipeConstants#DEFAULT_OUTPUT_KEYS} or see {@link GenericRecipeUnifier}.
|
||||
*
|
||||
* @param recipe the {@link RecipeJson} to fetch the output entries from
|
||||
* @param keys the keys of the output entries to unify
|
||||
* @return true if any element was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputs(RecipeJson recipe, String... keys);
|
||||
|
||||
/**
|
||||
* Fetches all entries of the given {@link RecipeJson} under the specified keys and unifies them as
|
||||
* outputs.<br>
|
||||
* Entries treated as outputs will be converted to target items. If the entry is a tag and tagsToItems is true,
|
||||
* it will be converted to the target item of the tag.
|
||||
* <p>
|
||||
* The keys refer to top-level entries in the {@link RecipeJson}. This method requires at least one key to be
|
||||
* provided.<br>
|
||||
* To use default keys, refer to {@link RecipeConstants#DEFAULT_OUTPUT_KEYS} or see {@link GenericRecipeUnifier}.
|
||||
*
|
||||
* @param recipe the {@link RecipeJson} to fetch the output entries from
|
||||
* @param tagsToItems if true, tags will be converted to target items
|
||||
* @param keys the keys of the output entries to unify
|
||||
* @return true if any element was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputs(RecipeJson recipe, boolean tagsToItems, String... keys);
|
||||
|
||||
/**
|
||||
* Fetches the entry of the given {@link RecipeJson} under the specified key and unifies it as output by using the
|
||||
* given inner keys.<br>
|
||||
* Entries treated as outputs will be converted to target items. If the entry is a tag and tagsToItems is true,
|
||||
* it will be converted to the target item of the tag.
|
||||
* <p>
|
||||
* The key refers to a top-level entry in the {@link RecipeJson} while the inner keys refer to nested entries within
|
||||
* the resulting {@link JsonElement}.
|
||||
*
|
||||
* @param recipe the {@link RecipeJson} to fetch the output entry from
|
||||
* @param key the key of the output entry to unify
|
||||
* @param tagsToItems if true, tags will be converted to target items
|
||||
* @param innerKeys the inner keys of the output entry to unify
|
||||
* @return true if any element was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputs(RecipeJson recipe, String key, boolean tagsToItems, String... innerKeys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonElement} as an output.<br>
|
||||
* Elements treated as outputs will be converted to target items. If the element is a tag and tagsToItems is true,
|
||||
* it will be converted to the target item of the tag.
|
||||
* <p>
|
||||
* This method can unify {@link JsonObject}s and {@link JsonArray}s.<br>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_OUTPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonElement the {@link JsonElement} to unify
|
||||
* @param tagsToItems if true, tags will be converted to target items
|
||||
* @param keys the keys to use
|
||||
* @return true if the {@link JsonElement} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputElement(@Nullable JsonElement jsonElement, boolean tagsToItems, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonArray} as an output.<br>
|
||||
* Elements treated as outputs will be converted to target items. If the element is a tag and tagsToItems is true,
|
||||
* it will be converted to the target item of the tag.
|
||||
* <p>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_OUTPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonArray the {@link JsonArray} to unify
|
||||
* @param tagsToItems if true, tags will be converted to target items
|
||||
* @param keys the keys to use
|
||||
* @return true if the {@link JsonArray} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputArray(JsonArray jsonArray, boolean tagsToItems, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as an output.<br>
|
||||
* Elements treated as outputs will be converted to target items. If the element is a tag and tagsToItems is true,
|
||||
* it will be converted to the target item of the tag.
|
||||
* <p>
|
||||
* The keys will be used for each nested element. If no keys are provided, it falls back to
|
||||
* {@link RecipeConstants#DEFAULT_OUTPUT_INNER_KEYS}.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @param tagsToItems if true, tags will be converted to target items
|
||||
* @param keys the keys to use
|
||||
* @return true if the {@link JsonObject} was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputObject(JsonObject jsonObject, boolean tagsToItems, String... keys);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as a tag output.<br>
|
||||
* If tagsToItems is true, it will be converted to the target item of the tag. If tagsToItems is false, it
|
||||
* will only be changed if the tag has an associated {@link TagSubstitutions} entry.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @param tagsToItems if true, the tag will be converted to the target item
|
||||
* @return true if the tag was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputTag(JsonObject jsonObject, boolean tagsToItems);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as an item output.<br>
|
||||
* The item will be converted to the target item of the tag if possible.
|
||||
* <p>
|
||||
* This uses the default keys {@link RecipeConstants#ITEM} and {@link RecipeConstants#ID}.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @return true if the item output was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputItem(JsonObject jsonObject);
|
||||
|
||||
/**
|
||||
* Unifies a {@link JsonObject} as an item output.<br>
|
||||
* The item will be converted to the target item of the tag if possible.
|
||||
*
|
||||
* @param jsonObject the {@link JsonObject} to unify
|
||||
* @param key the key of the output entry to unify
|
||||
* @return true if the item output was changed, false otherwise
|
||||
*/
|
||||
boolean unifyOutputItem(JsonObject jsonObject, String key);
|
||||
|
||||
/**
|
||||
* Handles the output item replacement.
|
||||
* <p>
|
||||
* It needs to be ensured that the passed {@link JsonPrimitive} is an item.
|
||||
*
|
||||
* @param jsonPrimitive the {@link JsonPrimitive} to handle
|
||||
* @return the replacement {@link JsonPrimitive} or null if no replacement was found
|
||||
*/
|
||||
@Nullable
|
||||
JsonPrimitive handleOutputItemReplacement(JsonPrimitive jsonPrimitive);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.api.unification.recipe;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -1,14 +0,0 @@
|
|||
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 AdAstraRecipeUnifier implements RecipeUnifier {
|
||||
@Override
|
||||
public void collectUnifier(RecipeUnifierBuilder builder) {
|
||||
builder.put("output", (json, ctx) -> {
|
||||
return ctx.createResultReplacement(json, false, RecipeConstants.ITEM, "id");
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.config.UnifyConfig;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AlmostKube {
|
||||
|
||||
private AlmostKube() {}
|
||||
|
||||
@Nullable
|
||||
public static String getPreferredTagForItem(ItemStack stack) {
|
||||
UnifyTag<Item> tag = AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.orElseThrow(AlmostKube::notLoadedException)
|
||||
.getPreferredTagForItem(getId(stack));
|
||||
return tag == null ? null : tag.location().toString();
|
||||
}
|
||||
|
||||
public static ItemStack getReplacementForItem(ItemStack stack) {
|
||||
ResourceLocation replacement = AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.orElseThrow(AlmostKube::notLoadedException)
|
||||
.getReplacementForItem(getId(stack));
|
||||
return BuiltInRegistries.ITEM.get(replacement).getDefaultInstance();
|
||||
}
|
||||
|
||||
public static ItemStack getPreferredItemForTag(ResourceLocation tag) {
|
||||
UnifyTag<Item> asUnifyTag = UnifyTag.item(tag);
|
||||
ResourceLocation item = AlmostUnified
|
||||
.getRuntime()
|
||||
.getReplacementMap()
|
||||
.orElseThrow(AlmostKube::notLoadedException)
|
||||
.getPreferredItemForTag(asUnifyTag, $ -> true);
|
||||
return BuiltInRegistries.ITEM.get(item).getDefaultInstance();
|
||||
}
|
||||
|
||||
public static Set<String> getTags() {
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getFilteredTagMap()
|
||||
.orElseThrow(AlmostKube::notLoadedException)
|
||||
.getTags()
|
||||
.stream()
|
||||
.map(tag -> tag.location().toString())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Set<String> getItemIds(ResourceLocation tag) {
|
||||
UnifyTag<Item> asUnifyTag = UnifyTag.item(tag);
|
||||
return AlmostUnified
|
||||
.getRuntime()
|
||||
.getFilteredTagMap()
|
||||
.orElseThrow(AlmostKube::notLoadedException)
|
||||
.getEntriesByTag(asUnifyTag)
|
||||
.stream()
|
||||
.map(ResourceLocation::toString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static UnifyConfig getUnifyConfig() {
|
||||
return AlmostUnified.getRuntime().getUnifyConfig().orElseThrow(AlmostKube::notLoadedException);
|
||||
}
|
||||
|
||||
private static ResourceLocation getId(ItemStack stack) {
|
||||
return BuiltInRegistries.ITEM
|
||||
.getResourceKey(stack.getItem())
|
||||
.map(ResourceKey::location)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Item not found in registry"));
|
||||
}
|
||||
|
||||
private static IllegalStateException notLoadedException() {
|
||||
return new IllegalStateException(
|
||||
"AlmostUnifiedRuntime is unavailable in KubeJS! Possible reasons: calling runtime too early, not in a server environment"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeConstants;
|
||||
import com.almostreliable.unified.api.recipe.RecipeContext;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class GregTechModernRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
private static final String CONTENT = "content";
|
||||
|
||||
@Override
|
||||
public void collectUnifier(RecipeUnifierBuilder builder) {
|
||||
List.of(
|
||||
RecipeConstants.INPUTS,
|
||||
RecipeConstants.TICK_INPUTS
|
||||
).forEach(key ->
|
||||
builder.put(key, (json, ctx) -> createContentReplacement(json, ctx, ctx::createIngredientReplacement))
|
||||
);
|
||||
|
||||
List.of(
|
||||
RecipeConstants.OUTPUTS,
|
||||
RecipeConstants.TICK_OUTPUTS
|
||||
).forEach(key ->
|
||||
builder.put(
|
||||
key,
|
||||
(json, ctx) -> createContentReplacement(
|
||||
json,
|
||||
ctx,
|
||||
element -> ctx.createResultReplacement(
|
||||
element,
|
||||
true,
|
||||
RecipeConstants.ITEM,
|
||||
RecipeConstants.INGREDIENT
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JsonElement createContentReplacement(@Nullable JsonElement json, RecipeContext ctx, Function<JsonElement, JsonElement> elementTransformer) {
|
||||
if (json instanceof JsonObject jsonObject &&
|
||||
jsonObject.get(RecipeConstants.ITEM) instanceof JsonArray jsonArray) {
|
||||
JsonArray result = new JsonArray();
|
||||
boolean changed = false;
|
||||
|
||||
for (JsonElement element : jsonArray) {
|
||||
if (element instanceof JsonObject elementObject) {
|
||||
JsonElement replacement = elementTransformer.apply(elementObject.get(CONTENT));
|
||||
if (replacement != null) {
|
||||
elementObject.add(CONTENT, replacement);
|
||||
changed = true;
|
||||
}
|
||||
result.add(elementObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
jsonObject.add(RecipeConstants.ITEM, result);
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
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.TagOwnerships;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
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.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class HideHelper {
|
||||
|
||||
private HideHelper() {}
|
||||
|
||||
public static Collection<ItemStack> createHidingList(AlmostUnifiedRuntime runtime) {
|
||||
ReplacementMap repMap = runtime.getReplacementMap().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.getEntriesByTag(unifyTag);
|
||||
|
||||
// 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) {
|
||||
replacements.add(getReplacementForItem(repMap, item));
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
hidingList.addAll(toHide);
|
||||
}
|
||||
|
||||
hidingList.addAll(getRefItems(repMap));
|
||||
|
||||
return hidingList
|
||||
.stream()
|
||||
.flatMap(rl -> BuiltInRegistries.ITEM.getOptional(rl).stream())
|
||||
.map(ItemStack::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the replacement for the given item, or the item itself if no replacement is found.
|
||||
* <p>
|
||||
* Returning the item itself is important for stone strata detection.
|
||||
*
|
||||
* @param repMap The replacement map.
|
||||
* @param item The item to get the replacement for.
|
||||
* @return The replacement for the given item, or the item itself if no replacement is found.
|
||||
*/
|
||||
private static ResourceLocation getReplacementForItem(ReplacementMap repMap, ResourceLocation item) {
|
||||
var replacement = repMap.getReplacementForItem(item);
|
||||
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<>();
|
||||
TagOwnerships ownerships = repMap.getTagOwnerships();
|
||||
|
||||
ownerships.getRefs().forEach(ref -> {
|
||||
var owner = ownerships.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()) return;
|
||||
|
||||
AlmostUnified.LOG.info(
|
||||
"[AutoHiding] Hiding reference tag '#{}' of owner tag '#{}' -> {}",
|
||||
ref.location(),
|
||||
owner.location(),
|
||||
refItems
|
||||
);
|
||||
|
||||
hidingList.addAll(refItems);
|
||||
});
|
||||
|
||||
return hidingList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.api.plugin.AlmostUnifiedPlugin;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifierRegistry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class PluginManager {
|
||||
|
||||
@Nullable private static PluginManager INSTANCE;
|
||||
private final List<AlmostUnifiedPlugin> plugins;
|
||||
|
||||
private PluginManager(List<AlmostUnifiedPlugin> plugins) {
|
||||
this.plugins = plugins;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StaticVariableUsedBeforeInitialization")
|
||||
public static PluginManager instance() {
|
||||
if (INSTANCE == null) {
|
||||
throw new IllegalStateException("PluginManager is not initialized");
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static void init(Collection<AlmostUnifiedPlugin> plugins) {
|
||||
if (INSTANCE != null) {
|
||||
throw new IllegalStateException("PluginManager is already initialized");
|
||||
}
|
||||
|
||||
var sortedPlugins = new ArrayList<>(plugins);
|
||||
sortedPlugins.sort((a, b) -> {
|
||||
if (a.getPluginId().getNamespace().equals(ModConstants.ALMOST_UNIFIED)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b.getPluginId().getNamespace().equals(ModConstants.ALMOST_UNIFIED)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.getPluginId().compareTo(b.getPluginId());
|
||||
});
|
||||
|
||||
String ids = sortedPlugins
|
||||
.stream()
|
||||
.map(AlmostUnifiedPlugin::getPluginId)
|
||||
.map(ResourceLocation::toString)
|
||||
.collect(Collectors.joining(", "));
|
||||
AlmostUnifiedCommon.LOGGER.info("Loaded plugins: {}", ids);
|
||||
|
||||
INSTANCE = new PluginManager(sortedPlugins);
|
||||
}
|
||||
|
||||
public void registerRecipeUnifiers(RecipeUnifierRegistry registry) {
|
||||
forEachPlugin(plugin -> plugin.registerRecipeUnifiers(registry));
|
||||
}
|
||||
|
||||
public void forEachPlugin(Consumer<AlmostUnifiedPlugin> consumer) {
|
||||
var it = plugins.listIterator();
|
||||
while (it.hasNext()) {
|
||||
AlmostUnifiedPlugin plugin = it.next();
|
||||
try {
|
||||
consumer.accept(plugin);
|
||||
} catch (Exception e) {
|
||||
it.remove();
|
||||
AlmostUnifiedCommon.LOGGER.error("Failed to process plugin {}, removing it.", plugin.getPluginId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.almostreliable.unified.compat.kube;
|
||||
|
||||
import com.almostreliable.unified.api.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AlmostKube {
|
||||
|
||||
private AlmostKube() {}
|
||||
|
||||
private static AlmostUnifiedRuntime getRuntime() {
|
||||
return AlmostUnified.INSTANCE.getRuntimeOrThrow();
|
||||
}
|
||||
|
||||
public static Set<String> getTags() {
|
||||
return getRuntime()
|
||||
.getUnificationLookup()
|
||||
.getTags()
|
||||
.stream()
|
||||
.map(tag -> tag.location().toString())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Set<String> getTagEntries(ResourceLocation tag) {
|
||||
var tagKey = TagKey.create(Registries.ITEM, tag);
|
||||
return getRuntime()
|
||||
.getUnificationLookup()
|
||||
.getTagEntries(tagKey)
|
||||
.stream()
|
||||
.map(holder -> holder.id().toString())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getRelevantItemTag(ItemStack stack) {
|
||||
var tag = getRuntime().getUnificationLookup().getRelevantItemTag(getId(stack));
|
||||
return tag == null ? null : tag.location().toString();
|
||||
}
|
||||
|
||||
public static ItemStack getVariantItemTarget(ItemStack stack) {
|
||||
var entry = getRuntime().getUnificationLookup().getVariantItemTarget(getId(stack));
|
||||
if (entry == null) return ItemStack.EMPTY;
|
||||
|
||||
return entry.value().getDefaultInstance();
|
||||
}
|
||||
|
||||
public static ItemStack getTagTargetItem(ResourceLocation tag) {
|
||||
var tagKey = TagKey.create(Registries.ITEM, tag);
|
||||
var entry = getRuntime().getUnificationLookup().getTagTargetItem(tagKey);
|
||||
if (entry == null) return ItemStack.EMPTY;
|
||||
|
||||
return entry.value().getDefaultInstance();
|
||||
}
|
||||
|
||||
private static ResourceLocation getId(ItemStack stack) {
|
||||
return BuiltInRegistries.ITEM
|
||||
.getResourceKey(stack.getItem())
|
||||
.map(ResourceKey::location)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Item not found in registry"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.compat.kube;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -0,0 +1,51 @@
|
|||
package com.almostreliable.unified.compat.unification;
|
||||
|
||||
import com.almostreliable.unified.api.constant.RecipeConstants;
|
||||
import com.almostreliable.unified.api.unification.bundled.GenericRecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeJson;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.unification.recipe.UnificationHelper;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class GregTechModernRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
private static final String TICK_INPUTS = "tickInputs";
|
||||
private static final String TICK_OUTPUTS = "tickOutputs";
|
||||
private static final String CONTENT = "content";
|
||||
|
||||
@Override
|
||||
public void unify(UnificationHelper helper, RecipeJson recipe) {
|
||||
GenericRecipeUnifier.INSTANCE.unify(helper, recipe);
|
||||
|
||||
doUnify(recipe, RecipeConstants.INPUTS, helper::unifyInputElement);
|
||||
doUnify(recipe, TICK_INPUTS, helper::unifyInputElement);
|
||||
|
||||
doUnify(recipe,
|
||||
RecipeConstants.OUTPUTS,
|
||||
json -> helper.unifyOutputObject(json, true, RecipeConstants.ITEM, RecipeConstants.INGREDIENT));
|
||||
doUnify(recipe,
|
||||
TICK_OUTPUTS,
|
||||
json -> helper.unifyOutputObject(json, true, RecipeConstants.ITEM, RecipeConstants.INGREDIENT));
|
||||
}
|
||||
|
||||
private void doUnify(RecipeJson recipe, String key, Consumer<JsonObject> callback) {
|
||||
JsonElement property = recipe.getProperty(key);
|
||||
if (property == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(property.getAsJsonObject().get(RecipeConstants.ITEM) instanceof JsonArray arr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (JsonElement element : arr) {
|
||||
if (element.getAsJsonObject().get(CONTENT) instanceof JsonObject content) {
|
||||
callback.accept(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.compat.unification;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -0,0 +1,57 @@
|
|||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import dev.emi.emi.api.EmiEntrypoint;
|
||||
import dev.emi.emi.api.EmiInitRegistry;
|
||||
import dev.emi.emi.api.EmiPlugin;
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeDecorator;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EmiEntrypoint
|
||||
public class AlmostEMI implements EmiPlugin {
|
||||
|
||||
@Override
|
||||
public void initialize(EmiInitRegistry registry) {
|
||||
if (!BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.EMI_STRICT_TAG).iterator().hasNext()) return;
|
||||
for (Holder<Item> itemHolder : BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.HIDE_TAG)) {
|
||||
registry.disableStack(EmiStack.of(new ItemStack(itemHolder)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(EmiRegistry registry) {
|
||||
registry.addRecipeDecorator(new IndicatorDecorator());
|
||||
|
||||
if (BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.EMI_STRICT_TAG).iterator().hasNext()) return;
|
||||
for (Holder<Item> itemHolder : BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.HIDE_TAG)) {
|
||||
registry.removeEmiStacks(EmiStack.of(new ItemStack(itemHolder)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class IndicatorDecorator implements EmiRecipeDecorator {
|
||||
|
||||
@Override
|
||||
public void decorateRecipe(EmiRecipe recipe, WidgetHolder widgets) {
|
||||
var recipeId = recipe.getId();
|
||||
if (recipeId == null) return;
|
||||
|
||||
var link = CRTLookup.getLink(recipeId);
|
||||
if (link == null) return;
|
||||
|
||||
int pX = recipe.getDisplayWidth() - 5;
|
||||
int pY = recipe.getDisplayHeight() - 3;
|
||||
int size = RecipeIndicator.RENDER_SIZE - 1;
|
||||
|
||||
widgets.addDrawable(0, 0, 0, 0, (guiGraphics, mX, mY, delta) ->
|
||||
RecipeIndicator.renderIndicator(guiGraphics, pX, pY, size));
|
||||
widgets.addTooltipText(RecipeIndicator.constructTooltip(link), pX, pY, size, size);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.AlmostUnifiedFallbackRuntime;
|
||||
import com.almostreliable.unified.api.ModConstants;
|
||||
import com.almostreliable.unified.config.UnifyConfig;
|
||||
import com.almostreliable.unified.recipe.CRTLookup;
|
||||
import com.almostreliable.unified.recipe.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.compat.viewer.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import me.shedaniel.rei.plugincompatibilities.api.REIPluginCompatIgnore;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
|
@ -17,10 +13,14 @@ import mezz.jei.api.recipe.category.extensions.IRecipeCategoryDecorator;
|
|||
import mezz.jei.api.registration.IAdvancedRegistration;
|
||||
import mezz.jei.api.runtime.IJeiRuntime;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@REIPluginCompatIgnore
|
||||
|
@ -34,15 +34,11 @@ public class AlmostJEI implements IModPlugin {
|
|||
|
||||
@Override
|
||||
public void onRuntimeAvailable(IJeiRuntime jei) {
|
||||
AlmostUnifiedFallbackRuntime.getInstance().reload();
|
||||
Collection<ItemStack> items = new ArrayList<>();
|
||||
for (Holder<Item> itemHolder : BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.HIDE_TAG)) {
|
||||
items.add(new ItemStack(itemHolder));
|
||||
}
|
||||
|
||||
Boolean jeiDisabled = AlmostUnified.getRuntime()
|
||||
.getUnifyConfig()
|
||||
.map(UnifyConfig::reiOrJeiDisabled)
|
||||
.orElse(false);
|
||||
if (jeiDisabled) return;
|
||||
|
||||
Collection<ItemStack> items = HideHelper.createHidingList(AlmostUnified.getRuntime());
|
||||
if (!items.isEmpty()) {
|
||||
jei.getIngredientManager().removeIngredientsAtRuntime(VanillaTypes.ITEM_STACK, items);
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.AlmostUnifiedFallbackRuntime;
|
||||
import com.almostreliable.unified.ClientTagUpdateEvent;
|
||||
import com.almostreliable.unified.api.ModConstants;
|
||||
import com.almostreliable.unified.config.UnifyConfig;
|
||||
import com.almostreliable.unified.recipe.CRTLookup;
|
||||
import com.almostreliable.unified.recipe.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.compat.viewer.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.utils.ClientTagUpdateEvent;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import me.shedaniel.math.Rectangle;
|
||||
import me.shedaniel.rei.api.client.entry.filtering.base.BasicFilteringRule;
|
||||
|
@ -23,8 +19,14 @@ import me.shedaniel.rei.api.common.display.Display;
|
|||
import me.shedaniel.rei.api.common.plugins.PluginManager;
|
||||
import me.shedaniel.rei.api.common.registry.ReloadStage;
|
||||
import me.shedaniel.rei.api.common.util.EntryIngredients;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
|
@ -46,16 +48,12 @@ public class AlmostREI implements REIClientPlugin {
|
|||
@Override
|
||||
public void registerBasicEntryFiltering(BasicFilteringRule<?> rule) {
|
||||
filterUpdate = rule.hide(() -> {
|
||||
AlmostUnifiedFallbackRuntime.getInstance().reload();
|
||||
Collection<ItemStack> items = new ArrayList<>();
|
||||
for (Holder<Item> itemHolder : BuiltInRegistries.ITEM.getTagOrEmpty(ItemHider.HIDE_TAG)) {
|
||||
items.add(new ItemStack(itemHolder));
|
||||
}
|
||||
|
||||
var reiDisabled = AlmostUnified
|
||||
.getRuntime()
|
||||
.getUnifyConfig()
|
||||
.map(UnifyConfig::reiOrJeiDisabled)
|
||||
.orElse(false);
|
||||
if (reiDisabled) return List.of();
|
||||
|
||||
return EntryIngredients.ofItemStacks(HideHelper.createHidingList(AlmostUnified.getRuntime()));
|
||||
return EntryIngredients.ofItemStacks(items);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.BuildConfig;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -12,14 +13,15 @@ public final class CRTLookup {
|
|||
|
||||
@Nullable
|
||||
public static ClientRecipeTracker.ClientRecipeLink getLink(ResourceLocation recipeId) {
|
||||
ResourceLocation linkRecipe = new ResourceLocation(BuildConfig.MOD_ID, recipeId.getNamespace());
|
||||
ResourceLocation link = Utils.getRL(recipeId.getNamespace());
|
||||
if (Minecraft.getInstance().level == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Minecraft.getInstance().level
|
||||
.getRecipeManager()
|
||||
.byKey(linkRecipe)
|
||||
.byKey(link)
|
||||
.map(RecipeHolder::value)
|
||||
.filter(ClientRecipeTracker.class::isInstance)
|
||||
.map(ClientRecipeTracker.class::cast)
|
||||
.map(tracker -> tracker.getLink(recipeId))
|
|
@ -1,29 +1,36 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.BuildConfig;
|
||||
import com.almostreliable.unified.unification.recipe.RecipeLink;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeInput;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This recipe is used to track which recipes were unified. It is NOT used for crafting.
|
||||
* Each tracker will hold one namespace with a list of recipes that were unified for it.
|
||||
*/
|
||||
public class ClientRecipeTracker implements Recipe<Container> {
|
||||
public record ClientRecipeTracker(String namespace, Map<ResourceLocation, ClientRecipeLink> recipes)
|
||||
implements Recipe<RecipeInput> {
|
||||
|
||||
public static final ResourceLocation ID = Utils.getRL("client_recipe_tracker");
|
||||
public static final String RECIPES = "recipes";
|
||||
public static final String NAMESPACE = "namespace";
|
||||
|
@ -37,14 +44,6 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
}
|
||||
};
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<ResourceLocation, ClientRecipeLink> recipes = new HashMap<>();
|
||||
private final String namespace;
|
||||
|
||||
protected ClientRecipeTracker(ResourceLocation id, String namespace) {
|
||||
this.id = id;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw string representation.
|
||||
|
@ -63,12 +62,12 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
|
||||
//<editor-fold defaultstate="collapsed" desc="Default recipe stuff. Ignore this. Forget this.">
|
||||
@Override
|
||||
public boolean matches(Container container, Level level) {
|
||||
public boolean matches(RecipeInput recipeInput, Level level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack assemble(Container container, RegistryAccess registryAccess) {
|
||||
public ItemStack assemble(RecipeInput recipeInput, HolderLookup.Provider provider) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
|
@ -78,14 +77,9 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getResultItem(RegistryAccess registryAccess) {
|
||||
public ItemStack getResultItem(HolderLookup.Provider provider) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@Override
|
||||
|
@ -109,10 +103,15 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
|
||||
public record ClientRecipeLink(ResourceLocation id, boolean isUnified, boolean isDuplicate) {}
|
||||
|
||||
|
||||
public List<String> getLinkStrings() {
|
||||
return recipes.values().stream().map(l -> createRaw(l.isUnified, l.isDuplicate, l.id.getPath())).toList();
|
||||
}
|
||||
|
||||
public static class Serializer implements RecipeSerializer<ClientRecipeTracker> {
|
||||
|
||||
/**
|
||||
* Reads a recipe from a json file. Recipe will look like this:
|
||||
* Codec for the recipe tracker. The recipe will look like this:
|
||||
* <pre>
|
||||
* {@code
|
||||
* {
|
||||
|
@ -127,35 +126,29 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param recipeId The id of the recipe for the tracker.
|
||||
* @param json The json object.
|
||||
* @return The recipe tracker.
|
||||
*/
|
||||
@Override
|
||||
public ClientRecipeTracker fromJson(ResourceLocation recipeId, JsonObject json) {
|
||||
String namespace = json.get(NAMESPACE).getAsString();
|
||||
JsonArray recipes = json.get(RECIPES).getAsJsonArray();
|
||||
ClientRecipeTracker tracker = new ClientRecipeTracker(recipeId, namespace);
|
||||
for (JsonElement element : recipes) {
|
||||
ClientRecipeLink clientRecipeLink = parseRaw(namespace, element.getAsString());
|
||||
tracker.add(clientRecipeLink);
|
||||
}
|
||||
return tracker;
|
||||
}
|
||||
public static final MapCodec<ClientRecipeTracker> CODEC = RecordCodecBuilder.mapCodec(instance -> instance
|
||||
.group(
|
||||
Codec.STRING.fieldOf("namespace").forGetter(ClientRecipeTracker::namespace),
|
||||
Codec.list(Codec.STRING).fieldOf("recipes").forGetter(ClientRecipeTracker::getLinkStrings)
|
||||
)
|
||||
.apply(instance, Serializer::of));
|
||||
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ClientRecipeTracker> STREAM_CODEC = new StreamCodec<>() {
|
||||
@Override
|
||||
public ClientRecipeTracker fromNetwork(ResourceLocation recipeId, FriendlyByteBuf buffer) {
|
||||
public ClientRecipeTracker decode(RegistryFriendlyByteBuf buffer) {
|
||||
int size = buffer.readInt();
|
||||
String namespace = buffer.readUtf();
|
||||
|
||||
ClientRecipeTracker recipe = new ClientRecipeTracker(recipeId, namespace);
|
||||
ImmutableMap.Builder<ResourceLocation, ClientRecipeLink> builder = ImmutableMap.builder();
|
||||
for (int i = 0; i < size; i++) {
|
||||
String raw = buffer.readUtf();
|
||||
ClientRecipeLink clientRecipeLink = parseRaw(namespace, raw);
|
||||
recipe.add(clientRecipeLink);
|
||||
builder.put(clientRecipeLink.id(), clientRecipeLink);
|
||||
}
|
||||
return recipe;
|
||||
|
||||
return new ClientRecipeTracker(namespace, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,7 +167,7 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
* @param recipe The recipe to write
|
||||
*/
|
||||
@Override
|
||||
public void toNetwork(FriendlyByteBuf buffer, ClientRecipeTracker recipe) {
|
||||
public void encode(RegistryFriendlyByteBuf buffer, ClientRecipeTracker recipe) {
|
||||
buffer.writeInt(recipe.recipes.size());
|
||||
buffer.writeUtf(recipe.namespace);
|
||||
for (ClientRecipeLink clientRecipeLink : recipe.recipes.values()) {
|
||||
|
@ -184,6 +177,28 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
buffer.writeUtf(raw);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public MapCodec<ClientRecipeTracker> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ClientRecipeTracker> streamCodec() {
|
||||
return STREAM_CODEC;
|
||||
}
|
||||
|
||||
private static ClientRecipeTracker of(String namespace, List<String> recipes) {
|
||||
ImmutableMap.Builder<ResourceLocation, ClientRecipeLink> builder = ImmutableMap.builder();
|
||||
|
||||
for (String recipe : recipes) {
|
||||
ClientRecipeLink link = parseRaw(namespace, recipe);
|
||||
builder.put(link.id(), link);
|
||||
}
|
||||
|
||||
return new ClientRecipeTracker(namespace, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientRecipeLink} from a raw string for the given namespace.
|
||||
|
@ -192,12 +207,16 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
* @param raw The raw string.
|
||||
* @return The client sided recipe link.
|
||||
*/
|
||||
private static ClientRecipeLink parseRaw(String namespace, String raw) {
|
||||
public static ClientRecipeLink parseRaw(String namespace, String raw) {
|
||||
String[] split = raw.split("\\$", 2);
|
||||
int flag = Integer.parseInt(split[0]);
|
||||
boolean isUnified = (flag & UNIFIED_FLAG) != 0;
|
||||
boolean isDuplicate = (flag & DUPLICATE_FLAG) != 0;
|
||||
return new ClientRecipeLink(new ResourceLocation(namespace, split[1]), isUnified, isDuplicate);
|
||||
return new ClientRecipeLink(
|
||||
ResourceLocation.fromNamespaceAndPath(namespace, split[1]),
|
||||
isUnified,
|
||||
isDuplicate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,10 +231,10 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a map with the namespace as key and the json recipe.
|
||||
* These recipes are used later in {@link Serializer#fromJson(ResourceLocation, JsonObject)}
|
||||
* Creates a map with the namespace as key and the JSON recipe.
|
||||
* These recipes are used later in {@link Serializer}
|
||||
*
|
||||
* @return The map with the namespace as key and the json recipe.
|
||||
* @return The map with the namespace as key and the JSON recipe.
|
||||
*/
|
||||
public Map<ResourceLocation, JsonObject> compute() {
|
||||
Map<ResourceLocation, JsonObject> result = new HashMap<>();
|
||||
|
@ -224,7 +243,7 @@ public class ClientRecipeTracker implements Recipe<Container> {
|
|||
json.addProperty("type", ID.toString());
|
||||
json.addProperty(NAMESPACE, namespace);
|
||||
json.add(RECIPES, recipes);
|
||||
result.put(new ResourceLocation(BuildConfig.MOD_ID, namespace), json);
|
||||
result.put(Utils.getRL(namespace), json);
|
||||
});
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.unification.UnificationEntry;
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.api.unification.UnificationSettings;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ItemHider {
|
||||
|
||||
public static final TagKey<Item> HIDE_TAG = TagKey.create(Registries.ITEM, Utils.getRL("hide"));
|
||||
public static final TagKey<Item> EMI_STRICT_TAG = TagKey.create(Registries.ITEM, Utils.getRL("emi_strict"));
|
||||
|
||||
private ItemHider() {}
|
||||
|
||||
public static void applyHideTags(VanillaTagWrapper<Item> tags, Collection<UnificationSettings> handlers, boolean emiHidingStrict) {
|
||||
for (var handler : handlers) {
|
||||
if (handler.shouldHideVariantItems()) {
|
||||
applyHideTags(tags, handler);
|
||||
}
|
||||
}
|
||||
|
||||
if (emiHidingStrict) {
|
||||
tags.add(EMI_STRICT_TAG.location(), BuiltInRegistries.ITEM.wrapAsHolder(Items.DEBUG_STICK));
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyHideTags(VanillaTagWrapper<Item> tags, UnificationSettings handler) {
|
||||
var holdersToHide = createHidingItems(handler);
|
||||
for (Holder<Item> holder : holdersToHide) {
|
||||
tags.add(HIDE_TAG.location(), holder);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<Holder<Item>> createHidingItems(UnificationSettings handler) {
|
||||
Set<Holder<Item>> hidings = new HashSet<>();
|
||||
|
||||
for (TagKey<Item> tag : handler.getTags()) {
|
||||
var entriesByTag = handler.getTagEntries(tag);
|
||||
|
||||
// avoid handling single entries and tags that only contain the same namespace for all items
|
||||
if (Utils.allSameNamespace(entriesByTag)) continue;
|
||||
|
||||
Set<UnificationEntry<Item>> replacements = new HashSet<>();
|
||||
for (var holder : entriesByTag) {
|
||||
replacements.add(getReplacementForItem(handler, holder));
|
||||
}
|
||||
|
||||
Set<Holder<Item>> toHide = new HashSet<>();
|
||||
Set<String> toHideIds = new HashSet<>();
|
||||
for (var entry : entriesByTag) {
|
||||
if (!replacements.contains(entry)) {
|
||||
toHide.add(entry.asHolderOrThrow());
|
||||
toHideIds.add(entry.id().toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (toHide.isEmpty()) continue;
|
||||
|
||||
AlmostUnifiedCommon.LOGGER.info(
|
||||
"[AutoHiding] Hiding {}/{} items for tag '#{}' -> {}",
|
||||
toHide.size(),
|
||||
entriesByTag.size(),
|
||||
tag.location(),
|
||||
toHideIds
|
||||
);
|
||||
|
||||
hidings.addAll(toHide);
|
||||
}
|
||||
|
||||
return hidings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the replacement for the given item, or the item itself if no replacement is found.
|
||||
* <p>
|
||||
* Returning the item itself is important for stone variant detection.
|
||||
*
|
||||
* @param repMap The replacement map.
|
||||
* @param entry The holder to get the replacement for.
|
||||
* @return The replacement for the given item, or the item itself if no replacement is found.
|
||||
*/
|
||||
private static UnificationEntry<Item> getReplacementForItem(UnificationLookup repMap, UnificationEntry<Item> entry) {
|
||||
var replacement = repMap.getVariantItemTarget(entry);
|
||||
if (replacement == null) return entry;
|
||||
return replacement;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.almostreliable.unified.compat;
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import com.almostreliable.unified.recipe.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.compat.viewer.ClientRecipeTracker.ClientRecipeLink;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.compat.viewer;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -1,13 +1,13 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -21,107 +21,128 @@ import java.util.stream.Collectors;
|
|||
|
||||
public class Config {
|
||||
|
||||
private static final String CONFIG_DIR_PROPERTY = ModConstants.ALMOST_UNIFIED + ".configDir";
|
||||
private final String name;
|
||||
|
||||
Config(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||
public static <T extends Config> T load(String name, Serializer<T> serializer) {
|
||||
JsonObject json = safeLoadJson(name);
|
||||
AlmostUnifiedCommon.LOGGER.info("Loading config '{}.json'.", name);
|
||||
|
||||
JsonObject json = JsonUtils.safeReadFromFile(buildPath(createConfigDir(), name), new JsonObject());
|
||||
T config = serializer.deserialize(json);
|
||||
|
||||
if (serializer.isInvalid()) {
|
||||
Path filePath = buildPath(createConfigDir(), name);
|
||||
if (Files.exists(filePath)) {
|
||||
backupConfig(name, filePath);
|
||||
}
|
||||
AlmostUnified.LOG.warn("Creating config: {}", name);
|
||||
save(filePath, config, serializer);
|
||||
save(buildPath(createConfigDir(), config.getName()), config, serializer);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static void backupConfig(String name, Path p) {
|
||||
AlmostUnified.LOG.warn("Config {} is invalid. Backing up and recreating.", name);
|
||||
Path backupPath = p.resolveSibling(p.getFileName() + ".bak");
|
||||
try {
|
||||
Files.deleteIfExists(backupPath);
|
||||
Files.move(p, backupPath);
|
||||
} catch (IOException e) {
|
||||
AlmostUnified.LOG.error("Could not backup config file", e);
|
||||
}
|
||||
static Path createConfigDir() {
|
||||
Path path = AlmostUnifiedPlatform.INSTANCE.getConfigPath();
|
||||
String property = System.getProperty(CONFIG_DIR_PROPERTY);
|
||||
if (property != null) {
|
||||
path = Path.of(property);
|
||||
}
|
||||
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
} catch (IOException e) {
|
||||
AlmostUnifiedCommon.LOGGER.error("Failed to create config directory.", e);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static <T extends Config> void save(Path path, T config, Serializer<T> serializer) {
|
||||
if (Files.exists(path)) {
|
||||
backupConfig(path);
|
||||
} else {
|
||||
AlmostUnifiedCommon.LOGGER.warn("Config '{}.json' not found. Creating default config.", config.getName());
|
||||
}
|
||||
|
||||
public static <T extends Config> void save(Path p, T config, Serializer<T> serializer) {
|
||||
JsonObject json = serializer.serialize(config);
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
String jsonString = gson.toJson(json);
|
||||
try {
|
||||
Files.writeString(p,
|
||||
Files.writeString(
|
||||
path,
|
||||
jsonString,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.WRITE);
|
||||
StandardOpenOption.WRITE
|
||||
);
|
||||
} catch (IOException e) {
|
||||
AlmostUnified.LOG.error(e);
|
||||
AlmostUnifiedCommon.LOGGER.error("Failed to save config '{}'.", config.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonObject safeLoadJson(String file) {
|
||||
Path p = createConfigDir();
|
||||
try (BufferedReader reader = Files.newBufferedReader(buildPath(p, file))) {
|
||||
return new Gson().fromJson(reader, JsonObject.class);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return new JsonObject();
|
||||
}
|
||||
private static void backupConfig(Path path) {
|
||||
AlmostUnifiedCommon.LOGGER.warn("Config '{}' is invalid. Backing up and recreating.", path.getFileName());
|
||||
|
||||
private static Path createConfigDir() {
|
||||
Path p = AlmostUnifiedPlatform.INSTANCE.getConfigPath();
|
||||
Path backupPath = path.resolveSibling(path.getFileName() + ".bak");
|
||||
try {
|
||||
Files.createDirectories(p);
|
||||
Files.deleteIfExists(backupPath);
|
||||
Files.move(path, backupPath);
|
||||
} catch (IOException e) {
|
||||
AlmostUnified.LOG.error("Failed to create config directory", e);
|
||||
AlmostUnifiedCommon.LOGGER.error("Config '{}' could not be backed up.", path.getFileName(), e);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
private static Path buildPath(Path p, String name) {
|
||||
return p.resolve(name + ".json");
|
||||
private static Path buildPath(Path path, String name) {
|
||||
return path.resolve(name + ".json");
|
||||
}
|
||||
|
||||
public abstract static class Serializer<T extends Config> {
|
||||
private boolean valid = true;
|
||||
|
||||
protected void setInvalid() {
|
||||
this.valid = false;
|
||||
private boolean valid;
|
||||
|
||||
T deserialize(JsonObject json) {
|
||||
valid = true;
|
||||
return handleDeserialization(json);
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return !valid;
|
||||
}
|
||||
abstract T handleDeserialization(JsonObject json);
|
||||
|
||||
public <V> V safeGet(Supplier<V> supplier, V defaultValue) {
|
||||
abstract JsonObject serialize(T config);
|
||||
|
||||
<V> V safeGet(Supplier<V> supplier, V defaultValue) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
setInvalid();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Pattern> deserializePatterns(JsonObject json, String configKey, List<String> defaultValue) {
|
||||
return safeGet(() -> JsonUtils
|
||||
void setInvalid() {
|
||||
this.valid = false;
|
||||
}
|
||||
|
||||
boolean isInvalid() {
|
||||
return !valid;
|
||||
}
|
||||
|
||||
Set<Pattern> deserializePatterns(JsonObject json, String configKey, List<String> defaultValue) {
|
||||
return safeGet(
|
||||
() -> JsonUtils
|
||||
.toList(json.getAsJsonArray(configKey))
|
||||
.stream()
|
||||
.map(Pattern::compile)
|
||||
.collect(Collectors.toSet()),
|
||||
new HashSet<>(defaultValue.stream().map(Pattern::compile).toList()));
|
||||
new HashSet<>(defaultValue.stream().map(Pattern::compile).toList())
|
||||
);
|
||||
}
|
||||
|
||||
protected void serializePatterns(JsonObject json, String configKey, Set<Pattern> patterns) {
|
||||
json.add(configKey,
|
||||
JsonUtils.toArray(patterns
|
||||
.stream()
|
||||
.map(Pattern::pattern)
|
||||
.toList()));
|
||||
}
|
||||
|
||||
public abstract T deserialize(JsonObject json);
|
||||
|
||||
public abstract JsonObject serialize(T src);
|
||||
void serializePatterns(JsonObject json, String configKey, Set<Pattern> patterns) {
|
||||
json.add(configKey, JsonUtils.toArray(patterns.stream().map(Pattern::pattern).toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,96 +1,76 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.utils.FileUtils;
|
||||
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;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
public final class DebugConfig extends Config {
|
||||
|
||||
public class DebugConfig extends Config {
|
||||
public static final String NAME = "debug";
|
||||
public static final DebugSerializer SERIALIZER = new DebugSerializer();
|
||||
|
||||
public final boolean dumpTagMap;
|
||||
public final boolean dumpDuplicates;
|
||||
public final boolean dumpUnification;
|
||||
public final boolean dumpOverview;
|
||||
public final boolean dumpRecipes;
|
||||
private final boolean dumpDuplicates;
|
||||
private final boolean dumpOverview;
|
||||
private final boolean dumpRecipes;
|
||||
private final boolean dumpTags;
|
||||
private final boolean dumpUnification;
|
||||
|
||||
public DebugConfig(boolean dumpTagMap, boolean dumpDuplicates, boolean dumpUnification, boolean dumpOverview, boolean dumpRecipes) {
|
||||
this.dumpTagMap = dumpTagMap;
|
||||
private DebugConfig(boolean dumpDuplicates, boolean dumpOverview, boolean dumpRecipes, boolean dumpTags, boolean dumpUnification) {
|
||||
super(NAME);
|
||||
this.dumpDuplicates = dumpDuplicates;
|
||||
this.dumpUnification = dumpUnification;
|
||||
this.dumpOverview = dumpOverview;
|
||||
this.dumpRecipes = dumpRecipes;
|
||||
this.dumpTags = dumpTags;
|
||||
this.dumpUnification = dumpUnification;
|
||||
}
|
||||
|
||||
public void logUnifyTagDump(TagMap<Item> tagMap) {
|
||||
if (!dumpTagMap) {
|
||||
return;
|
||||
public boolean shouldDumpDuplicates() {
|
||||
return dumpDuplicates;
|
||||
}
|
||||
|
||||
FileUtils.write(AlmostUnifiedPlatform.INSTANCE.getLogPath(), "unify_tag_dump.txt", sb -> {
|
||||
sb.append(tagMap
|
||||
.getTags()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(t -> t.location().toString()))
|
||||
.map(t -> StringUtils.rightPad(t.location().toString(), 40) + " => " + tagMap
|
||||
.getEntriesByTag(t)
|
||||
.stream()
|
||||
.map(ResourceLocation::toString)
|
||||
.sorted()
|
||||
.collect(Collectors.joining(", ")) + "\n")
|
||||
.collect(Collectors.joining()));
|
||||
});
|
||||
public boolean shouldDumpOverview() {
|
||||
return dumpOverview;
|
||||
}
|
||||
|
||||
public void logRecipes(Map<ResourceLocation, JsonElement> recipes, String filename) {
|
||||
if (!dumpRecipes) {
|
||||
return;
|
||||
public boolean shouldDumpRecipes() {
|
||||
return dumpRecipes;
|
||||
}
|
||||
|
||||
FileUtils.write(AlmostUnifiedPlatform.INSTANCE.getLogPath(),
|
||||
filename,
|
||||
sb -> recipes.forEach((key, value) -> sb
|
||||
.append(key.toString())
|
||||
.append(" [JSON]:")
|
||||
.append(value.toString())
|
||||
.append("\n")));
|
||||
public boolean shouldDumpTags() {
|
||||
return dumpTags;
|
||||
}
|
||||
|
||||
public static class Serializer extends Config.Serializer<DebugConfig> {
|
||||
public boolean shouldDumpUnification() {
|
||||
return dumpUnification;
|
||||
}
|
||||
|
||||
public static final String DUMP_TAG_MAP = "dumpTagMap";
|
||||
public static final String DUMP_DUPLICATES = "dumpDuplicates";
|
||||
public static final String DUMP_UNIFICATION = "dumpUnification";
|
||||
public static final String DUMP_OVERVIEW = "dumpOverview";
|
||||
public static final String DUMP_RECIPES = "dumpRecipes";
|
||||
public static final class DebugSerializer extends Config.Serializer<DebugConfig> {
|
||||
|
||||
private static final String DUMP_DUPLICATES = "dump_duplicates";
|
||||
private static final String DUMP_OVERVIEW = "dump_overview";
|
||||
private static final String DUMP_RECIPES = "dump_recipes";
|
||||
private static final String DUMP_TAGS = "dump_tags";
|
||||
private static final String DUMP_UNIFICATION = "dump_unification";
|
||||
|
||||
private DebugSerializer() {}
|
||||
|
||||
@Override
|
||||
public DebugConfig deserialize(JsonObject json) {
|
||||
public DebugConfig handleDeserialization(JsonObject json) {
|
||||
return new DebugConfig(
|
||||
safeGet(() -> json.get(DUMP_TAG_MAP).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_DUPLICATES).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_UNIFICATION).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_OVERVIEW).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_RECIPES).getAsBoolean(), false)
|
||||
safeGet(() -> json.get(DUMP_RECIPES).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_TAGS).getAsBoolean(), false),
|
||||
safeGet(() -> json.get(DUMP_UNIFICATION).getAsBoolean(), false)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(DebugConfig src) {
|
||||
public JsonObject serialize(DebugConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty(DUMP_TAG_MAP, src.dumpTagMap);
|
||||
json.addProperty(DUMP_DUPLICATES, src.dumpDuplicates);
|
||||
json.addProperty(DUMP_UNIFICATION, src.dumpUnification);
|
||||
json.addProperty(DUMP_OVERVIEW, src.dumpOverview);
|
||||
json.addProperty(DUMP_RECIPES, src.dumpRecipes);
|
||||
json.addProperty(DUMP_DUPLICATES, config.dumpDuplicates);
|
||||
json.addProperty(DUMP_OVERVIEW, config.dumpOverview);
|
||||
json.addProperty(DUMP_RECIPES, config.dumpRecipes);
|
||||
json.addProperty(DUMP_TAGS, config.dumpTags);
|
||||
json.addProperty(DUMP_UNIFICATION, config.dumpUnification);
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,23 +2,34 @@ package com.almostreliable.unified.config;
|
|||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.utils.JsonCompare;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public final class Defaults {
|
||||
|
||||
public static final List<String> STONE_STRATA = List.of(
|
||||
private Defaults() {}
|
||||
|
||||
public static final List<String> STONE_VARIANTS = List.of(
|
||||
"stone",
|
||||
"nether",
|
||||
"andesite",
|
||||
"deepslate",
|
||||
"granite",
|
||||
"diorite",
|
||||
"andesite"
|
||||
"granite",
|
||||
"nether"
|
||||
);
|
||||
public static final List<String> MATERIALS = List.of(
|
||||
|
||||
public static final Map<String, Collection<String>> PLACEHOLDERS = Util.make(() -> {
|
||||
ImmutableMap.Builder<String, Collection<String>> builder = ImmutableMap.builder();
|
||||
|
||||
builder.put("material", List.of(
|
||||
"aeternium",
|
||||
"aluminum",
|
||||
"amber",
|
||||
|
@ -26,11 +37,8 @@ public final class Defaults {
|
|||
"bitumen",
|
||||
"brass",
|
||||
"bronze",
|
||||
"charcoal",
|
||||
"chrome",
|
||||
"cinnabar",
|
||||
"coal",
|
||||
"coal_coke",
|
||||
"cobalt",
|
||||
"constantan",
|
||||
"copper",
|
||||
|
@ -66,82 +74,43 @@ public final class Defaults {
|
|||
"tungsten",
|
||||
"uranium",
|
||||
"zinc"
|
||||
);
|
||||
));
|
||||
|
||||
private Defaults() {}
|
||||
return builder.build();
|
||||
});
|
||||
|
||||
public static List<String> getModPriorities(AlmostUnifiedPlatform.Platform platform) {
|
||||
return switch (platform) {
|
||||
case FORGE -> List.of(
|
||||
public static final List<String> MOD_PRIORITIES = Stream.of(
|
||||
"minecraft",
|
||||
"kubejs",
|
||||
"crafttweaker",
|
||||
"create",
|
||||
"thermal",
|
||||
"immersiveengineering",
|
||||
"mekanism"
|
||||
);
|
||||
case FABRIC -> List.of(
|
||||
"minecraft",
|
||||
"kubejs",
|
||||
"crafttweaker",
|
||||
"create",
|
||||
"mekanism",
|
||||
"techreborn",
|
||||
"modern_industrialization",
|
||||
"indrev"
|
||||
);
|
||||
};
|
||||
}
|
||||
).filter(AlmostUnifiedPlatform.INSTANCE::isModLoaded).toList();
|
||||
|
||||
public static List<String> getTags(AlmostUnifiedPlatform.Platform platform) {
|
||||
return switch (platform) {
|
||||
case FORGE -> List.of(
|
||||
"forge:nuggets/{material}",
|
||||
"forge:dusts/{material}",
|
||||
"forge:gears/{material}",
|
||||
"forge:gems/{material}",
|
||||
"forge:ingots/{material}",
|
||||
"forge:raw_materials/{material}",
|
||||
"forge:ores/{material}",
|
||||
"forge:plates/{material}",
|
||||
"forge:rods/{material}",
|
||||
"forge:wires/{material}",
|
||||
"forge:storage_blocks/{material}",
|
||||
"forge:storage_blocks/raw_{material}"
|
||||
public static final List<String> TAGS = List.of(
|
||||
"c:dusts/{material}",
|
||||
"c:gears/{material}",
|
||||
"c:gems/{material}",
|
||||
"c:ingots/{material}",
|
||||
"c:nuggets/{material}",
|
||||
"c:ores/{material}",
|
||||
"c:plates/{material}",
|
||||
"c:raw_materials/{material}",
|
||||
"c:rods/{material}",
|
||||
"c:storage_blocks/raw_{material}",
|
||||
"c:storage_blocks/{material}",
|
||||
"c:wires/{material}"
|
||||
);
|
||||
case FABRIC -> List.of(
|
||||
"c:{material}_nuggets",
|
||||
"c:{material}_dusts",
|
||||
"c:{material}_gears",
|
||||
"c:{material}_gems",
|
||||
"c:{material}_ingots",
|
||||
"c:{material}_raw_materials",
|
||||
"c:{material}_ores",
|
||||
"c:{material}_plates",
|
||||
"c:{material}_rods",
|
||||
"c:{material}_blocks",
|
||||
"c:{material}_wires",
|
||||
"c:{material}_storage_blocks",
|
||||
"c:raw_{material}_ores",
|
||||
"c:raw_{material}_blocks",
|
||||
"c:raw_{material}_storage_blocks"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public static List<String> getIgnoredRecipeTypes(AlmostUnifiedPlatform.Platform platform) {
|
||||
return switch (platform) {
|
||||
default -> List.of("cucumber:shaped_tag");
|
||||
};
|
||||
}
|
||||
public static final List<String> IGNORED_RECIPE_TYPES = List.of("cucumber:shaped_tag");
|
||||
|
||||
public static JsonCompare.CompareSettings getDefaultDuplicateRules(AlmostUnifiedPlatform.Platform platform) {
|
||||
JsonCompare.CompareSettings result = new JsonCompare.CompareSettings();
|
||||
result.ignoreField(switch (platform) {
|
||||
case FORGE -> "conditions";
|
||||
case FABRIC -> "fabric:load_conditions";
|
||||
});
|
||||
result.ignoreField("group");
|
||||
JsonCompare.CompareSettings result = getDefaultCompareSettings(platform);
|
||||
result.addRule("cookingtime", new JsonCompare.HigherRule());
|
||||
result.addRule("energy", new JsonCompare.HigherRule());
|
||||
result.addRule("experience", new JsonCompare.HigherRule());
|
||||
|
@ -149,16 +118,24 @@ public final class Defaults {
|
|||
}
|
||||
|
||||
public static LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> getDefaultDuplicateOverrides(AlmostUnifiedPlatform.Platform platform) {
|
||||
JsonCompare.CompareSettings result = getDefaultCompareSettings(platform);
|
||||
result.ignoreField("pattern");
|
||||
result.ignoreField("key");
|
||||
|
||||
LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> resultMap = new LinkedHashMap<>();
|
||||
resultMap.put(ResourceLocation.withDefaultNamespace("crafting_shaped"), result);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
public static JsonCompare.CompareSettings getDefaultCompareSettings(AlmostUnifiedPlatform.Platform platform) {
|
||||
JsonCompare.CompareSettings result = new JsonCompare.CompareSettings();
|
||||
result.ignoreField(switch (platform) {
|
||||
case FORGE -> "conditions";
|
||||
case NEO_FORGE -> "neoforge:conditions";
|
||||
case FABRIC -> "fabric:load_conditions";
|
||||
});
|
||||
result.ignoreField("group");
|
||||
result.ignoreField("pattern");
|
||||
result.ignoreField("key");
|
||||
LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> resultMap = new LinkedHashMap<>();
|
||||
resultMap.put(new ResourceLocation("minecraft", "crafting_shaped"), result);
|
||||
return resultMap;
|
||||
result.ignoreField("category");
|
||||
result.ignoreField("show_notification");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.recipe.RecipeLink;
|
||||
import com.almostreliable.unified.unification.recipe.RecipeLink;
|
||||
import com.almostreliable.unified.utils.JsonCompare;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -10,21 +10,25 @@ import java.util.*;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DuplicationConfig extends Config {
|
||||
public final class DuplicateConfig extends Config {
|
||||
|
||||
public static final String NAME = "duplicates";
|
||||
public static final DuplicateSerializer SERIALIZER = new DuplicateSerializer();
|
||||
|
||||
private final JsonCompare.CompareSettings defaultRules;
|
||||
private final LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> overrideRules;
|
||||
private final Set<Pattern> ignoreRecipeTypes;
|
||||
private final Set<Pattern> ignoreRecipes;
|
||||
private final Set<Pattern> ignoreRecipeIds;
|
||||
private final boolean strictMode;
|
||||
|
||||
private final Map<ResourceLocation, Boolean> ignoredRecipeTypesCache;
|
||||
|
||||
public DuplicationConfig(JsonCompare.CompareSettings defaultRules, LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> overrideRules, Set<Pattern> ignoreRecipeTypes, Set<Pattern> ignoreRecipes, boolean strictMode) {
|
||||
private DuplicateConfig(JsonCompare.CompareSettings defaultRules, LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> overrideRules, Set<Pattern> ignoreRecipeTypes, Set<Pattern> ignoreRecipeIds, boolean strictMode) {
|
||||
super(NAME);
|
||||
this.defaultRules = defaultRules;
|
||||
this.overrideRules = overrideRules;
|
||||
this.ignoreRecipeTypes = ignoreRecipeTypes;
|
||||
this.ignoreRecipes = ignoreRecipes;
|
||||
this.ignoreRecipeIds = ignoreRecipeIds;
|
||||
this.strictMode = strictMode;
|
||||
this.ignoredRecipeTypesCache = new HashMap<>();
|
||||
}
|
||||
|
@ -34,7 +38,7 @@ public class DuplicationConfig extends Config {
|
|||
return true;
|
||||
}
|
||||
|
||||
for (Pattern ignoreRecipePattern : ignoreRecipes) {
|
||||
for (Pattern ignoreRecipePattern : ignoreRecipeIds) {
|
||||
if (ignoreRecipePattern.matcher(recipe.getId().toString()).matches()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -72,35 +76,53 @@ public class DuplicationConfig extends Config {
|
|||
ignoredRecipeTypesCache.clear();
|
||||
}
|
||||
|
||||
public static class Serializer extends Config.Serializer<DuplicationConfig> {
|
||||
public static final String DEFAULT_DUPLICATE_RULES = "defaultDuplicateRules";
|
||||
public static final String OVERRIDE_DUPLICATE_RULES = "overrideDuplicateRules";
|
||||
public static final String IGNORED_RECIPE_TYPES = "ignoredRecipeTypes";
|
||||
public static final String IGNORED_RECIPES = "ignoredRecipes";
|
||||
public static final String STRICT_MODE = "strictMode";
|
||||
public static final class DuplicateSerializer extends Config.Serializer<DuplicateConfig> {
|
||||
|
||||
private static final String DEFAULT_DUPLICATE_RULES = "default_duplicate_rules";
|
||||
private static final String OVERRIDE_DUPLICATE_RULES = "override_duplicate_rules";
|
||||
private static final String IGNORED_RECIPE_TYPES = "ignored_recipe_types";
|
||||
private static final String IGNORED_RECIPE_IDS = "ignored_recipe_ids";
|
||||
private static final String STRICT_MODE = "strict_mode";
|
||||
|
||||
private DuplicateSerializer() {}
|
||||
|
||||
@Override
|
||||
public DuplicationConfig deserialize(JsonObject json) {
|
||||
public DuplicateConfig handleDeserialization(JsonObject json) {
|
||||
var platform = AlmostUnifiedPlatform.INSTANCE.getPlatform();
|
||||
Set<Pattern> ignoreRecipeTypes = deserializePatterns(json,
|
||||
Set<Pattern> ignoreRecipeTypes = deserializePatterns(
|
||||
json,
|
||||
IGNORED_RECIPE_TYPES,
|
||||
Defaults.getIgnoredRecipeTypes(platform));
|
||||
Set<Pattern> ignoreRecipes = deserializePatterns(json, IGNORED_RECIPES, List.of());
|
||||
Defaults.IGNORED_RECIPE_TYPES
|
||||
);
|
||||
Set<Pattern> ignoreRecipeIds = deserializePatterns(json, IGNORED_RECIPE_IDS, List.of());
|
||||
|
||||
JsonCompare.CompareSettings defaultRules = safeGet(() -> createCompareSet(json.getAsJsonObject(
|
||||
DEFAULT_DUPLICATE_RULES)),
|
||||
Defaults.getDefaultDuplicateRules(platform));
|
||||
LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> overrideRules = safeGet(() -> json
|
||||
LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> overrideRules = safeGet(() -> getOverrideRules(
|
||||
json), Defaults.getDefaultDuplicateOverrides(platform));
|
||||
boolean strictMode = safeGet(() -> json.get(STRICT_MODE).getAsBoolean(), false);
|
||||
|
||||
return new DuplicateConfig(
|
||||
defaultRules,
|
||||
overrideRules,
|
||||
ignoreRecipeTypes,
|
||||
ignoreRecipeIds,
|
||||
strictMode
|
||||
);
|
||||
}
|
||||
|
||||
// Extracted as method because `safeGet` couldn't cast the type... Seems to be an old SDK bug :-)
|
||||
// https://bugs.openjdk.org/browse/JDK-8324860
|
||||
private LinkedHashMap<ResourceLocation, JsonCompare.CompareSettings> getOverrideRules(JsonObject json) {
|
||||
return json
|
||||
.getAsJsonObject(OVERRIDE_DUPLICATE_RULES)
|
||||
.entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(entry -> new ResourceLocation(entry.getKey()),
|
||||
.collect(Collectors.toMap(entry -> ResourceLocation.parse(entry.getKey()),
|
||||
entry -> createCompareSet(entry.getValue().getAsJsonObject()),
|
||||
(a, b) -> b,
|
||||
LinkedHashMap::new)), Defaults.getDefaultDuplicateOverrides(platform));
|
||||
boolean strictMode = safeGet(() -> json.get(STRICT_MODE).getAsBoolean(), false);
|
||||
|
||||
return new DuplicationConfig(defaultRules, overrideRules, ignoreRecipeTypes, ignoreRecipes, strictMode);
|
||||
LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private JsonCompare.CompareSettings createCompareSet(JsonObject rules) {
|
||||
|
@ -110,16 +132,15 @@ public class DuplicationConfig extends Config {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(DuplicationConfig config) {
|
||||
public JsonObject serialize(DuplicateConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
|
||||
serializePatterns(json, IGNORED_RECIPE_TYPES, config.ignoreRecipeTypes);
|
||||
serializePatterns(json, IGNORED_RECIPES, config.ignoreRecipes);
|
||||
serializePatterns(json, IGNORED_RECIPE_IDS, config.ignoreRecipeIds);
|
||||
json.add(DEFAULT_DUPLICATE_RULES, config.defaultRules.serialize());
|
||||
JsonObject overrides = new JsonObject();
|
||||
config.overrideRules.forEach((rl, compareSettings) -> {
|
||||
overrides.add(rl.toString(), compareSettings.serialize());
|
||||
});
|
||||
config.overrideRules.forEach((rl, compareSettings) ->
|
||||
overrides.add(rl.toString(), compareSettings.serialize()));
|
||||
json.add(OVERRIDE_DUPLICATE_RULES, overrides);
|
||||
json.addProperty(STRICT_MODE, false);
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.api.unification.Placeholders;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class PlaceholderConfig extends Config implements Placeholders {
|
||||
|
||||
public static final String NAME = "placeholders";
|
||||
public static final PlaceholderSerializer SERIALIZER = new PlaceholderSerializer();
|
||||
|
||||
private final Map<String, Collection<String>> placeholders;
|
||||
|
||||
private PlaceholderConfig(Map<String, Collection<String>> placeholders) {
|
||||
super(NAME);
|
||||
this.placeholders = placeholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> apply(String str) {
|
||||
AtomicReference<Collection<String>> inflated = new AtomicReference<>(new HashSet<>());
|
||||
inflated.get().add(str);
|
||||
forEach((placeholder, replacements) -> inflated.set(inflate(inflated.get(), placeholder, replacements)));
|
||||
return inflated.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPlaceholders() {
|
||||
return Collections.unmodifiableCollection(placeholders.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getReplacements(String placeholder) {
|
||||
return placeholders.getOrDefault(placeholder, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<String, Collection<String>> consumer) {
|
||||
placeholders.forEach(consumer);
|
||||
}
|
||||
|
||||
private static Collection<String> inflate(Collection<String> values, String placeholder, Collection<String> replacements) {
|
||||
String formattedPlaceholder = "{" + placeholder + "}";
|
||||
Set<String> result = new HashSet<>();
|
||||
|
||||
for (String value : values) {
|
||||
for (String replacement : replacements) {
|
||||
result.add(value.replace(formattedPlaceholder, replacement));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class PlaceholderSerializer extends Config.Serializer<PlaceholderConfig> {
|
||||
|
||||
private PlaceholderSerializer() {}
|
||||
|
||||
@Override
|
||||
public PlaceholderConfig handleDeserialization(JsonObject json) {
|
||||
// noinspection SizeReplaceableByIsEmpty
|
||||
if (json.size() == 0) { // json.isEmpty crashes in prod...
|
||||
setInvalid();
|
||||
return new PlaceholderConfig(Defaults.PLACEHOLDERS);
|
||||
}
|
||||
|
||||
Map<String, Collection<String>> replacements = safeGet(() -> {
|
||||
ImmutableMap.Builder<String, Collection<String>> builder = ImmutableMap.builder();
|
||||
for (var entry : json.entrySet()) {
|
||||
ImmutableSet.Builder<String> placeholders = ImmutableSet.builder();
|
||||
for (JsonElement element : entry.getValue().getAsJsonArray()) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
placeholders.add(element.getAsString().trim());
|
||||
}
|
||||
}
|
||||
|
||||
builder.put(entry.getKey().trim(), placeholders.build());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}, Defaults.PLACEHOLDERS);
|
||||
|
||||
return new PlaceholderConfig(replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(PlaceholderConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
for (var entry : config.placeholders.entrySet()) {
|
||||
json.add(entry.getKey(), JsonUtils.toArray(entry.getValue()));
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
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;
|
||||
private final DuplicationConfig dupConfig;
|
||||
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());
|
||||
return new ServerConfigs(unifyConfig, dupConfig, debugConfig);
|
||||
}
|
||||
|
||||
private ServerConfigs(UnifyConfig unifyConfig, DuplicationConfig dupConfig, DebugConfig debugConfig) {
|
||||
this.unifyConfig = unifyConfig;
|
||||
this.dupConfig = dupConfig;
|
||||
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;
|
||||
}
|
||||
|
||||
public DuplicationConfig getDupConfig() {
|
||||
return dupConfig;
|
||||
}
|
||||
|
||||
public DebugConfig getDebugConfig() {
|
||||
return debugConfig;
|
||||
}
|
||||
}
|
|
@ -1,32 +1,55 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class StartupConfig extends Config {
|
||||
public final class StartupConfig extends Config {
|
||||
|
||||
public static final String NAME = "startup";
|
||||
private final boolean serverOnly;
|
||||
public static final StartupSerializer SERIALIZER = new StartupSerializer();
|
||||
|
||||
public StartupConfig(boolean serverOnly) {
|
||||
private final boolean serverOnly;
|
||||
private final Boolean worldGenUnification;
|
||||
|
||||
private StartupConfig(boolean serverOnly, boolean worldGenUnification) {
|
||||
super(NAME);
|
||||
this.serverOnly = serverOnly;
|
||||
this.worldGenUnification = worldGenUnification;
|
||||
}
|
||||
|
||||
public boolean isServerOnly() {
|
||||
return serverOnly;
|
||||
}
|
||||
|
||||
public static class Serializer extends Config.Serializer<StartupConfig> {
|
||||
public static final String SERVER_ONLY = "serverOnly";
|
||||
public boolean allowWorldGenUnification() {
|
||||
return worldGenUnification;
|
||||
}
|
||||
|
||||
public static final class StartupSerializer extends Config.Serializer<StartupConfig> {
|
||||
|
||||
private static final String SERVER_ONLY = "server_only";
|
||||
private static final String WORLD_GEN_UNIFICATION = "world_gen_unification";
|
||||
|
||||
private StartupSerializer() {}
|
||||
|
||||
@Override
|
||||
public StartupConfig deserialize(JsonObject json) {
|
||||
return new StartupConfig(safeGet(() -> json.get(SERVER_ONLY).getAsBoolean(), false));
|
||||
public StartupConfig handleDeserialization(JsonObject json) {
|
||||
boolean serverOnly = safeGet(() -> json.get(SERVER_ONLY).getAsBoolean(), false);
|
||||
boolean worldGenUnification = switch (AlmostUnifiedPlatform.INSTANCE.getPlatform()) {
|
||||
case NEO_FORGE -> safeGet(() -> json.get(WORLD_GEN_UNIFICATION).getAsBoolean(), false);
|
||||
case FABRIC -> false;
|
||||
};
|
||||
return new StartupConfig(serverOnly, worldGenUnification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(StartupConfig src) {
|
||||
public JsonObject serialize(StartupConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty(SERVER_ONLY, src.serverOnly);
|
||||
json.addProperty(SERVER_ONLY, config.serverOnly);
|
||||
if (AlmostUnifiedPlatform.INSTANCE.getPlatform() == AlmostUnifiedPlatform.Platform.NEO_FORGE) {
|
||||
json.addProperty(WORLD_GEN_UNIFICATION, config.worldGenUnification);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.unification.TagInheritance;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class TagConfig extends Config {
|
||||
|
||||
public static final String NAME = "tags";
|
||||
public static final TagSerializer SERIALIZER = new TagSerializer();
|
||||
|
||||
private final Map<ResourceLocation, Set<ResourceLocation>> customTags;
|
||||
private final Map<ResourceLocation, Set<ResourceLocation>> tagSubstitutions;
|
||||
private final TagInheritance.Mode itemTagInheritanceMode;
|
||||
private final Map<TagKey<Item>, Set<Pattern>> itemTagInheritance;
|
||||
private final TagInheritance.Mode blockTagInheritanceMode;
|
||||
private final Map<TagKey<Block>, Set<Pattern>> blockTagInheritance;
|
||||
private final boolean emiStrictHiding;
|
||||
|
||||
private TagConfig(Map<ResourceLocation, Set<ResourceLocation>> customTags, Map<ResourceLocation, Set<ResourceLocation>> tagSubstitutions, TagInheritance.Mode itemTagInheritanceMode, Map<TagKey<Item>, Set<Pattern>> itemTagInheritance, TagInheritance.Mode blockTagInheritanceMode, Map<TagKey<Block>, Set<Pattern>> blockTagInheritance, boolean emiStrictHiding) {
|
||||
super(NAME);
|
||||
this.customTags = customTags;
|
||||
this.tagSubstitutions = tagSubstitutions;
|
||||
this.itemTagInheritanceMode = itemTagInheritanceMode;
|
||||
this.itemTagInheritance = itemTagInheritance;
|
||||
this.blockTagInheritanceMode = blockTagInheritanceMode;
|
||||
this.blockTagInheritance = blockTagInheritance;
|
||||
this.emiStrictHiding = emiStrictHiding;
|
||||
}
|
||||
|
||||
public TagInheritance getTagInheritance() {
|
||||
return new TagInheritance(itemTagInheritanceMode,
|
||||
itemTagInheritance,
|
||||
blockTagInheritanceMode,
|
||||
blockTagInheritance);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Set<ResourceLocation>> getCustomTags() {
|
||||
return Collections.unmodifiableMap(customTags);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Set<ResourceLocation>> getTagSubstitutions() {
|
||||
return Collections.unmodifiableMap(tagSubstitutions);
|
||||
}
|
||||
|
||||
public boolean isEmiHidingStrict() {
|
||||
return emiStrictHiding;
|
||||
}
|
||||
|
||||
public static final class TagSerializer extends Config.Serializer<TagConfig> {
|
||||
|
||||
private static final String CUSTOM_TAGS = "custom_tags";
|
||||
private static final String TAG_SUBSTITUTIONS = "tag_substitutions";
|
||||
private static final String ITEM_TAG_INHERITANCE_MODE = "item_tag_inheritance_mode";
|
||||
private static final String ITEM_TAG_INHERITANCE = "item_tag_inheritance";
|
||||
private static final String BLOCK_TAG_INHERITANCE_MODE = "block_tag_inheritance_mode";
|
||||
private static final String BLOCK_TAG_INHERITANCE = "block_tag_inheritance";
|
||||
private static final String EMI_STRICT_HIDING = "emi_strict_hiding";
|
||||
|
||||
private TagSerializer() {}
|
||||
|
||||
@Override
|
||||
public TagConfig handleDeserialization(JsonObject json) {
|
||||
Map<ResourceLocation, Set<ResourceLocation>> customTags = safeGet(() -> JsonUtils.deserializeMapSet(json,
|
||||
CUSTOM_TAGS,
|
||||
e -> ResourceLocation.parse(e.getKey()),
|
||||
ResourceLocation::parse), new HashMap<>());
|
||||
|
||||
Map<ResourceLocation, Set<ResourceLocation>> tagSubstitutions = safeGet(() -> JsonUtils.deserializeMapSet(
|
||||
json,
|
||||
TAG_SUBSTITUTIONS,
|
||||
e -> ResourceLocation.parse(e.getKey()),
|
||||
ResourceLocation::parse), new HashMap<>());
|
||||
|
||||
TagInheritance.Mode itemTagInheritanceMode = deserializeTagInheritanceMode(json,
|
||||
ITEM_TAG_INHERITANCE_MODE);
|
||||
Map<TagKey<Item>, Set<Pattern>> itemTagInheritance = deserializePatternsForLocations(Registries.ITEM,
|
||||
json,
|
||||
ITEM_TAG_INHERITANCE);
|
||||
TagInheritance.Mode blockTagInheritanceMode = deserializeTagInheritanceMode(json,
|
||||
BLOCK_TAG_INHERITANCE_MODE);
|
||||
Map<TagKey<Block>, Set<Pattern>> blockTagInheritance = deserializePatternsForLocations(Registries.BLOCK,
|
||||
json,
|
||||
BLOCK_TAG_INHERITANCE);
|
||||
|
||||
boolean emiStrictHiding = AlmostUnifiedPlatform.INSTANCE.isModLoaded(ModConstants.EMI) ?
|
||||
safeGet(() -> json.get(EMI_STRICT_HIDING).getAsBoolean(), true) :
|
||||
false;
|
||||
|
||||
return new TagConfig(
|
||||
customTags,
|
||||
tagSubstitutions,
|
||||
itemTagInheritanceMode,
|
||||
itemTagInheritance,
|
||||
blockTagInheritanceMode,
|
||||
blockTagInheritance,
|
||||
emiStrictHiding
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(TagConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
|
||||
JsonObject customTags = new JsonObject();
|
||||
config.customTags.forEach((parent, child) -> {
|
||||
customTags.add(parent.toString(),
|
||||
JsonUtils.toArray(child.stream().map(ResourceLocation::toString).toList()));
|
||||
});
|
||||
json.add(CUSTOM_TAGS, customTags);
|
||||
|
||||
JsonObject tagSubstitutions = new JsonObject();
|
||||
config.tagSubstitutions.forEach((parent, child) -> {
|
||||
tagSubstitutions.add(parent.toString(),
|
||||
JsonUtils.toArray(child.stream().map(ResourceLocation::toString).toList()));
|
||||
});
|
||||
json.add(TAG_SUBSTITUTIONS, tagSubstitutions);
|
||||
|
||||
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);
|
||||
|
||||
if (AlmostUnifiedPlatform.INSTANCE.isModLoaded(ModConstants.EMI)) {
|
||||
json.addProperty(EMI_STRICT_HIDING, config.emiStrictHiding);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <T> Map<TagKey<T>, Set<Pattern>> unsafeDeserializePatternsForLocations(ResourceKey<Registry<T>> registry, JsonObject rawConfigJson, String baseKey) {
|
||||
return JsonUtils.deserializeMapSet(rawConfigJson,
|
||||
baseKey,
|
||||
e -> TagKey.create(registry, ResourceLocation.parse(e.getKey())),
|
||||
Pattern::compile);
|
||||
}
|
||||
|
||||
private <T> Map<TagKey<T>, Set<Pattern>> deserializePatternsForLocations(ResourceKey<Registry<T>> registry, JsonObject rawConfigJson, String baseKey) {
|
||||
return safeGet(() -> unsafeDeserializePatternsForLocations(registry, rawConfigJson, baseKey),
|
||||
new HashMap<>());
|
||||
}
|
||||
|
||||
|
||||
private TagInheritance.Mode deserializeTagInheritanceMode(JsonObject json, String key) {
|
||||
return safeGet(() -> TagInheritance.Mode.valueOf(json
|
||||
.getAsJsonPrimitive(key)
|
||||
.getAsString()
|
||||
.toUpperCase()), TagInheritance.Mode.ALLOW);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.api.unification.ModPriorities;
|
||||
import com.almostreliable.unified.api.unification.Placeholders;
|
||||
import com.almostreliable.unified.unification.ModPrioritiesImpl;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class UnificationConfig extends Config {
|
||||
|
||||
private static final String SUB_FOLDER = "unification";
|
||||
|
||||
private final List<String> modPriorities;
|
||||
private final Map<TagKey<Item>, String> priorityOverrides;
|
||||
private final List<String> stoneVariants;
|
||||
private final List<String> tags;
|
||||
private final Set<TagKey<Item>> ignoredTags;
|
||||
private final Set<Pattern> ignoredItems;
|
||||
private final Set<Pattern> ignoredRecipeTypes;
|
||||
private final Set<Pattern> ignoredRecipeIds;
|
||||
private final boolean recipeViewerHiding;
|
||||
private final boolean lootUnification;
|
||||
private final Set<Pattern> ignoredLootTables;
|
||||
|
||||
private final Map<ResourceLocation, Boolean> ignoredItemsCache = new HashMap<>();
|
||||
private final Map<ResourceLocation, Boolean> ignoredRecipeTypesCache = new HashMap<>();
|
||||
private final Map<ResourceLocation, Boolean> ignoredRecipeIdsCache = new HashMap<>();
|
||||
private final Map<ResourceLocation, Boolean> ignoredLootTablesCache = new HashMap<>();
|
||||
@Nullable private Set<TagKey<Item>> bakedTags;
|
||||
|
||||
private UnificationConfig(String name, List<String> modPriorities, Map<TagKey<Item>, String> priorityOverrides, List<String> stoneVariants, List<String> tags, Set<TagKey<Item>> ignoredTags, Set<Pattern> ignoredItems, Set<Pattern> ignoredRecipeTypes, Set<Pattern> ignoredRecipeIds, boolean recipeViewerHiding, boolean lootUnification, Set<Pattern> ignoredLootTables) {
|
||||
super(name);
|
||||
this.modPriorities = modPriorities;
|
||||
this.priorityOverrides = priorityOverrides;
|
||||
this.stoneVariants = stoneVariants;
|
||||
this.tags = tags;
|
||||
this.ignoredTags = ignoredTags;
|
||||
this.ignoredItems = ignoredItems;
|
||||
this.ignoredRecipeTypes = ignoredRecipeTypes;
|
||||
this.ignoredRecipeIds = ignoredRecipeIds;
|
||||
this.recipeViewerHiding = recipeViewerHiding;
|
||||
this.lootUnification = lootUnification;
|
||||
this.ignoredLootTables = ignoredLootTables;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||
public static Collection<UnificationConfig> safeLoadConfigs() {
|
||||
try {
|
||||
return loadConfigs();
|
||||
} catch (Exception e) {
|
||||
AlmostUnifiedCommon.LOGGER.error("Could not load unify configs.", e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<UnificationConfig> loadConfigs() throws IOException {
|
||||
Path subFolder = createConfigDir().resolve(SUB_FOLDER);
|
||||
|
||||
var jsons = readJsons(subFolder);
|
||||
if (jsons.isEmpty()) {
|
||||
String name = "materials";
|
||||
UnifySerializer serializer = new UnifySerializer(name);
|
||||
UnificationConfig defaultConfig = serializer.deserialize(new JsonObject());
|
||||
save(subFolder.resolve(name + ".json"), defaultConfig, serializer);
|
||||
return List.of(defaultConfig);
|
||||
}
|
||||
|
||||
Collection<UnificationConfig> configs = new ArrayList<>();
|
||||
for (var entry : jsons.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
JsonObject json = entry.getValue();
|
||||
|
||||
UnifySerializer serializer = new UnifySerializer(name);
|
||||
var config = serializer.deserialize(json);
|
||||
if (serializer.isInvalid()) {
|
||||
save(subFolder.resolve(name + ".json"), config, serializer);
|
||||
}
|
||||
|
||||
configs.add(config);
|
||||
}
|
||||
|
||||
logMissingPriorityMods(configs);
|
||||
return configs;
|
||||
}
|
||||
|
||||
private static Map<String, JsonObject> readJsons(Path subFolder) throws IOException {
|
||||
Files.createDirectories(subFolder);
|
||||
var files = FileUtils.listFiles(subFolder.toFile(), new String[]{ "json" }, false);
|
||||
|
||||
Map<String, JsonObject> jsons = new HashMap<>();
|
||||
for (var file : files) {
|
||||
String fileName = FilenameUtils.getBaseName(file.getName());
|
||||
try {
|
||||
jsons.put(fileName, JsonUtils.readFromFile(file.toPath(), JsonObject.class));
|
||||
} catch (Throwable e) {
|
||||
AlmostUnifiedCommon.LOGGER.error("Unify config '{}.json' could not be loaded.", fileName, e);
|
||||
}
|
||||
}
|
||||
|
||||
return jsons;
|
||||
}
|
||||
|
||||
private static void logMissingPriorityMods(Collection<UnificationConfig> unificationConfigs) {
|
||||
Set<String> mods = unificationConfigs
|
||||
.stream()
|
||||
.map(UnificationConfig::getModPriorities)
|
||||
.flatMap(ModPriorities::stream)
|
||||
.filter(m -> !AlmostUnifiedPlatform.INSTANCE.isModLoaded(m))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (mods.isEmpty()) return;
|
||||
AlmostUnifiedCommon.LOGGER.warn(
|
||||
"The following mods are used in unification settings, but are not loaded: {}",
|
||||
mods
|
||||
);
|
||||
}
|
||||
|
||||
public ModPriorities getModPriorities() {
|
||||
return new ModPrioritiesImpl(modPriorities, priorityOverrides);
|
||||
}
|
||||
|
||||
public List<String> getStoneVariants() {
|
||||
return stoneVariants;
|
||||
}
|
||||
|
||||
public Set<TagKey<Item>> getTags() {
|
||||
if (bakedTags == null) {
|
||||
throw new IllegalStateException("unification tags are not baked yet");
|
||||
}
|
||||
|
||||
return bakedTags;
|
||||
}
|
||||
|
||||
public Set<TagKey<Item>> bakeTags(Predicate<TagKey<Item>> tagValidator, Placeholders placeholders) {
|
||||
if (bakedTags != null) return bakedTags;
|
||||
|
||||
bakedTags = new HashSet<>();
|
||||
for (var tag : tags) {
|
||||
var replacedTags = placeholders.apply(tag);
|
||||
for (var replacedTag : replacedTags) {
|
||||
ResourceLocation parsedTag = ResourceLocation.tryParse(replacedTag);
|
||||
if (parsedTag == null) continue;
|
||||
|
||||
var tagKey = TagKey.create(Registries.ITEM, parsedTag);
|
||||
if (ignoredTags.contains(tagKey)) continue;
|
||||
if (!tagValidator.test(tagKey)) continue;
|
||||
|
||||
bakedTags.add(tagKey);
|
||||
}
|
||||
}
|
||||
|
||||
return bakedTags;
|
||||
}
|
||||
|
||||
public boolean shouldIncludeItem(ResourceLocation item) {
|
||||
return ignoredItemsCache.computeIfAbsent(item, i -> {
|
||||
for (Pattern pattern : ignoredItems) {
|
||||
if (pattern.matcher(i.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shouldIncludeRecipeType(ResourceLocation type) {
|
||||
return ignoredRecipeTypesCache.computeIfAbsent(type, t -> {
|
||||
for (Pattern pattern : ignoredRecipeTypes) {
|
||||
if (pattern.matcher(t.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shouldIncludeRecipeId(ResourceLocation id) {
|
||||
return ignoredRecipeIdsCache.computeIfAbsent(id, i -> {
|
||||
for (Pattern pattern : ignoredRecipeIds) {
|
||||
if (pattern.matcher(i.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shouldHideVariantItems() {
|
||||
return recipeViewerHiding;
|
||||
}
|
||||
|
||||
public boolean shouldUnifyLoot() {
|
||||
return lootUnification;
|
||||
}
|
||||
|
||||
public boolean shouldIncludeLootTable(ResourceLocation table) {
|
||||
return ignoredLootTablesCache.computeIfAbsent(table, t -> {
|
||||
for (Pattern pattern : ignoredRecipeIds) {
|
||||
if (pattern.matcher(t.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void clearCaches() {
|
||||
ignoredItemsCache.clear();
|
||||
ignoredRecipeTypesCache.clear();
|
||||
ignoredRecipeIdsCache.clear();
|
||||
ignoredLootTablesCache.clear();
|
||||
}
|
||||
|
||||
public static final class UnifySerializer extends Config.Serializer<UnificationConfig> {
|
||||
|
||||
private static final String MOD_PRIORITIES = "mod_priorities";
|
||||
private static final String PRIORITY_OVERRIDES = "priority_overrides";
|
||||
private static final String STONE_VARIANTS = "stone_variants";
|
||||
private static final String TAGS = "tags";
|
||||
private static final String IGNORED_TAGS = "ignored_tags";
|
||||
private static final String IGNORED_ITEMS = "ignored_items";
|
||||
private static final String IGNORED_RECIPE_TYPES = "ignored_recipe_types";
|
||||
private static final String IGNORED_RECIPES_IDS = "ignored_recipe_ids";
|
||||
private static final String RECIPE_VIEWER_HIDING = "recipe_viewer_hiding";
|
||||
private static final String LOOT_UNIFICATION = "loot_unification";
|
||||
private static final String IGNORED_LOOT_TABLES = "ignored_loot_tables";
|
||||
|
||||
private final String name;
|
||||
|
||||
private UnifySerializer(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnificationConfig handleDeserialization(JsonObject json) {
|
||||
List<String> modPriorities = safeGet(
|
||||
() -> JsonUtils.toList(json.getAsJsonArray(MOD_PRIORITIES)),
|
||||
Defaults.MOD_PRIORITIES
|
||||
);
|
||||
Map<TagKey<Item>, String> priorityOverrides = safeGet(
|
||||
() -> JsonUtils.deserializeMap(
|
||||
json,
|
||||
PRIORITY_OVERRIDES,
|
||||
e -> TagKey.create(Registries.ITEM, ResourceLocation.parse(e.getKey())),
|
||||
e -> e.getValue().getAsString()
|
||||
),
|
||||
new HashMap<>()
|
||||
);
|
||||
|
||||
List<String> stoneVariants = safeGet(
|
||||
() -> JsonUtils.toList(json.getAsJsonArray(STONE_VARIANTS)),
|
||||
Defaults.STONE_VARIANTS
|
||||
);
|
||||
|
||||
List<String> tags = safeGet(() -> JsonUtils.toList(json.getAsJsonArray(TAGS)), Defaults.TAGS);
|
||||
Set<TagKey<Item>> ignoredTags = safeGet(
|
||||
() -> JsonUtils
|
||||
.toList(json.getAsJsonArray(IGNORED_TAGS))
|
||||
.stream()
|
||||
.map(s -> TagKey.create(Registries.ITEM, ResourceLocation.parse(s)))
|
||||
.collect(Collectors.toSet()),
|
||||
new HashSet<>()
|
||||
);
|
||||
Set<Pattern> ignoredItems = deserializePatterns(json, IGNORED_ITEMS, List.of());
|
||||
Set<Pattern> ignoredRecipeTypes = deserializePatterns(
|
||||
json,
|
||||
IGNORED_RECIPE_TYPES,
|
||||
Defaults.IGNORED_RECIPE_TYPES
|
||||
);
|
||||
Set<Pattern> ignoredRecipeIds = deserializePatterns(json, IGNORED_RECIPES_IDS, List.of());
|
||||
|
||||
boolean recipeViewerHiding = safeGet(
|
||||
() -> json.getAsJsonPrimitive(RECIPE_VIEWER_HIDING).getAsBoolean(),
|
||||
true
|
||||
);
|
||||
|
||||
boolean lootUnification = safeGet(
|
||||
() -> json.getAsJsonPrimitive(LOOT_UNIFICATION).getAsBoolean(),
|
||||
false
|
||||
);
|
||||
Set<Pattern> ignoredLootTables = deserializePatterns(json, IGNORED_LOOT_TABLES, List.of());
|
||||
|
||||
return new UnificationConfig(
|
||||
name,
|
||||
modPriorities,
|
||||
priorityOverrides,
|
||||
stoneVariants,
|
||||
tags,
|
||||
ignoredTags,
|
||||
ignoredItems,
|
||||
ignoredRecipeTypes,
|
||||
ignoredRecipeIds,
|
||||
recipeViewerHiding,
|
||||
lootUnification,
|
||||
ignoredLootTables
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize(UnificationConfig config) {
|
||||
JsonObject json = new JsonObject();
|
||||
|
||||
json.add(MOD_PRIORITIES, JsonUtils.toArray(config.modPriorities));
|
||||
JsonObject priorityOverrides = new JsonObject();
|
||||
config.priorityOverrides.forEach(
|
||||
(tag, mod) -> priorityOverrides.addProperty(tag.location().toString(), mod)
|
||||
);
|
||||
json.add(PRIORITY_OVERRIDES, priorityOverrides);
|
||||
|
||||
json.add(STONE_VARIANTS, JsonUtils.toArray(config.stoneVariants));
|
||||
|
||||
json.add(TAGS, JsonUtils.toArray(config.tags));
|
||||
json.add(
|
||||
IGNORED_TAGS,
|
||||
JsonUtils.toArray(config.ignoredTags
|
||||
.stream()
|
||||
.map(TagKey::location)
|
||||
.map(ResourceLocation::toString)
|
||||
.toList())
|
||||
);
|
||||
serializePatterns(json, IGNORED_ITEMS, config.ignoredItems);
|
||||
serializePatterns(json, IGNORED_RECIPE_TYPES, config.ignoredRecipeTypes);
|
||||
serializePatterns(json, IGNORED_RECIPES_IDS, config.ignoredRecipeIds);
|
||||
|
||||
json.addProperty(RECIPE_VIEWER_HIDING, config.recipeViewerHiding);
|
||||
|
||||
json.addProperty(LOOT_UNIFICATION, config.lootUnification);
|
||||
serializePatterns(json, IGNORED_LOOT_TABLES, config.ignoredLootTables);
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
package com.almostreliable.unified.config;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
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;
|
||||
|
||||
public class UnifyConfig extends Config {
|
||||
public static final String NAME = "unify";
|
||||
|
||||
private final List<String> modPriorities;
|
||||
private final List<String> stoneStrata;
|
||||
private final List<String> unbakedTags;
|
||||
private final List<String> materials;
|
||||
private final Map<ResourceLocation, String> priorityOverrides;
|
||||
private final Map<ResourceLocation, Set<ResourceLocation>> customTags;
|
||||
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;
|
||||
private final Set<Pattern> ignoredRecipes;
|
||||
private final 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>> customTags,
|
||||
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,
|
||||
Set<Pattern> ignoredRecipes,
|
||||
boolean hideJeiRei
|
||||
) {
|
||||
this.modPriorities = modPriorities;
|
||||
this.stoneStrata = stoneStrata;
|
||||
this.unbakedTags = unbakedTags;
|
||||
this.materials = materials;
|
||||
this.priorityOverrides = priorityOverrides;
|
||||
this.customTags = customTags;
|
||||
this.tagOwnerships = tagOwnerships;
|
||||
this.itemTagInheritanceMode = itemTagInheritanceMode;
|
||||
this.itemTagInheritance = itemTagInheritance;
|
||||
this.blockTagInheritanceMode = blockTagInheritanceMode;
|
||||
this.blockTagInheritance = blockTagInheritance;
|
||||
this.ignoredTags = ignoredTags;
|
||||
this.ignoredItems = ignoredItems;
|
||||
this.ignoredRecipeTypes = ignoredRecipeTypes;
|
||||
this.ignoredRecipes = ignoredRecipes;
|
||||
this.hideJeiRei = hideJeiRei;
|
||||
this.ignoredRecipeTypesCache = new HashMap<>();
|
||||
}
|
||||
|
||||
public List<String> getModPriorities() {
|
||||
return Collections.unmodifiableList(modPriorities);
|
||||
}
|
||||
|
||||
public List<String> getStoneStrata() {
|
||||
return Collections.unmodifiableList(stoneStrata);
|
||||
}
|
||||
|
||||
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) {
|
||||
String replace = tag.replace("{material}", material);
|
||||
ResourceLocation asRL = ResourceLocation.tryParse(replace);
|
||||
if (asRL == null) {
|
||||
AlmostUnified.LOG.warn("Could not bake tag <{}> with material <{}>", tag, material);
|
||||
continue;
|
||||
}
|
||||
|
||||
UnifyTag<Item> t = UnifyTag.item(asRL);
|
||||
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;
|
||||
}
|
||||
|
||||
// exposed for KubeJS binding
|
||||
@SuppressWarnings("unused")
|
||||
public List<String> getMaterials() {
|
||||
return Collections.unmodifiableList(materials);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, String> getPriorityOverrides() {
|
||||
return Collections.unmodifiableMap(priorityOverrides);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Set<ResourceLocation>> getCustomTags() {
|
||||
return Collections.unmodifiableMap(customTags);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Set<ResourceLocation>> getTagOwnerships() {
|
||||
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()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean includeRecipe(ResourceLocation recipe) {
|
||||
for (Pattern pattern : ignoredRecipes) {
|
||||
if (pattern.matcher(recipe.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean includeRecipeType(ResourceLocation type) {
|
||||
return ignoredRecipeTypesCache.computeIfAbsent(type, t -> {
|
||||
for (Pattern pattern : ignoredRecipeTypes) {
|
||||
if (pattern.matcher(t.toString()).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean reiOrJeiDisabled() {
|
||||
return !hideJeiRei;
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
ignoredRecipeTypesCache.clear();
|
||||
}
|
||||
|
||||
public static class Serializer extends Config.Serializer<UnifyConfig> {
|
||||
|
||||
public static final String MOD_PRIORITIES = "modPriorities";
|
||||
public static final String STONE_STRATA = "stoneStrata";
|
||||
public static final String TAGS = "tags";
|
||||
public static final String MATERIALS = "materials";
|
||||
public static final String PRIORITY_OVERRIDES = "priorityOverrides";
|
||||
public static final String CUSTOM_TAGS = "customTags";
|
||||
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";
|
||||
public static final String IGNORED_RECIPES = "ignoredRecipes";
|
||||
public static final String HIDE_JEI_REI = "itemsHidingJeiRei";
|
||||
|
||||
@Override
|
||||
public UnifyConfig deserialize(JsonObject json) {
|
||||
var platform = AlmostUnifiedPlatform.INSTANCE.getPlatform();
|
||||
List<String> modPriorities = safeGet(() -> JsonUtils.toList(json.getAsJsonArray(MOD_PRIORITIES)),
|
||||
Defaults.getModPriorities(platform));
|
||||
List<String> stoneStrata = safeGet(() -> JsonUtils.toList(json.getAsJsonArray(STONE_STRATA)),
|
||||
Defaults.STONE_STRATA);
|
||||
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(
|
||||
() -> JsonUtils.deserializeMap(
|
||||
json,
|
||||
PRIORITY_OVERRIDES,
|
||||
e -> new ResourceLocation(e.getKey()),
|
||||
e -> e.getValue().getAsString()
|
||||
),
|
||||
new HashMap<>()
|
||||
);
|
||||
|
||||
Map<ResourceLocation, Set<ResourceLocation>> customTags = safeGet(
|
||||
() -> JsonUtils.deserializeMapSet(
|
||||
json,
|
||||
CUSTOM_TAGS,
|
||||
e -> new ResourceLocation(e.getKey()),
|
||||
ResourceLocation::new
|
||||
),
|
||||
new HashMap<>()
|
||||
);
|
||||
|
||||
Map<ResourceLocation, Set<ResourceLocation>> tagOwnerships = safeGet(
|
||||
() -> JsonUtils.deserializeMapSet(
|
||||
json,
|
||||
TAG_OWNERSHIPS,
|
||||
e -> new ResourceLocation(e.getKey()),
|
||||
ResourceLocation::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()
|
||||
.map(s -> UnifyTag.item(new ResourceLocation(s)))
|
||||
.collect(Collectors.toSet()), new HashSet<>());
|
||||
Set<Pattern> ignoredItems = deserializePatterns(json, IGNORED_ITEMS, List.of());
|
||||
Set<Pattern> ignoredRecipeTypes = deserializePatterns(json, IGNORED_RECIPE_TYPES,
|
||||
Defaults.getIgnoredRecipeTypes(platform));
|
||||
Set<Pattern> ignoredRecipes = deserializePatterns(json, IGNORED_RECIPES, List.of());
|
||||
boolean hideJeiRei = safeGet(() -> json.getAsJsonPrimitive(HIDE_JEI_REI).getAsBoolean(), true);
|
||||
|
||||
return new UnifyConfig(
|
||||
modPriorities,
|
||||
stoneStrata,
|
||||
tags,
|
||||
materials,
|
||||
priorityOverrides,
|
||||
customTags,
|
||||
tagOwnerships,
|
||||
itemTagInheritanceMode,
|
||||
itemTagInheritance,
|
||||
blockTagInheritanceMode,
|
||||
blockTagInheritance,
|
||||
ignoredTags,
|
||||
ignoredItems,
|
||||
ignoredRecipeTypes,
|
||||
ignoredRecipes,
|
||||
hideJeiRei
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
return JsonUtils.deserializeMapSet(
|
||||
rawConfigJson,
|
||||
baseKey,
|
||||
e -> new ResourceLocation(e.getKey()),
|
||||
Pattern::compile
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
json.add(MOD_PRIORITIES, JsonUtils.toArray(config.modPriorities));
|
||||
json.add(STONE_STRATA, JsonUtils.toArray(config.stoneStrata));
|
||||
json.add(TAGS, JsonUtils.toArray(config.unbakedTags));
|
||||
json.add(MATERIALS, JsonUtils.toArray(config.materials));
|
||||
JsonObject priorityOverrides = new JsonObject();
|
||||
config.priorityOverrides.forEach((tag, mod) -> {
|
||||
priorityOverrides.addProperty(tag.toString(), mod);
|
||||
});
|
||||
json.add(PRIORITY_OVERRIDES, priorityOverrides);
|
||||
JsonObject customTags = new JsonObject();
|
||||
config.customTags.forEach((parent, child) -> {
|
||||
customTags.add(parent.toString(),
|
||||
JsonUtils.toArray(child.stream().map(ResourceLocation::toString).toList()));
|
||||
});
|
||||
json.add(CUSTOM_TAGS, customTags);
|
||||
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);
|
||||
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()
|
||||
.map(UnifyTag::location)
|
||||
.map(ResourceLocation::toString)
|
||||
.toList()));
|
||||
serializePatterns(json, IGNORED_ITEMS, config.ignoredItems);
|
||||
serializePatterns(json, IGNORED_RECIPE_TYPES, config.ignoredRecipeTypes);
|
||||
serializePatterns(json, IGNORED_RECIPES, config.ignoredRecipes);
|
||||
json.addProperty(HIDE_JEI_REI, config.hideJeiRei);
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TagInheritanceMode {
|
||||
ALLOW,
|
||||
DENY
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.almostreliable.unified.core;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import com.almostreliable.unified.api.unification.UnificationEntry;
|
||||
import com.google.auto.service.AutoService;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AutoService(AlmostUnified.class)
|
||||
public class AlmostUnifiedImpl implements AlmostUnified {
|
||||
|
||||
@Override
|
||||
public boolean isRuntimeLoaded() {
|
||||
return getRuntime() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AlmostUnifiedRuntime getRuntime() {
|
||||
return AlmostUnifiedCommon.getRuntime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlmostUnifiedRuntime getRuntimeOrThrow() {
|
||||
AlmostUnifiedRuntime runtime = AlmostUnifiedCommon.getRuntime();
|
||||
if (runtime == null) {
|
||||
throw new IllegalStateException("runtime is not loaded");
|
||||
}
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TagKey<Item>> getTags() {
|
||||
if (!isRuntimeLoaded()) return Set.of();
|
||||
return getRuntimeOrThrow().getUnificationLookup().getTags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Item> getTagEntries(TagKey<Item> tag) {
|
||||
if (!isRuntimeLoaded()) return Set.of();
|
||||
return getRuntimeOrThrow()
|
||||
.getUnificationLookup()
|
||||
.getTagEntries(tag)
|
||||
.stream()
|
||||
.map(UnificationEntry::value)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagKey<Item> getRelevantItemTag(ItemLike itemLike) {
|
||||
if (!isRuntimeLoaded()) return null;
|
||||
return getRuntimeOrThrow().getUnificationLookup().getRelevantItemTag(itemLike.asItem());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getVariantItemTarget(ItemLike itemLike) {
|
||||
if (!isRuntimeLoaded()) return null;
|
||||
|
||||
ResourceLocation id = BuiltInRegistries.ITEM.getKey(itemLike.asItem());
|
||||
var replacement = getRuntimeOrThrow().getUnificationLookup().getVariantItemTarget(id);
|
||||
return replacement == null ? null : replacement.value();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Item getTagTargetItem(TagKey<Item> tag) {
|
||||
if (!isRuntimeLoaded()) return null;
|
||||
|
||||
var replacement = getRuntimeOrThrow().getUnificationLookup().getTagTargetItem(tag);
|
||||
return replacement == null ? null : replacement.value();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
package com.almostreliable.unified.core;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import com.almostreliable.unified.api.unification.*;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifierRegistry;
|
||||
import com.almostreliable.unified.compat.PluginManager;
|
||||
import com.almostreliable.unified.compat.viewer.ItemHider;
|
||||
import com.almostreliable.unified.config.Config;
|
||||
import com.almostreliable.unified.config.PlaceholderConfig;
|
||||
import com.almostreliable.unified.config.TagConfig;
|
||||
import com.almostreliable.unified.config.UnificationConfig;
|
||||
import com.almostreliable.unified.unification.TagInheritance;
|
||||
import com.almostreliable.unified.unification.TagSubstitutionsImpl;
|
||||
import com.almostreliable.unified.unification.UnificationSettingsImpl;
|
||||
import com.almostreliable.unified.unification.recipe.RecipeTransformer;
|
||||
import com.almostreliable.unified.unification.recipe.RecipeUnifierRegistryImpl;
|
||||
import com.almostreliable.unified.utils.DebugHandler;
|
||||
import com.almostreliable.unified.utils.FileUtils;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class AlmostUnifiedRuntimeImpl implements AlmostUnifiedRuntime {
|
||||
|
||||
private final Collection<? extends UnificationSettings> unificationSettings;
|
||||
private final RecipeUnifierRegistry recipeUnifierRegistry;
|
||||
private final TagSubstitutions tagSubstitutions;
|
||||
private final Placeholders placeholders;
|
||||
private final UnificationLookup compositeUnificationLookup;
|
||||
|
||||
private AlmostUnifiedRuntimeImpl(Collection<? extends UnificationSettings> unificationSettings, RecipeUnifierRegistry recipeUnifierRegistry, TagSubstitutions tagSubstitutions, Placeholders placeholders) {
|
||||
this.unificationSettings = unificationSettings;
|
||||
this.recipeUnifierRegistry = recipeUnifierRegistry;
|
||||
this.tagSubstitutions = tagSubstitutions;
|
||||
this.placeholders = placeholders;
|
||||
this.compositeUnificationLookup = new CompositeUnificationLookup(unificationSettings);
|
||||
}
|
||||
|
||||
public static AlmostUnifiedRuntimeImpl create(VanillaTagWrapper<Item> itemTags, VanillaTagWrapper<Block> blockTags) {
|
||||
AlmostUnifiedCommon.LOGGER.warn("Reload detected. Reconstructing runtime.");
|
||||
|
||||
FileUtils.createGitIgnore();
|
||||
var tagConfig = Config.load(TagConfig.NAME, TagConfig.SERIALIZER);
|
||||
var placeholderConfig = Config.load(PlaceholderConfig.NAME, PlaceholderConfig.SERIALIZER);
|
||||
var unificationConfigs = UnificationConfig.safeLoadConfigs();
|
||||
|
||||
var unificationTags = bakeAndValidateTags(unificationConfigs, itemTags, placeholderConfig);
|
||||
|
||||
RecipeUnifierRegistry recipeUnifierRegistry = new RecipeUnifierRegistryImpl();
|
||||
PluginManager.instance().registerRecipeUnifiers(recipeUnifierRegistry);
|
||||
// TODO: add plugin support for registering config defaults
|
||||
|
||||
TagReloadHandler.applyCustomTags(tagConfig.getCustomTags(), itemTags);
|
||||
TagSubstitutionsImpl tagSubstitutions = TagSubstitutionsImpl.create(
|
||||
itemTags::has,
|
||||
unificationTags::contains,
|
||||
tagConfig.getTagSubstitutions()
|
||||
);
|
||||
tagSubstitutions.apply(itemTags);
|
||||
|
||||
List<UnificationSettings> unificationSettings = createUnificationLookups(
|
||||
itemTags,
|
||||
blockTags,
|
||||
unificationConfigs,
|
||||
tagSubstitutions,
|
||||
tagConfig.getTagInheritance()
|
||||
);
|
||||
ItemHider.applyHideTags(itemTags, unificationSettings, tagConfig.isEmiHidingStrict());
|
||||
|
||||
return new AlmostUnifiedRuntimeImpl(
|
||||
unificationSettings,
|
||||
recipeUnifierRegistry,
|
||||
tagSubstitutions,
|
||||
placeholderConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake all tags from unify configs and validate them.
|
||||
* Validating contains:
|
||||
* <ul>
|
||||
* <li>Tag must exist in vanilla tags, which means that the tag is in used by either vanilla or any mods.</li>
|
||||
* <li>Tag must not exist in another unify config. If found, the tag will be skipped.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param unificationConfigs The unify configs
|
||||
* @param itemTags The vanilla tags
|
||||
* @param placeholders The replacements
|
||||
* @return The baked tags combined from all unify configs
|
||||
*/
|
||||
private static Set<TagKey<Item>> bakeAndValidateTags(Collection<UnificationConfig> unificationConfigs, VanillaTagWrapper<Item> itemTags, Placeholders placeholders) {
|
||||
Set<TagKey<Item>> result = new HashSet<>();
|
||||
|
||||
Map<TagKey<Item>, String> visitedTags = new HashMap<>();
|
||||
Set<TagKey<Item>> wrongTags = new HashSet<>();
|
||||
|
||||
for (UnificationConfig config : unificationConfigs) {
|
||||
Predicate<TagKey<Item>> validator = tag -> {
|
||||
if (!itemTags.has(tag)) {
|
||||
wrongTags.add(tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visitedTags.containsKey(tag)) {
|
||||
AlmostUnifiedCommon.LOGGER.warn(
|
||||
"Tag '{}' from unify config '{}' was already created in unify config '{}'",
|
||||
config.getName(),
|
||||
tag.location(),
|
||||
visitedTags.get(tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
visitedTags.put(tag, config.getName());
|
||||
return true;
|
||||
};
|
||||
|
||||
Set<TagKey<Item>> tags = config.bakeTags(validator, placeholders);
|
||||
result.addAll(tags);
|
||||
}
|
||||
|
||||
if (!wrongTags.isEmpty()) {
|
||||
AlmostUnifiedCommon.LOGGER.warn("The following tags are invalid or not in use and will be ignored: {}",
|
||||
wrongTags.stream().map(TagKey::location).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all unify handlers for further unification.
|
||||
* <p>
|
||||
* This method also applies tag inheritance. If tag inheritance was applied, all handlers will be rebuilt due to tag inheritance modifications against vanilla tags.
|
||||
*
|
||||
* @param itemTags All existing item tags which are used ingame
|
||||
* @param blockTags All existing block tags which are used ingame
|
||||
* @param unificationConfigs All unify configs
|
||||
* @param tagSubstitutions All tag substitutions
|
||||
* @param tagInheritance Tag inheritance data
|
||||
* @return All unify handlers
|
||||
*/
|
||||
private static List<UnificationSettings> createUnificationLookups(VanillaTagWrapper<Item> itemTags, VanillaTagWrapper<Block> blockTags, Collection<UnificationConfig> unificationConfigs, TagSubstitutionsImpl tagSubstitutions, TagInheritance tagInheritance) {
|
||||
var unificationSettings = UnificationSettingsImpl.create(unificationConfigs,
|
||||
itemTags,
|
||||
blockTags,
|
||||
tagSubstitutions);
|
||||
|
||||
var needsRebuild = tagInheritance.apply(itemTags, blockTags, unificationSettings);
|
||||
if (needsRebuild) {
|
||||
return UnificationSettingsImpl.create(unificationConfigs, itemTags, blockTags, tagSubstitutions);
|
||||
}
|
||||
|
||||
return unificationSettings;
|
||||
}
|
||||
|
||||
public void run(Map<ResourceLocation, JsonElement> recipes) {
|
||||
DebugHandler debugHandler = DebugHandler.onRunStart(recipes, compositeUnificationLookup);
|
||||
|
||||
debugHandler.measure(() -> {
|
||||
var transformer = new RecipeTransformer(recipeUnifierRegistry, unificationSettings);
|
||||
return transformer.transformRecipes(recipes);
|
||||
});
|
||||
|
||||
debugHandler.onRunEnd(recipes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnificationLookup getUnificationLookup() {
|
||||
return compositeUnificationLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends UnificationSettings> getUnificationSettings() {
|
||||
return Collections.unmodifiableCollection(unificationSettings);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UnificationSettings getUnificationSettings(String name) {
|
||||
for (UnificationSettings settings : unificationSettings) {
|
||||
if (settings.getName().equals(name)) {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagSubstitutions getTagSubstitutions() {
|
||||
return tagSubstitutions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholders getPlaceholders() {
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
private static final class CompositeUnificationLookup implements UnificationLookup {
|
||||
|
||||
private final Iterable<? extends UnificationLookup> unificationLookups;
|
||||
|
||||
@Nullable private Collection<TagKey<Item>> unificationTagsView;
|
||||
|
||||
private CompositeUnificationLookup(Iterable<? extends UnificationLookup> unificationLookups) {
|
||||
this.unificationLookups = unificationLookups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TagKey<Item>> getTags() {
|
||||
if (unificationTagsView == null) {
|
||||
Set<TagKey<Item>> tagView = new HashSet<>();
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
tagView.addAll(unificationLookup.getTags());
|
||||
}
|
||||
|
||||
unificationTagsView = Collections.unmodifiableCollection(tagView);
|
||||
}
|
||||
|
||||
return unificationTagsView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<UnificationEntry<Item>> getTagEntries(TagKey<Item> tag) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
var resultItems = unificationLookup.getTagEntries(tag);
|
||||
if (!resultItems.isEmpty()) {
|
||||
return resultItems;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UnificationEntry<Item> getItemEntry(ResourceLocation item) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
var resultItem = unificationLookup.getItemEntry(item);
|
||||
if (resultItem != null) {
|
||||
return resultItem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagKey<Item> getRelevantItemTag(ResourceLocation item) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
TagKey<Item> tag = unificationLookup.getRelevantItemTag(item);
|
||||
if (tag != null) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnificationEntry<Item> getVariantItemTarget(ResourceLocation item) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
var resultItem = unificationLookup.getVariantItemTarget(item);
|
||||
if (resultItem != null) {
|
||||
return resultItem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnificationEntry<Item> getTagTargetItem(TagKey<Item> tag, Predicate<ResourceLocation> itemFilter) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
var result = unificationLookup.getTagTargetItem(tag, itemFilter);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnifiedIngredientItem(Ingredient ingredient, ItemStack item) {
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
if (unificationLookup.isUnifiedIngredientItem(ingredient, item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.almostreliable.unified.core;
|
||||
|
||||
import com.almostreliable.unified.api.constant.ModConstants;
|
||||
import com.almostreliable.unified.api.plugin.AlmostUnifiedNeoPlugin;
|
||||
import com.almostreliable.unified.api.plugin.AlmostUnifiedPlugin;
|
||||
import com.almostreliable.unified.api.unification.recipe.RecipeUnifierRegistry;
|
||||
import com.almostreliable.unified.compat.unification.GregTechModernRecipeUnifier;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
@AlmostUnifiedNeoPlugin
|
||||
public class CommonPlugin implements AlmostUnifiedPlugin {
|
||||
|
||||
@Override
|
||||
public ResourceLocation getPluginId() {
|
||||
return Utils.getRL("common");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRecipeUnifiers(RecipeUnifierRegistry registry) {
|
||||
registry.registerForModId(ModConstants.GREGTECH_MODERN, new GregTechModernRecipeUnifier());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.almostreliable.unified.core;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
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.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings({ "StaticVariableMayNotBeInitialized", "StaticVariableUsedBeforeInitialization" })
|
||||
public final class TagReloadHandler {
|
||||
|
||||
private static final Object LOCK = new Object();
|
||||
@Nullable private static VanillaTagWrapper<Item> VANILLA_ITEM_TAGS;
|
||||
@Nullable private static VanillaTagWrapper<Block> VANILLA_BLOCK_TAGS;
|
||||
|
||||
private TagReloadHandler() {}
|
||||
|
||||
public static void initItemTags(Map<ResourceLocation, Collection<Holder<Item>>> rawItemTags) {
|
||||
synchronized (LOCK) {
|
||||
VANILLA_ITEM_TAGS = VanillaTagWrapper.of(BuiltInRegistries.ITEM, rawItemTags);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initBlockTags(Map<ResourceLocation, Collection<Holder<Block>>> rawBlockTags) {
|
||||
synchronized (LOCK) {
|
||||
VANILLA_BLOCK_TAGS = VanillaTagWrapper.of(BuiltInRegistries.BLOCK, rawBlockTags);
|
||||
}
|
||||
}
|
||||
|
||||
public static void run() {
|
||||
if (VANILLA_ITEM_TAGS == null || VANILLA_BLOCK_TAGS == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlmostUnifiedCommon.onTagLoaderReload(VANILLA_ITEM_TAGS, VANILLA_BLOCK_TAGS);
|
||||
|
||||
VANILLA_ITEM_TAGS.seal();
|
||||
VANILLA_BLOCK_TAGS.seal();
|
||||
VANILLA_ITEM_TAGS = null;
|
||||
VANILLA_BLOCK_TAGS = null;
|
||||
}
|
||||
|
||||
public static void applyCustomTags(Map<ResourceLocation, Set<ResourceLocation>> customTags, VanillaTagWrapper<Item> itemTags) {
|
||||
Multimap<ResourceLocation, ResourceLocation> changedItemTags = HashMultimap.create();
|
||||
|
||||
for (var entry : customTags.entrySet()) {
|
||||
ResourceLocation tag = entry.getKey();
|
||||
Set<ResourceLocation> itemIds = entry.getValue();
|
||||
|
||||
for (ResourceLocation itemId : itemIds) {
|
||||
if (!BuiltInRegistries.ITEM.containsKey(itemId)) {
|
||||
AlmostUnifiedCommon.LOGGER.warn("[CustomTags] Custom tag '{}' contains invalid item '{}'",
|
||||
tag,
|
||||
itemId);
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceKey<Item> itemKey = ResourceKey.create(Registries.ITEM, itemId);
|
||||
Holder<Item> itemHolder = BuiltInRegistries.ITEM.getHolder(itemKey).orElse(null);
|
||||
if (itemHolder == null) continue;
|
||||
|
||||
var currentHolders = itemTags.get(tag);
|
||||
|
||||
if (!currentHolders.isEmpty()) {
|
||||
if (currentHolders.contains(itemHolder)) {
|
||||
AlmostUnifiedCommon.LOGGER.warn("[CustomTags] Custom tag '{}' already contains item '{}'",
|
||||
tag,
|
||||
itemId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
itemTags.add(tag, itemHolder);
|
||||
changedItemTags.put(tag, itemId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changedItemTags.isEmpty()) {
|
||||
changedItemTags.asMap().forEach(
|
||||
(tag, items) -> AlmostUnifiedCommon.LOGGER.info(
|
||||
"[CustomTags] Modified tag '#{}', added {}",
|
||||
tag,
|
||||
items
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.mixin;
|
||||
package com.almostreliable.unified.core;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.almostreliable.unified.mixin.loot;
|
||||
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.unification.loot.LootUnificationHandler;
|
||||
import net.minecraft.world.level.storage.loot.entries.CompositeEntryBase;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(CompositeEntryBase.class)
|
||||
public class CompositeEntryBaseMixin implements LootUnificationHandler {
|
||||
@Shadow @Final protected List<LootPoolEntryContainer> children;
|
||||
|
||||
@Override
|
||||
public boolean almostunified$unify(UnificationLookup lookup) {
|
||||
boolean unified = false;
|
||||
|
||||
for (LootPoolEntryContainer child : children) {
|
||||
if (child instanceof LootUnificationHandler handler) {
|
||||
unified |= handler.almostunified$unify(lookup);
|
||||
}
|
||||
}
|
||||
|
||||
return unified;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.almostreliable.unified.mixin.loot;
|
||||
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.unification.loot.LootUnificationHandler;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootItem;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(LootItem.class)
|
||||
public class LootItemMixin implements LootUnificationHandler {
|
||||
@Shadow @Final @Mutable private Holder<Item> item;
|
||||
|
||||
@Override
|
||||
public boolean almostunified$unify(UnificationLookup lookup) {
|
||||
var replacement = lookup.getVariantItemTarget(item);
|
||||
if (replacement == null || item.value().equals(replacement.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.item = replacement.asHolderOrThrow();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.almostreliable.unified.mixin.loot;
|
||||
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.unification.loot.LootUnificationHandler;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootPool.class)
|
||||
public class LootPoolMixin implements LootUnificationHandler {
|
||||
|
||||
@Shadow @Final private List<LootPoolEntryContainer> entries;
|
||||
|
||||
@Override
|
||||
public boolean almostunified$unify(UnificationLookup lookup) {
|
||||
boolean unified = false;
|
||||
|
||||
for (LootPoolEntryContainer entry : this.entries) {
|
||||
if (entry instanceof LootUnificationHandler handler) {
|
||||
unified |= handler.almostunified$unify(lookup);
|
||||
}
|
||||
}
|
||||
|
||||
return unified;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.almostreliable.unified.mixin.loot;
|
||||
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.unification.loot.LootUnificationHandler;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootTable.class)
|
||||
public class LootTableMixin implements LootUnificationHandler {
|
||||
@Shadow @Final private List<LootPool> pools;
|
||||
|
||||
@Override
|
||||
public boolean almostunified$unify(UnificationLookup lookup) {
|
||||
boolean unified = false;
|
||||
for (LootPool pool : pools) {
|
||||
unified |= LootUnificationHandler.cast(pool).almostunified$unify(lookup);
|
||||
}
|
||||
|
||||
return unified;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.mixin.loot;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -1,8 +1,8 @@
|
|||
package com.almostreliable.unified.mixin.runtime;
|
||||
|
||||
import com.almostreliable.unified.ClientTagUpdateEvent;
|
||||
import com.almostreliable.unified.utils.ClientTagUpdateEvent;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundUpdateTagsPacket;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package com.almostreliable.unified.mixin.runtime;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
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;
|
||||
|
@ -17,12 +20,14 @@ import java.util.Map;
|
|||
@Mixin(value = RecipeManager.class, priority = 1_099)
|
||||
public class RecipeManagerMixin {
|
||||
|
||||
@Shadow @Final private HolderLookup.Provider registries;
|
||||
|
||||
@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.onRecipeManagerReload(recipes);
|
||||
AlmostUnifiedCommon.onRecipeManagerReload(recipes, registries);
|
||||
} catch (Exception e) {
|
||||
AlmostUnified.LOG.error(e.getMessage(), e);
|
||||
AlmostUnifiedCommon.LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.almostreliable.unified.mixin.runtime;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.utils.TagReloadHandler;
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.core.TagReloadHandler;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -26,22 +26,22 @@ public class TagLoaderMixin {
|
|||
|
||||
@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")) {
|
||||
if (directory.equals("tags/item")) {
|
||||
try {
|
||||
Map<ResourceLocation, Collection<Holder<Item>>> tags = Utils.cast(cir.getReturnValue());
|
||||
TagReloadHandler.initItemTags(tags);
|
||||
TagReloadHandler.run();
|
||||
} catch (Exception e) {
|
||||
AlmostUnified.LOG.error(e.getMessage(), e);
|
||||
AlmostUnifiedCommon.LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (directory.equals("tags/blocks")) {
|
||||
if (directory.equals("tags/block")) {
|
||||
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);
|
||||
AlmostUnifiedCommon.LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package com.almostreliable.unified.mixin.unifier;
|
||||
package com.almostreliable.unified.mixin.unification;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.ArmorMaterial;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
@ -14,14 +17,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||
@Mixin(ArmorItem.class)
|
||||
public class ArmorItemMixin {
|
||||
|
||||
@Shadow @Final protected ArmorMaterial material;
|
||||
@Shadow @Final protected Holder<ArmorMaterial> material;
|
||||
|
||||
@Inject(method = "isValidRepairItem", at = @At("HEAD"), cancellable = true)
|
||||
private void unified$repairUnification(ItemStack stack, ItemStack repairCandidate, CallbackInfoReturnable<Boolean> cir) {
|
||||
AlmostUnified.getRuntime().getReplacementMap().ifPresent(replacementMap -> {
|
||||
if (replacementMap.isItemInUnifiedIngredient(material.getRepairIngredient(), repairCandidate)) {
|
||||
AlmostUnifiedRuntime runtime = AlmostUnified.INSTANCE.getRuntime();
|
||||
if (runtime == null) return;
|
||||
|
||||
Ingredient repairIngredient = material.value().repairIngredient().get();
|
||||
if (runtime.getUnificationLookup().isUnifiedIngredientItem(repairIngredient, repairCandidate)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package com.almostreliable.unified.mixin.unifier;
|
||||
package com.almostreliable.unified.mixin.unification;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnified;
|
||||
import com.almostreliable.unified.api.AlmostUnifiedRuntime;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Tier;
|
||||
import net.minecraft.world.item.TieredItem;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
@ -18,10 +20,12 @@ public class TieredItemMixin {
|
|||
|
||||
@Inject(method = "isValidRepairItem", at = @At("HEAD"), cancellable = true)
|
||||
private void unified$repairUnification(ItemStack stack, ItemStack repairCandidate, CallbackInfoReturnable<Boolean> cir) {
|
||||
AlmostUnified.getRuntime().getReplacementMap().ifPresent(replacementMap -> {
|
||||
if (replacementMap.isItemInUnifiedIngredient(tier.getRepairIngredient(), repairCandidate)) {
|
||||
AlmostUnifiedRuntime runtime = AlmostUnified.INSTANCE.getRuntime();
|
||||
if (runtime == null) return;
|
||||
|
||||
Ingredient repairIngredient = tier.getRepairIngredient();
|
||||
if (runtime.getUnificationLookup().isUnifiedIngredientItem(repairIngredient, repairCandidate)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.mixin.unification;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -1,168 +0,0 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeConstants;
|
||||
import com.almostreliable.unified.api.recipe.RecipeContext;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.almostreliable.unified.utils.ReplacementMap;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public class RecipeContextImpl implements RecipeContext {
|
||||
|
||||
private final ReplacementMap replacementMap;
|
||||
private final JsonObject originalRecipe;
|
||||
|
||||
public RecipeContextImpl(JsonObject json, ReplacementMap replacementMap) {
|
||||
this.originalRecipe = json;
|
||||
this.replacementMap = replacementMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getReplacementForItem(@Nullable ResourceLocation item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return replacementMap.getReplacementForItem(item);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getPreferredItemForTag(@Nullable UnifyTag<Item> tag, Predicate<ResourceLocation> filter) {
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return replacementMap.getPreferredItemForTag(tag, filter);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UnifyTag<Item> getPreferredTagForItem(@Nullable ResourceLocation item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return replacementMap.getPreferredTagForItem(item);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JsonElement createIngredientReplacement(@Nullable JsonElement element) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonElement copy = element.deepCopy();
|
||||
tryCreateIngredientReplacement(copy);
|
||||
return element.equals(copy) ? null : copy;
|
||||
}
|
||||
|
||||
private void tryCreateIngredientReplacement(@Nullable JsonElement element) {
|
||||
if (element instanceof JsonArray array) {
|
||||
for (JsonElement e : array) {
|
||||
tryCreateIngredientReplacement(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (element instanceof JsonObject object) {
|
||||
tryCreateIngredientReplacement(object.get(RecipeConstants.VALUE));
|
||||
tryCreateIngredientReplacement(object.get(RecipeConstants.BASE));
|
||||
tryCreateIngredientReplacement(object.get(RecipeConstants.INGREDIENT));
|
||||
|
||||
if (object.get(RecipeConstants.TAG) instanceof JsonPrimitive primitive) {
|
||||
UnifyTag<Item> tag = Utils.toItemTag(primitive.getAsString());
|
||||
var ownerTag = replacementMap.getTagOwnerships().getOwnerByTag(tag);
|
||||
if (ownerTag != null) {
|
||||
object.addProperty(RecipeConstants.TAG, ownerTag.location().toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (object.get(RecipeConstants.ITEM) instanceof JsonPrimitive primitive) {
|
||||
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());
|
||||
UnifyTag<Item> tag = getPreferredTagForItem(item);
|
||||
if (tag != null) {
|
||||
object.remove(RecipeConstants.ITEM);
|
||||
object.addProperty(RecipeConstants.TAG, tag.location().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonElement createResultReplacement(@Nullable JsonElement element) {
|
||||
return createResultReplacement(element, true, RecipeConstants.ITEM);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonElement createResultReplacement(@Nullable JsonElement element, boolean tagLookup, String... lookupKeys) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonElement copy = element.deepCopy();
|
||||
JsonElement result = tryCreateResultReplacement(copy, tagLookup, lookupKeys);
|
||||
return element.equals(result) ? null : result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JsonElement tryCreateResultReplacement(JsonElement element, boolean tagLookup, String... lookupKeys) {
|
||||
if (element instanceof JsonPrimitive primitive) {
|
||||
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());
|
||||
ResourceLocation replacement = getReplacementForItem(item);
|
||||
if (replacement != null) {
|
||||
return new JsonPrimitive(replacement.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (element instanceof JsonArray array &&
|
||||
JsonUtils.replaceOn(array, j -> tryCreateResultReplacement(j, tagLookup, lookupKeys))) {
|
||||
return element;
|
||||
}
|
||||
|
||||
if (element instanceof JsonObject object) {
|
||||
for (String key : lookupKeys) {
|
||||
if (JsonUtils.replaceOn(object, key, j -> tryCreateResultReplacement(j, tagLookup, lookupKeys))) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
// when tags are used as outputs, replace them with the preferred item
|
||||
if (tagLookup && object.get(RecipeConstants.TAG) instanceof JsonPrimitive primitive) {
|
||||
ResourceLocation item = getPreferredItemForTag(Utils.toItemTag(primitive.getAsString()), $ -> true);
|
||||
if (item != null) {
|
||||
object.remove(RecipeConstants.TAG);
|
||||
object.addProperty(RecipeConstants.ITEM, item.toString());
|
||||
}
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getType() {
|
||||
String type = originalRecipe.get("type").getAsString();
|
||||
return new ResourceLocation(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(String property) {
|
||||
return originalRecipe.has(property);
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedPlatform;
|
||||
import com.almostreliable.unified.utils.FileUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class RecipeDumper {
|
||||
private final RecipeTransformer.Result result;
|
||||
private final long startTime;
|
||||
private final long endTime;
|
||||
private final DateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
|
||||
|
||||
public RecipeDumper(RecipeTransformer.Result result, long startTime, long endTime) {
|
||||
this.result = result;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public void dump(boolean dumpOverview, boolean dumpUnify, boolean dumpDuplicate) {
|
||||
String last = "# Last execution: " + format.format(new Date(startTime));
|
||||
|
||||
if (dumpOverview) {
|
||||
FileUtils.write(AlmostUnifiedPlatform.INSTANCE.getLogPath(), "overview_dump.txt", sb -> {
|
||||
sb.append(last).append("\n");
|
||||
dumpOverview(sb);
|
||||
});
|
||||
}
|
||||
|
||||
if (dumpUnify) {
|
||||
FileUtils.write(AlmostUnifiedPlatform.INSTANCE.getLogPath(), "unify_dump.txt", sb -> {
|
||||
sb.append(last).append("\n");
|
||||
dumpUnifyRecipes(sb);
|
||||
});
|
||||
}
|
||||
|
||||
if (dumpDuplicate) {
|
||||
FileUtils.write(AlmostUnifiedPlatform.INSTANCE.getLogPath(), "duplicates_dump.txt", sb -> {
|
||||
sb.append(last).append("\n");
|
||||
dumpDuplicates(sb);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void dumpDuplicates(StringBuilder stringBuilder) {
|
||||
getSortedUnifiedRecipeTypes().forEach(type -> {
|
||||
Collection<RecipeLink.DuplicateLink> duplicates = result
|
||||
.getDuplicates(type)
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(l -> l.getMaster().getId().toString()))
|
||||
.toList();
|
||||
if (duplicates.isEmpty()) return;
|
||||
|
||||
stringBuilder.append(duplicates
|
||||
.stream()
|
||||
.map(this::createDuplicatesDump)
|
||||
.collect(Collectors.joining("", type + " {\n", "}\n\n")));
|
||||
});
|
||||
}
|
||||
|
||||
private String createDuplicatesDump(RecipeLink.DuplicateLink link) {
|
||||
return link
|
||||
.getRecipes()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(r -> r.getId().toString()))
|
||||
.map(r -> "\t\t- " + r.getId() + "\n")
|
||||
.collect(Collectors.joining("", String.format("\t%s\n", link.getMaster().getId().toString()), "\n"));
|
||||
}
|
||||
|
||||
private void dumpOverview(StringBuilder stringBuilder) {
|
||||
stringBuilder
|
||||
.append("# Overview:\n")
|
||||
.append("\t- Unified: ")
|
||||
.append(result.getUnifiedRecipeCount())
|
||||
.append("\n")
|
||||
.append("\t- Duplicates: ")
|
||||
.append(result.getDuplicatesCount())
|
||||
.append(" (Individual: ")
|
||||
.append(result.getDuplicateRecipesCount())
|
||||
.append(")\n")
|
||||
.append("\t- Total Recipes: ")
|
||||
.append(result.getRecipeCount())
|
||||
.append("\n")
|
||||
.append("\t- Elapsed Time: ")
|
||||
.append(getTotalTime())
|
||||
.append("ms")
|
||||
.append("\n\n")
|
||||
.append("# Summary: \n");
|
||||
|
||||
stringBuilder
|
||||
.append(rf("Recipe type", 45))
|
||||
.append(" | ")
|
||||
.append(lf("Unifies", 10))
|
||||
.append(" | ")
|
||||
.append(lf("Duplicates", 10))
|
||||
.append(" | ")
|
||||
.append(lf("All", 5))
|
||||
.append("\n")
|
||||
.append(StringUtils.repeat("-", 45 + 10 + 10 + 5 + 9))
|
||||
.append("\n");
|
||||
|
||||
getSortedUnifiedRecipeTypes().forEach(type -> {
|
||||
int unifiedSize = result.getUnifiedRecipes(type).size();
|
||||
int allSize = result.getRecipes(type).size();
|
||||
int duplicatesSize = result.getDuplicates(type).size();
|
||||
int individualDuplicatesSize = result
|
||||
.getDuplicates(type)
|
||||
.stream()
|
||||
.mapToInt(l -> l.getRecipes().size())
|
||||
.sum();
|
||||
|
||||
String dStr = String.format("%s (%s)", lf(duplicatesSize, 3), lf(individualDuplicatesSize, 3));
|
||||
stringBuilder
|
||||
.append(rf(type, 45))
|
||||
.append(" | ")
|
||||
.append(lf(unifiedSize, 10))
|
||||
.append(" | ")
|
||||
.append(lf(duplicatesSize == 0 ? " " : dStr, 10))
|
||||
.append(" | ")
|
||||
.append(lf(allSize, 5))
|
||||
.append("\n");
|
||||
});
|
||||
}
|
||||
|
||||
private void dumpUnifyRecipes(StringBuilder stringBuilder) {
|
||||
getSortedUnifiedRecipeTypes().forEach(type -> {
|
||||
stringBuilder.append(type.toString()).append(" {\n");
|
||||
|
||||
getSortedUnifiedRecipes(type).forEach(recipe -> {
|
||||
stringBuilder
|
||||
.append("\t- ")
|
||||
.append(recipe.getId())
|
||||
.append("\n")
|
||||
.append("\t\t Original: ")
|
||||
.append(recipe.getOriginal().toString())
|
||||
.append("\n")
|
||||
.append("\t\t Transformed: ")
|
||||
.append(recipe.getUnified() == null ? "NOT UNIFIED" : recipe.getUnified().toString())
|
||||
.append("\n\n");
|
||||
});
|
||||
|
||||
stringBuilder.append("}\n\n");
|
||||
});
|
||||
}
|
||||
|
||||
private String rf(Object v, int size) {
|
||||
return StringUtils.rightPad(v.toString(), size);
|
||||
}
|
||||
|
||||
private String lf(Object v, int size) {
|
||||
return StringUtils.leftPad(v.toString(), size);
|
||||
}
|
||||
|
||||
private Stream<ResourceLocation> getSortedUnifiedRecipeTypes() {
|
||||
return result.getUnifiedRecipeTypes().stream().sorted(Comparator.comparing(ResourceLocation::toString));
|
||||
}
|
||||
|
||||
private Stream<RecipeLink> getSortedUnifiedRecipes(ResourceLocation type) {
|
||||
return result.getUnifiedRecipes(type).stream().sorted(Comparator.comparing(r -> r.getId().toString()));
|
||||
}
|
||||
|
||||
private long getTotalTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeContext;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class RecipeUnifierBuilderImpl implements RecipeUnifierBuilder {
|
||||
private final Map<String, Entry<?>> consumers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void forEachObject(String property, BiFunction<JsonObject, RecipeContext, JsonObject> consumer) {
|
||||
BiFunction<JsonArray, RecipeContext, JsonArray> arrayConsumer = (array, ctx) -> {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JsonElement element = array.get(i);
|
||||
if (element instanceof JsonObject obj) {
|
||||
JsonObject result = consumer.apply(obj, ctx);
|
||||
if (result != null) {
|
||||
array.set(i, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
put(property, JsonArray.class, arrayConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String property, BiFunction<JsonElement, RecipeContext, JsonElement> consumer) {
|
||||
consumers.put(property, new Entry<>(JsonElement.class, consumer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends JsonElement> void put(String property, Class<T> type, BiFunction<T, RecipeContext, T> consumer) {
|
||||
consumers.put(property, new Entry<>(type, consumer));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JsonObject unify(JsonObject json, RecipeContext context) {
|
||||
JsonObject changedValues = new JsonObject();
|
||||
|
||||
for (var e : json.entrySet()) {
|
||||
Entry<?> consumer = consumers.get(e.getKey());
|
||||
if (consumer != null) {
|
||||
JsonElement currentElement = e.getValue();
|
||||
JsonElement transformedElement = consumer.apply(currentElement.deepCopy(), context);
|
||||
if (transformedElement != null && !transformedElement.equals(currentElement)) {
|
||||
changedValues.add(e.getKey(), transformedElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changedValues.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// helps to preserve the order of the elements
|
||||
JsonObject result = new JsonObject();
|
||||
for (var entry : json.entrySet()) {
|
||||
JsonElement changedValue = changedValues.get(entry.getKey());
|
||||
if (changedValue != null) {
|
||||
result.add(entry.getKey(), changedValue);
|
||||
} else {
|
||||
result.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection<String> getKeys() {
|
||||
return consumers.keySet();
|
||||
}
|
||||
|
||||
private record Entry<T extends JsonElement>(Class<T> expectedType,
|
||||
BiFunction<T, RecipeContext, T> func) {
|
||||
@Nullable
|
||||
T apply(JsonElement json, RecipeContext context) {
|
||||
if (expectedType.isInstance(json)) {
|
||||
return func.apply(expectedType.cast(json), context);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.almostreliable.unified.recipe.unifier;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeConstants;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class GenericRecipeUnifier implements RecipeUnifier {
|
||||
public static final GenericRecipeUnifier INSTANCE = new GenericRecipeUnifier();
|
||||
private static final Set<String> INPUT_KEYS = Set.of(
|
||||
RecipeConstants.INPUT,
|
||||
RecipeConstants.INPUTS,
|
||||
RecipeConstants.INGREDIENT,
|
||||
RecipeConstants.INGREDIENTS,
|
||||
RecipeConstants.INPUT_ITEMS
|
||||
);
|
||||
private static final Set<String> OUTPUT_KEYS = Set.of(
|
||||
RecipeConstants.OUTPUT,
|
||||
RecipeConstants.OUTPUTS,
|
||||
RecipeConstants.RESULT,
|
||||
RecipeConstants.RESULTS,
|
||||
RecipeConstants.OUTPUT_ITEMS
|
||||
);
|
||||
|
||||
@Override
|
||||
public void collectUnifier(RecipeUnifierBuilder builder) {
|
||||
for (String inputKey : INPUT_KEYS) {
|
||||
builder.put(inputKey, (json, ctx) -> ctx.createIngredientReplacement(json));
|
||||
}
|
||||
|
||||
for (String outputKey : OUTPUT_KEYS) {
|
||||
builder.put(outputKey, (json, ctx) -> ctx.createResultReplacement(json));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package com.almostreliable.unified.recipe.unifier;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeConstants;
|
||||
import com.almostreliable.unified.api.recipe.RecipeContext;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RecipeHandlerFactory {
|
||||
|
||||
private static final ResourceLocation SMITHING_TYPE = new ResourceLocation("minecraft:smithing");
|
||||
|
||||
private final Map<ResourceLocation, RecipeUnifier> transformersByType = new HashMap<>();
|
||||
private final Map<String, RecipeUnifier> transformersByModId = new HashMap<>();
|
||||
|
||||
public void fillUnifier(RecipeUnifierBuilder builder, RecipeContext context) {
|
||||
GenericRecipeUnifier.INSTANCE.collectUnifier(builder);
|
||||
|
||||
if (context.hasProperty(ShapedRecipeKeyUnifier.PATTERN_PROPERTY) &&
|
||||
context.hasProperty(ShapedRecipeKeyUnifier.KEY_PROPERTY)) {
|
||||
ShapedRecipeKeyUnifier.INSTANCE.collectUnifier(builder);
|
||||
}
|
||||
|
||||
if (context.hasProperty(SmithingRecipeUnifier.ADDITION_PROPERTY) &&
|
||||
context.hasProperty(SmithingRecipeUnifier.BASE_PROPERTY) &&
|
||||
context.hasProperty(RecipeConstants.RESULT)) {
|
||||
SmithingRecipeUnifier.INSTANCE.collectUnifier(builder);
|
||||
}
|
||||
|
||||
ResourceLocation type = context.getType();
|
||||
RecipeUnifier byMod = transformersByModId.get(type.getNamespace());
|
||||
if (byMod != null) {
|
||||
byMod.collectUnifier(builder);
|
||||
}
|
||||
|
||||
RecipeUnifier byType = transformersByType.get(type);
|
||||
if (byType != null) {
|
||||
byType.collectUnifier(builder);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerForType(ResourceLocation type, RecipeUnifier transformer) {
|
||||
transformersByType.put(type, transformer);
|
||||
}
|
||||
|
||||
public void registerForMod(String mod, RecipeUnifier transformer) {
|
||||
transformersByModId.put(mod, transformer);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package com.almostreliable.unified.recipe.unifier;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class ShapedRecipeKeyUnifier implements RecipeUnifier {
|
||||
public static final RecipeUnifier INSTANCE = new ShapedRecipeKeyUnifier();
|
||||
public static final String PATTERN_PROPERTY = "pattern";
|
||||
public static final String KEY_PROPERTY = "key";
|
||||
|
||||
@Override
|
||||
public void collectUnifier(RecipeUnifierBuilder builder) {
|
||||
builder.put(KEY_PROPERTY, JsonObject.class, (json, context) -> {
|
||||
for (var entry : json.entrySet()) {
|
||||
JsonElement result = context.createIngredientReplacement(entry.getValue());
|
||||
if (result != null) {
|
||||
entry.setValue(result);
|
||||
}
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package com.almostreliable.unified.recipe.unifier;
|
||||
|
||||
import com.almostreliable.unified.api.recipe.RecipeConstants;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifier;
|
||||
import com.almostreliable.unified.api.recipe.RecipeUnifierBuilder;
|
||||
|
||||
public class SmithingRecipeUnifier implements RecipeUnifier {
|
||||
|
||||
public static final RecipeUnifier INSTANCE = new SmithingRecipeUnifier();
|
||||
|
||||
public static final String ADDITION_PROPERTY = "addition";
|
||||
public static final String BASE_PROPERTY = "base";
|
||||
|
||||
@Override
|
||||
public void collectUnifier(RecipeUnifierBuilder builder) {
|
||||
builder.put(ADDITION_PROPERTY, (json, ctx) -> ctx.createIngredientReplacement(json));
|
||||
builder.put(BASE_PROPERTY, (json, ctx) -> ctx.createIngredientReplacement(json));
|
||||
builder.put(RecipeConstants.RESULT, (json, ctx) -> ctx.createResultReplacement(json));
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.almostreliable.unified.recipe.unifier;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -0,0 +1,78 @@
|
|||
package com.almostreliable.unified.unification;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.unification.ModPriorities;
|
||||
import com.almostreliable.unified.api.unification.UnificationEntry;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ModPrioritiesImpl implements ModPriorities {
|
||||
|
||||
private final List<String> modPriorities;
|
||||
private final Map<TagKey<Item>, String> priorityOverrides;
|
||||
|
||||
public ModPrioritiesImpl(List<String> modPriorities, Map<TagKey<Item>, String> priorityOverrides) {
|
||||
this.modPriorities = modPriorities;
|
||||
this.priorityOverrides = priorityOverrides;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPriorityOverride(TagKey<Item> tag) {
|
||||
return priorityOverrides.get(tag);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UnificationEntry<Item> findPriorityOverrideItem(TagKey<Item> tag, List<UnificationEntry<Item>> items) {
|
||||
String priorityOverride = getPriorityOverride(tag);
|
||||
if (priorityOverride == null) return null;
|
||||
|
||||
var entry = findItemByNamespace(items, priorityOverride);
|
||||
if (entry != null) return entry;
|
||||
|
||||
AlmostUnifiedCommon.LOGGER.warn(
|
||||
"Priority override mod '{}' for tag '{}' does not contain a valid item. Falling back to default priority.",
|
||||
priorityOverride,
|
||||
tag.location()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UnificationEntry<Item> findTargetItem(TagKey<Item> tag, List<UnificationEntry<Item>> items) {
|
||||
var overrideEntry = findPriorityOverrideItem(tag, items);
|
||||
if (overrideEntry != null) {
|
||||
return overrideEntry;
|
||||
}
|
||||
|
||||
for (String modPriority : modPriorities) {
|
||||
var entry = findItemByNamespace(items, modPriority);
|
||||
if (entry != null) return entry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return modPriorities.iterator();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static UnificationEntry<Item> findItemByNamespace(List<UnificationEntry<Item>> items, String namespace) {
|
||||
for (var item : items) {
|
||||
if (item.id().getNamespace().equals(namespace)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package com.almostreliable.unified.unification;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.unification.StoneVariants;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
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.level.block.Block;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class StoneVariantsImpl implements StoneVariants {
|
||||
|
||||
private static final Pattern ORE_TAG_PATTERN = Pattern.compile("(c:ores/.+|(minecraft|c):.+_ores)");
|
||||
|
||||
private final Map<TagKey<Item>, Boolean> isOreTagCache = new HashMap<>();
|
||||
|
||||
private final List<String> stoneVariants;
|
||||
private final Map<ResourceLocation, String> itemToStoneVariant;
|
||||
|
||||
private StoneVariantsImpl(Collection<String> stoneVariants, Map<ResourceLocation, String> itemToStoneVariant) {
|
||||
this.stoneVariants = sortStoneVariants(stoneVariants);
|
||||
this.itemToStoneVariant = itemToStoneVariant;
|
||||
}
|
||||
|
||||
public static StoneVariants create(Collection<String> stoneVariants, VanillaTagWrapper<Item> itemTags, VanillaTagWrapper<Block> blockTags) {
|
||||
Set<TagKey<Item>> stoneVariantItemTags = new HashSet<>();
|
||||
Set<TagKey<Block>> stoneVariantBlockTags = new HashSet<>();
|
||||
|
||||
for (String stoneVariant : stoneVariants) {
|
||||
ResourceLocation id = ResourceLocation.fromNamespaceAndPath("c", "ores_in_ground/" + stoneVariant);
|
||||
stoneVariantItemTags.add(TagKey.create(Registries.ITEM, id));
|
||||
stoneVariantBlockTags.add(TagKey.create(Registries.BLOCK, id));
|
||||
}
|
||||
|
||||
var itemToStoneVariantTag = mapEntriesToStoneVariantTags(stoneVariantItemTags, itemTags);
|
||||
var blockToStoneVariantTag = mapEntriesToStoneVariantTags(stoneVariantBlockTags, blockTags);
|
||||
|
||||
Map<ResourceLocation, String> itemToStoneVariant = new HashMap<>();
|
||||
itemToStoneVariantTag.forEach((item, tag) -> {
|
||||
String itemStoneVariant = getVariantFromStoneVariantTag(tag);
|
||||
|
||||
var blockTagFromItem = blockToStoneVariantTag.get(item);
|
||||
if (blockTagFromItem != null) {
|
||||
String blockStoneVariant = getVariantFromStoneVariantTag(blockTagFromItem);
|
||||
if (blockStoneVariant.length() > itemStoneVariant.length()) {
|
||||
itemToStoneVariant.put(item, blockStoneVariant);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
itemToStoneVariant.put(item, itemStoneVariant);
|
||||
});
|
||||
|
||||
return new StoneVariantsImpl(stoneVariants, itemToStoneVariant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps all entries of a stone variant tag to its respective stone variant tag.
|
||||
*
|
||||
* @param stoneVariantTags the stone variant tags
|
||||
* @param tags the vanilla tag wrapper to get the tag entries from
|
||||
* @param <T> the tag type
|
||||
* @return the entry to stone variant tag mapping
|
||||
*/
|
||||
private static <T> Map<ResourceLocation, TagKey<T>> mapEntriesToStoneVariantTags(Set<TagKey<T>> stoneVariantTags, VanillaTagWrapper<T> tags) {
|
||||
Map<ResourceLocation, TagKey<T>> idToStoneVariantTag = new HashMap<>();
|
||||
|
||||
for (var stoneVariantTag : stoneVariantTags) {
|
||||
for (var holder : tags.get(stoneVariantTag)) {
|
||||
ResourceLocation id = holder
|
||||
.unwrapKey()
|
||||
.orElseThrow(() -> new IllegalStateException("Tag is not bound for holder " + holder))
|
||||
.location();
|
||||
|
||||
var oldTag = idToStoneVariantTag.put(id, stoneVariantTag);
|
||||
if (oldTag != null) {
|
||||
AlmostUnifiedCommon.LOGGER.error(
|
||||
"{} is bound to multiple stone variant tags: {} and {}",
|
||||
id,
|
||||
oldTag,
|
||||
stoneVariantTag
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idToStoneVariantTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variant of a stone variant tag.
|
||||
* <p>
|
||||
* Example: {@code c:ores_in_ground/deepslate} -> {@code deepslate}
|
||||
*
|
||||
* @param tag the stone variant tag
|
||||
* @return the stone variant
|
||||
*/
|
||||
private static String getVariantFromStoneVariantTag(TagKey<?> tag) {
|
||||
String tagString = tag.location().toString();
|
||||
int i = tagString.lastIndexOf('/');
|
||||
String stoneVariant = tagString.substring(i + 1);
|
||||
stoneVariant = stoneVariant.equals("stone") ? "" : stoneVariant;
|
||||
return stoneVariant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStoneVariant(ResourceLocation item) {
|
||||
return itemToStoneVariant.computeIfAbsent(item, this::computeStoneVariant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOreTag(TagKey<Item> tag) {
|
||||
return isOreTagCache.computeIfAbsent(tag, t -> ORE_TAG_PATTERN.matcher(t.location().toString()).matches());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all stone variants sorted from longest to shortest.
|
||||
* <p>
|
||||
* This is required to ensure that the longest variant is returned first and no sub-matches happen.<br>
|
||||
* Example: "nether" and "blue_nether" would both match "nether" if the list is not sorted.
|
||||
*
|
||||
* @param stoneVariants the stone variants to sort
|
||||
* @return the sorted stone variants
|
||||
*/
|
||||
private static List<String> sortStoneVariants(Collection<String> stoneVariants) {
|
||||
return stoneVariants.stream().sorted(Comparator.comparingInt(String::length).reversed()).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation logic for caching in {@link #getStoneVariant(ResourceLocation)}.
|
||||
*
|
||||
* @param item the item to get the stone variant of
|
||||
* @return the stone variant of the item
|
||||
*/
|
||||
private String computeStoneVariant(ResourceLocation item) {
|
||||
for (String stoneVariant : stoneVariants) {
|
||||
if (item.getPath().contains(stoneVariant + "_") || item.getPath().endsWith("_" + stoneVariant)) {
|
||||
return stoneVariant.equals("stone") ? "" : stoneVariant;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
package com.almostreliable.unified.unification;
|
||||
|
||||
import com.almostreliable.unified.AlmostUnifiedCommon;
|
||||
import com.almostreliable.unified.api.unification.UnificationEntry;
|
||||
import com.almostreliable.unified.api.unification.UnificationLookup;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
import com.almostreliable.unified.utils.VanillaTagWrapper;
|
||||
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.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TagInheritance {
|
||||
|
||||
private final Options<Item> itemOptions;
|
||||
private final Options<Block> blockOptions;
|
||||
|
||||
public TagInheritance(Mode itemMode, Map<TagKey<Item>, Set<Pattern>> itemInheritance, Mode blockMode, Map<TagKey<Block>, Set<Pattern>> blockInheritance) {
|
||||
itemOptions = new Options<>(itemMode, itemInheritance);
|
||||
blockOptions = new Options<>(blockMode, blockInheritance);
|
||||
}
|
||||
|
||||
public boolean apply(VanillaTagWrapper<Item> itemTags, VanillaTagWrapper<Block> blockTags, List<? extends UnificationLookup> unificationLookups) {
|
||||
Multimap<UnificationEntry<Item>, ResourceLocation> changedItemTags = HashMultimap.create();
|
||||
Multimap<UnificationEntry<Item>, ResourceLocation> changedBlockTags = HashMultimap.create();
|
||||
|
||||
var relations = resolveRelations(unificationLookups);
|
||||
if (relations.isEmpty()) return false;
|
||||
|
||||
for (var relation : relations) {
|
||||
var targetItem = relation.targetItem;
|
||||
var targetItemHolder = targetItem.asHolderOrThrow();
|
||||
var targetBlockHolder = findTargetBlockHolder(blockTags, targetItem);
|
||||
|
||||
var targetItemTags = itemTags
|
||||
.getTags(targetItem)
|
||||
.stream()
|
||||
.map(rl -> TagKey.create(Registries.ITEM, rl))
|
||||
.collect(ImmutableSet.toImmutableSet());
|
||||
|
||||
for (var item : relation.items) {
|
||||
var appliedItemTags = applyItemTags(itemTags, targetItemHolder, targetItemTags, item);
|
||||
changedItemTags.putAll(targetItem, appliedItemTags);
|
||||
|
||||
if (targetBlockHolder != null) {
|
||||
var appliedBlockTags = applyBlockTags(blockTags, targetBlockHolder, targetItemTags, item);
|
||||
changedBlockTags.putAll(targetItem, appliedBlockTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!changedBlockTags.isEmpty()) {
|
||||
changedBlockTags.asMap().forEach((target, tags) -> {
|
||||
AlmostUnifiedCommon.LOGGER.info("[TagInheritance] Added '{}' to block tags {}", target.id(), tags);
|
||||
});
|
||||
}
|
||||
|
||||
if (!changedItemTags.isEmpty()) {
|
||||
changedItemTags.asMap().forEach((target, tags) -> {
|
||||
AlmostUnifiedCommon.LOGGER.info("[TagInheritance] Added '{}' to item tags {}", target.id(), tags);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Holder<Block> findTargetBlockHolder(VanillaTagWrapper<Block> tagMap, UnificationEntry<Item> targetItem) {
|
||||
var blockTags = tagMap.getTags(targetItem.id());
|
||||
if (blockTags.isEmpty()) return null;
|
||||
|
||||
return BuiltInRegistries.BLOCK.getHolderOrThrow(ResourceKey.create(Registries.BLOCK, targetItem.id()));
|
||||
}
|
||||
|
||||
private Set<ResourceLocation> applyItemTags(VanillaTagWrapper<Item> vanillaTags, Holder<Item> targetItem, Set<TagKey<Item>> targetItemTags, UnificationEntry<Item> item) {
|
||||
var itemTags = vanillaTags.getTags(item);
|
||||
Set<ResourceLocation> changed = new HashSet<>();
|
||||
|
||||
for (var itemTag : itemTags) {
|
||||
var tag = TagKey.create(Registries.ITEM, itemTag);
|
||||
if (itemOptions.shouldInherit(tag, targetItemTags) && addToVanilla(targetItem, tag, vanillaTags)) {
|
||||
changed.add(itemTag);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private Set<ResourceLocation> applyBlockTags(VanillaTagWrapper<Block> blockTagMap, Holder<Block> targetBlock, Set<TagKey<Item>> targetItemTags, UnificationEntry<Item> item) {
|
||||
var blockTags = blockTagMap.getTags(item.id());
|
||||
Set<ResourceLocation> changed = new HashSet<>();
|
||||
|
||||
for (var blockTag : blockTags) {
|
||||
var tag = TagKey.create(Registries.BLOCK, blockTag);
|
||||
if (blockOptions.shouldInherit(tag, targetItemTags) && addToVanilla(targetBlock, tag, blockTagMap)) {
|
||||
changed.add(blockTag);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given holder to the given tag.
|
||||
*
|
||||
* @param holder The holder
|
||||
* @param tag The tag the holder should be added to
|
||||
* @param vanillaTags The vanilla tag wrapper
|
||||
* @return true if the holder was added, false if it was already present
|
||||
*/
|
||||
private static <T> boolean addToVanilla(Holder<T> holder, TagKey<T> tag, VanillaTagWrapper<T> vanillaTags) {
|
||||
var tagHolders = vanillaTags.get(tag);
|
||||
if (tagHolders.contains(holder)) return false; // already present, no need to add it again
|
||||
|
||||
vanillaTags.add(tag.location(), holder);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Set<TagRelation> resolveRelations(Collection<? extends UnificationLookup> unificationLookups) {
|
||||
Set<TagRelation> relations = new HashSet<>();
|
||||
|
||||
for (var unificationLookup : unificationLookups) {
|
||||
for (TagKey<Item> unifyTag : unificationLookup.getTags()) {
|
||||
if (itemOptions.skipForInheritance(unifyTag) && blockOptions.skipForInheritance(unifyTag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var itemsByTag = unificationLookup.getTagEntries(unifyTag);
|
||||
|
||||
// avoid handling single entries and tags that only contain the same namespace for all items
|
||||
if (Utils.allSameNamespace(itemsByTag)) continue;
|
||||
|
||||
var target = unificationLookup.getTagTargetItem(unifyTag);
|
||||
if (target == null) continue;
|
||||
|
||||
var items = removeTargetItem(itemsByTag, target);
|
||||
|
||||
if (items.isEmpty()) continue;
|
||||
relations.add(new TagRelation(unifyTag, target, items));
|
||||
}
|
||||
}
|
||||
|
||||
return relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all items that are not the target item and are valid by checking if they are registered.
|
||||
*
|
||||
* @param holders The set of all items that are in the tag
|
||||
* @param target The target item
|
||||
* @return A set of all items that are not the target item and are valid
|
||||
*/
|
||||
private Set<UnificationEntry<Item>> removeTargetItem(Collection<UnificationEntry<Item>> holders, UnificationEntry<Item> target) {
|
||||
Set<UnificationEntry<Item>> result = new HashSet<>(holders.size());
|
||||
for (var holder : holders) {
|
||||
if (!holder.equals(target)) {
|
||||
result.add(holder);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private record TagRelation(TagKey<Item> tag, UnificationEntry<Item> targetItem,
|
||||
Set<UnificationEntry<Item>> items) {}
|
||||
|
||||
public enum Mode {
|
||||
ALLOW,
|
||||
DENY
|
||||
}
|
||||
|
||||
private record Options<T>(Mode mode, Map<TagKey<T>, Set<Pattern>> inheritance) {
|
||||
|
||||
/**
|
||||
* Checks if given tag is used in the inheritance config.
|
||||
* <p>
|
||||
* If mode is allowed, the tag should match any pattern in the config. If mode is deny, the tag should not match
|
||||
* any pattern in the config.
|
||||
*
|
||||
* @param tag The tag to check
|
||||
* @return True if the tag should be skipped
|
||||
*/
|
||||
public boolean skipForInheritance(TagKey<Item> tag) {
|
||||
var tagStr = tag.location().toString();
|
||||
boolean modeResult = mode == Mode.ALLOW;
|
||||
for (Set<Pattern> patterns : inheritance.values()) {
|
||||
for (Pattern pattern : patterns) {
|
||||
if (pattern.matcher(tagStr).matches()) {
|
||||
return !modeResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modeResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given inheritance tag would match any of the target item tags.
|
||||
* <p>
|
||||
* E. g based on a simple config:
|
||||
* <pre>
|
||||
* {@code {
|
||||
* "minecraft:beacon_payment_items": [
|
||||
* "c:ores/silver"
|
||||
* ]
|
||||
* }}
|
||||
* </pre>
|
||||
* "minecraft:beacon_payment_items" would be the inheritance tag and "c:ores/silver" would be one of the target item tags.
|
||||
* If mode is {@code DENY}, the check would be inverted.
|
||||
*
|
||||
* @param inheritanceTag The inheritance tag
|
||||
* @param targetItemTags The target item tags
|
||||
* @return True if we should allow the inheritance or false if we should deny the inheritance
|
||||
*/
|
||||
public boolean shouldInherit(TagKey<T> inheritanceTag, Collection<TagKey<Item>> targetItemTags) {
|
||||
var patterns = inheritance.getOrDefault(inheritanceTag, Set.of());
|
||||
boolean result = checkPatterns(targetItemTags, patterns);
|
||||
// noinspection SimplifiableConditionalExpression
|
||||
return mode == Mode.ALLOW ? result : !result;
|
||||
}
|
||||
|
||||
private boolean checkPatterns(Collection<TagKey<Item>> tags, Collection<Pattern> patterns) {
|
||||
for (var pattern : patterns) {
|
||||
for (var tag : tags) {
|
||||
if (pattern.matcher(tag.location().toString()).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue