diff --git a/.github/check-self-contained.sh b/.github/check-self-contained.sh new file mode 100755 index 0000000..c2a8fff --- /dev/null +++ b/.github/check-self-contained.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# +# Verifies that this API can be compiled on its own, without the CommonCapabilities mod +# and without loader-specific CyclopsCore classes. +# +# CyclopsCore consumes this repo as a submodule in a dedicated source set that only sees +# Minecraft, NeoForge and CyclopsCore's loader-common. Anything outside of that breaks its +# build, which is what this check guards against. +# +# Usage: .github/check-self-contained.sh [path-to-cyclopscore-checkout] + +set -euo pipefail + +API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CYCLOPSCORE_DIR="${1:-}" +CYCLOPSCORE_COMMON="" + +if [[ -n "${CYCLOPSCORE_DIR}" ]]; then + CYCLOPSCORE_COMMON="${CYCLOPSCORE_DIR}/loader-common/src/main/java" + if [[ ! -d "${CYCLOPSCORE_COMMON}" ]]; then + echo "error: ${CYCLOPSCORE_COMMON} does not exist" >&2 + exit 1 + fi +fi + +failures=0 + +fail() { + echo " $1" >&2 + failures=$((failures + 1)) +} + +# Reduce a dotted name to its top-level class, e.g. a.b.C.D.FIELD -> a.b.C +top_level_class() { + local name="$1" out="" segment + local IFS='.' + for segment in ${name}; do + out="${out:+${out}.}${segment}" + if [[ "${segment}" =~ ^[A-Z] ]]; then + echo "${out}" + return + fi + done + echo "" +} + +# Collect every org.cyclops.* reference, both imports and inline fully-qualified usages. +# Only this repo's own tracked sources are scanned, so a CyclopsCore checkout that happens to +# sit inside the working directory is not picked up. +sources="$(git -C "${API_DIR}" ls-files '*.java')" +if [[ -z "${sources}" ]]; then + echo "error: no java sources found in ${API_DIR}" >&2 + exit 1 +fi +references="$(echo "${sources}" \ + | sed -E "s|^|${API_DIR}/|" \ + | xargs grep -hoE 'org\.cyclops\.[A-Za-z0-9_.]+' \ + | sed -E 's/\.$//' \ + | sort -u)" + +while IFS= read -r reference; do + [[ -n "${reference}" ]] || continue + class="$(top_level_class "${reference}")" + + if [[ "${reference}" == org.cyclops.commoncapabilities.* ]]; then + if [[ "${reference}" != org.cyclops.commoncapabilities.api.* ]]; then + fail "${reference} lives in the CommonCapabilities mod, not in this API" + fi + continue + fi + + if [[ "${reference}" == org.cyclops.cyclopscore.* ]]; then + if [[ -z "${class}" ]]; then + # A wildcard import or a bare package reference, nothing to resolve + continue + fi + if [[ -z "${CYCLOPSCORE_COMMON}" ]]; then + echo " skipping ${class}, no CyclopsCore checkout given" >&2 + continue + fi + if [[ ! -f "${CYCLOPSCORE_COMMON}/${class//.//}.java" ]]; then + fail "${class} is not in CyclopsCore's loader-common, so it is loader-specific" + fi + continue + fi + + fail "${reference} is not part of this API, CyclopsCore's loader-common, Minecraft or NeoForge" +done <<< "${references}" + +if [[ "${failures}" -gt 0 ]]; then + echo "" >&2 + echo "${failures} disallowed reference(s) found, this API is no longer self-contained." >&2 + exit 1 +fi + +echo "This API is self-contained." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fdd31c0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + pull_request: + +jobs: + self-contained: + name: Check self-containment + runs-on: ubuntu-latest + steps: + - name: Checkout API + uses: actions/checkout@v4 + + - name: Checkout CyclopsCore + uses: actions/checkout@v4 + with: + repository: CyclopsMC/CyclopsCore + # The branch that consumes this API branch as a submodule + ref: master-26 + path: cyclopscore + + - name: Check + run: .github/check-self-contained.sh cyclopscore diff --git a/capability/itemhandler/ItemMatch.java b/capability/itemhandler/ItemMatch.java index 049f600..83df6d0 100644 --- a/capability/itemhandler/ItemMatch.java +++ b/capability/itemhandler/ItemMatch.java @@ -2,7 +2,7 @@ import net.minecraft.core.component.DataComponentMap; import net.minecraft.world.item.ItemStack; -import org.cyclops.commoncapabilities.ingredient.DataComparator; +import org.cyclops.commoncapabilities.api.ingredient.IDataComparator; /** * Item matching flags to be used in {@link ISlotlessItemHandler}. @@ -34,7 +34,7 @@ public final class ItemMatch { /** * A comparator for data components. (This is set in GeneralConfig) */ - public static DataComparator DATA_COMPARATOR; + public static IDataComparator DATA_COMPARATOR; public static boolean areItemStacksEqual(ItemStack a, ItemStack b, int matchFlags) { if (matchFlags == ANY) { diff --git a/capability/recipehandler/RecipeDefinition.java b/capability/recipehandler/RecipeDefinition.java index 4ed611b..a9d4ec0 100644 --- a/capability/recipehandler/RecipeDefinition.java +++ b/capability/recipehandler/RecipeDefinition.java @@ -8,7 +8,6 @@ import net.minecraft.world.level.Level; import org.cyclops.commoncapabilities.api.ingredient.*; import org.cyclops.cyclopscore.helper.IModHelpers; -import org.cyclops.cyclopscore.helper.IModHelpersNeoForge; import javax.annotation.Nullable; import java.util.*; @@ -64,7 +63,7 @@ public RecipeDefinition(Map, List> recipeId) { Optional> recipeHolder; if (IModHelpers.get().getMinecraftHelpers().isClientSide()) { - recipeHolder = Optional.ofNullable(IModHelpersNeoForge.get().getMinecraftClientHelpers().getRecipes().byKey(recipeId)); + recipeHolder = Optional.ofNullable(IModHelpers.get().getMinecraftClientHelpers().getRecipes().byKey(recipeId)); } else { recipeHolder = IModHelpers.get().getCraftingHelpers().getRecipeManager().byKey(recipeId); } diff --git a/ingredient/IDataComparator.java b/ingredient/IDataComparator.java new file mode 100644 index 0000000..dc62e51 --- /dev/null +++ b/ingredient/IDataComparator.java @@ -0,0 +1,18 @@ +package org.cyclops.commoncapabilities.api.ingredient; + +import net.minecraft.core.component.DataComponentMap; + +import java.util.Comparator; + +/** + * A comparator for data component maps that can ignore certain data component types. + * @author rubensworks + */ +public interface IDataComparator extends Comparator { + + /** + * @return If at least one data component type is being ignored. + */ + public boolean hasIgnoreDataComponentTypes(); + +}