Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ othermods
crashreport
.cursor
.cache
*.cache
*.bak.png
*.orig
*.rej
.codex
# external reference code
references/
Expand Down
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@ A Minecraft mod featuring various utilities and enhancements.

## Forge 1.20.1

Version `20.2.4.6` targets Minecraft 1.20.1, Forge 47.3.10+, Java 17,
AE2 15.4.8 and the Create 2 Mekanism: Sky Edition 2 integration profile.
Version `20.2.4.7` targets Minecraft 1.20.1, Forge 47.3.10+, Java 17,
AE2 15.4.8+, Mekanism 10.4.15+ (compiled against 10.4.16.80),
Mekanism Extras 1.5.0+ and the Create 2 Mekanism: Sky Edition 2 integration profile.
Build with `./gradlew build` (Windows: `gradlew.bat build`); Python 3 is used by
nine release-bytecode/outline regression checks. Dependencies resolve from public Maven repositories.
The `portBridgeTest` suite also runs as part of `check` and is not shipped in the mod jar.

Forge, Minecraft and AE2 are required. Every compiled mod integration declares a
minimum and compatible version range in `src/main/resources/META-INF/mods.toml`;
these integrations remain optional, so an absent integration does not prevent startup.
Minimum versions follow the actual mod metadata (for example, Mekanism `10.4.15`,
not the jar filename's build suffix). Ore Generator samples accept Forge's `ores`
and `raw_materials` item tags, plus the equivalent `c:` datapack tags.
Runtime minima and compile pins are separate: for example, Mekanism accepts
`10.4.15+` while this build compiles against `10.4.16.80`. Botany Pots is accepted
from `13.0.0`; Mekanism Extras starts at `1.5.0` and uses its plural
`registries`/`genregistries` API for the Naquadah reactor adapter. Ore Generator
samples accept Forge's `ores` and `raw_materials` item tags, plus the equivalent
`c:` datapack tags.

Optional integrations without a Forge implementation are excluded explicitly in
`gradle/forge1201-pack-profile.json`; this is not a universal backport of every
NeoForge integration. Extended Crafting compiles against official 6.0.10; the
pack's unofficial tier-5/11x11 extension is supported through its native recipe
contract and registry lookup without bundling or replacing the pack's jar.
Missing optional adapter classes are skipped quietly at debug level instead of
failing the interaction; actual invocation failures are reported once. The
Occultism spirit-egg handler is one such excluded adapter in this Forge build.

## Features

Expand Down
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ dependencies {
modCompileOnly 'maven.modrinth:rxYaglEe:d83Wdhdn' // AdvancedAE 1.3.6-1.20.1
modCompileOnly 'maven.modrinth:qDJXZJTz:PQ3IlR98' // Mekanism More Machine 1.2.1
modCompileOnly 'maven.modrinth:rqFWfVlz:tT3CLYvK' // apotheosis-1.20.1-7.4.8
modCompileOnly 'maven.modrinth:IiATswDj:d9GM663Q' // applied-mekanistics-1.4.2
modCompileOnly 'maven.modrinth:IiATswDj:9n9p68Qq' // applied-mekanistics-1.4.3
modCompileOnly 'maven.modrinth:XxWD5pD3:jYgSnxye' // appliedenergistics2-forge-15.4.11
modCompileOnly 'maven.modrinth:udZtKfzP:Qm3GW128' // neoecoae-20.4.2-forge
modCompileOnly 'maven.modrinth:B1CBVXHX:YSOP0G6O' // ldlib-forge-1.20.1-1.0.52.a
Expand All @@ -81,8 +81,8 @@ dependencies {
modCompileOnly 'maven.modrinth:fRiHVvU7:WtJS5tVw' // emi-1.1.22-1.20.1-forge
modCompileOnly 'maven.modrinth:u6dRKJwZ:TrKZlZ0b' // JEI 15.74.0.227
modCompileOnly 'maven.modrinth:umyGl7zF:g5igndAv' // kubejs-forge-2001.6.5-build.16
modCompileOnly 'maven.modrinth:Ce6I4WUE:9iboPfea' // mekanism-1.20.1-10.4.15.75
modCompileOnly 'maven.modrinth:OFVYKsAk:x9re7wt4' // mekanismgenerators-1.20.1-10.4.15.75
modCompileOnly 'maven.modrinth:Ce6I4WUE:uxe1WQp4' // mekanism-1.20.1-10.4.16.80
modCompileOnly 'maven.modrinth:OFVYKsAk:Th4Czz4N' // mekanismgenerators-1.20.1-10.4.16.80
modCompileOnly 'maven.modrinth:tCkE8p2N:6SkuAGoz' // placebo-1.20.1-8.6.3
modCompileOnly 'maven.modrinth:KZO4S4DO:xDOvxyqP' // powah-5.0.11
modCompileOnly 'com.tterrag.registrate:Registrate:MC1.20-1.3.11' // registrate-mc1.20-1.3.11
Expand All @@ -91,7 +91,7 @@ dependencies {
modCompileOnly 'maven.modrinth:1Ro7m06l:8krhHkrZ' // titanium-1.20.1-3.8.32
modCompileOnly 'maven.modrinth:Rw1NrDzF:tHDveaxK' // cucumber-1.20.1-7.0.15
modCompileOnly 'maven.modrinth:UhW5uCKw:eoUaDkZf' // glodium-1.20-1.5-forge
modCompileOnly 'maven.modrinth:HUZkxNTQ:zzqHs32A' // mekanism_extras-1.20.1-1.4.1
modCompileOnly 'maven.modrinth:HUZkxNTQ:9T6CetkG' // mekanism_extras-1.20.1-1.5.0
modCompileOnly 'net.createmod.ponder:Ponder-Forge-1.20.1:1.0.80' // ponder-forge-1.20.1-1.0.80
modCompileOnly 'dev.engine-room.flywheel:flywheel-forge-1.20.1:1.0.4' // flywheel-forge-1.20.1-1.0.4
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ forge_version=47.4.10
loader_version_range=[47,)
mod_id=useless_mod
mod_name=Useless Mod
mod_version=20.2.4.6
mod_version=20.2.4.7
mod_group_id=com.sorrowmist.useless
mod_license=MIT
4 changes: 3 additions & 1 deletion src/main/java/com/sorrowmist/useless/UselessMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ public void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
}

private static InteractionResult trySpawnMarkedOccultismSpirit(PlayerInteractEvent.RightClickBlock event) {
if (!ModList.get().isLoaded("occultism")) {
if (!ModList.get().isLoaded("occultism")
|| !OptionalIntegration.hasClass(
"com.sorrowmist.useless.content.recipe.adapters.occultism.OccultismSpiritEggHandler")) {
return InteractionResult.PASS;
}
return OccultismCompat.trySpawnMarkedSpirit(event);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.sorrowmist.useless.compat.forge1201;

import com.mojang.logging.LogUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraftforge.fml.ModList;
import org.slf4j.Logger;

/** Optional source boundaries: no class linkage when the corresponding mod is absent. */
public final class OptionalIntegration {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Set<String> REPORTED_FAILURES = ConcurrentHashMap.newKeySet();

private OptionalIntegration() {}

public static boolean hasClass(String className) {
Expand All @@ -14,14 +21,46 @@ public static boolean hasClass(String className) {

public static <T> T call(String modId,String className,String method,Class<T> resultType,T absent,
Class<?>[] signature,Object... arguments) {
if (!ModList.get().isLoaded(modId)) return absent;
ModList modList = ModList.get();
if (modList == null || !modList.isLoaded(modId)) return absent;
try {
Object result=Class.forName(className).getMethod(method,signature).invoke(null,arguments);
return resultType==Void.class ? null : resultType.cast(result);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Integration "+modId+" failed in "+className+"#"+method,e.getCause());
reportFailure(modId, className, method, e.getCause());
return absent;
} catch (ReflectiveOperationException | LinkageError e) {
throw new IllegalStateException("Installed integration "+modId+" is unavailable in this build: "+className,e);
reportFailure(modId, className, method, e);
return absent;
} catch (ClassCastException e) {
reportFailure(modId, className, method, e);
return absent;
}
}

/**
* Optional integrations must not take the game down. A missing class is common
* when this Forge build intentionally excludes a NeoForge-only adapter; report
* the first occurrence once and let the caller continue with its fallback.
*/
private static void reportFailure(String modId, String className, String method, Throwable cause) {
String key = modId + "#" + className + "#" + method;
if (REPORTED_FAILURES.add(key)) {
boolean missing = cause instanceof ClassNotFoundException
|| cause instanceof NoClassDefFoundError;
if (missing) {
// Build-profile exclusions are expected; keep them out of normal logs.
LOGGER.debug("Optional integration {} is not present at {}#{}; skipping it",
modId, className, method);
return;
}
if (cause == null) {
LOGGER.warn("Optional integration {} is unavailable or failed at {}#{}; skipping it",
modId, className, method);
} else {
LOGGER.warn("Optional integration {} is unavailable or failed at {}#{}; skipping it",
modId, className, method, cause);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,34 @@ private MekanismGeneratorsCompatLoader() {
public static void register() {
AlloyFurnaceRecipeManager.getInstance().registerAdapter(
new FissionRecipeAdapter(), RecipeSourceIds.MEKANISM_GENERATORS);
if (ModList.get().isLoaded("mekanism_extras")) {
if (ModList.get().isLoaded("mekanism_extras") && hasNaquadahApi()) {
AlloyFurnaceRecipeManager.getInstance().registerAdapter(
new NaquadahReactorRecipeAdapter(), RecipeSourceIds.MEKANISM_GENERATORS);
}
}

/**
* The 1.5 API moved these registries into plural packages. Keep a string-only
* check here so a future package move does not turn an optional integration
* into a hard class-loading failure.
*/
private static boolean hasNaquadahApi() {
try {
ClassLoader loader = MekanismGeneratorsCompatLoader.class.getClassLoader();
Class<?> gases = Class.forName(
"com.jerry.mekanism_extras.common.registries.ExtraGases", false, loader);
gases.getField("RICH_NAQUADAH_FUEL");
gases.getField("RICH_URANIUM_FUEL");
gases.getField("NAQUADAH_URANIUM_FUEL");
Class<?> blocks = Class.forName(
"com.jerry.generator_extras.common.genregistries.ExtraGenBlocks", false, loader);
blocks.getField("NAQUADAH_REACTOR_CONTROLLER");
Class<?> genGases = Class.forName(
"com.jerry.generator_extras.common.genregistries.ExtraGenGases", false, loader);
genGases.getField("POLONIUM_CONTAINING_STEAM");
return true;
} catch (ClassNotFoundException | NoSuchFieldException | LinkageError ignored) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ private static Snapshot build(Level level, long generation) {
Map.copyOf(bySource), Map.copyOf(bySourceAndRecipeId), generation,
new ConcurrentHashMap<>(), new ConcurrentHashMap<>(),
ConcurrentHashMap.newKeySet());
LOGGER.info("Built alloy-furnace recipe catalog: generation={}, recipes={}, sources={}, elapsed={} ms",
LOGGER.debug("Built alloy-furnace recipe catalog: generation={}, recipes={}, sources={}, elapsed={} ms",
generation, ordered.size(), bySource.size(), (System.nanoTime() - startedAt) / 1_000_000L);
return snapshot;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ private static void initCompat(FMLCommonSetupEvent event, @Nullable String modId
}

private static void registerByName(String className) {
if (!isClassAvailable(className)) {
LOGGER.debug("Optional recipe adapter {} is not present in this build", className);
return;
}
try {
Class<?> type=Class.forName(className,true,RecipeAdapterCompatRegistry.class.getClassLoader());
register((IRecipeAdapter<?>)type.getConstructor().newInstance());
Expand All @@ -209,6 +213,10 @@ private static void registerByName(String className) {
}

private static void registerFactory(String className) {
if (!isClassAvailable(className)) {
LOGGER.debug("Optional recipe adapter factory {} is not present in this build", className);
return;
}
try {
Class<?> type=Class.forName(className,true,RecipeAdapterCompatRegistry.class.getClassLoader());
var adapters=(java.util.List<?>)type.getMethod("createAdapters").invoke(null);
Expand Down Expand Up @@ -315,6 +323,10 @@ private static void registerExtendedAE() {
}

private static void registerAdvancedAE() {
if (!isClassAvailable("net.pedroksl.ae2addonlib.recipes.IngredientStack$Item")) {
LOGGER.debug("AdvancedAE 1.2.x does not expose the 1.3 addon-lib recipe API; skipping reaction chamber adapter");
return;
}
registerByName("com.sorrowmist.useless.content.recipe.adapters.ae.advancedae.ReactionChamberRecipeAdapter");
}

Expand All @@ -329,6 +341,10 @@ private static void registerMekanismGenerators() {
}

private static void invokeOptionalLoader(String className) {
if (!isClassAvailable(className)) {
LOGGER.debug("Optional recipe adapter loader {} is not present in this build", className);
return;
}
try {
Class<?> loader = Class.forName(className, true, RecipeAdapterCompatRegistry.class.getClassLoader());
loader.getMethod("register").invoke(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import appeng.api.stacks.GenericStack;
import com.jerry.generator_extras.common.config.GenLoadConfig;
import com.jerry.generator_extras.common.genregistry.ExtraGenBlocks;
import com.jerry.generator_extras.common.genregistry.ExtraGenGases;
import com.jerry.mekanism_extras.common.registry.ExtraGases;
import com.jerry.generator_extras.common.genregistries.ExtraGenBlocks;
import com.jerry.generator_extras.common.genregistries.ExtraGenGases;
import com.jerry.mekanism_extras.common.registries.ExtraGases;
import com.sorrowmist.useless.content.recipe.AdapterUtils;
import com.sorrowmist.useless.content.recipe.AdvancedAlloyFurnaceRecipe;
import com.sorrowmist.useless.content.recipe.adapters.mekanism.MekanismChemicalRecipeSupport;
Expand All @@ -22,7 +22,7 @@

import java.util.List;

/** Optional Mekanism Extras naquadah reactor recipe-viewer adapter. */
/** Optional Mekanism Extras 1.5+ naquadah reactor recipe-viewer adapter. */
public final class NaquadahReactorRecipeAdapter extends MekanismSyntheticRecipeAdapter {
private static final long PROCESS_TICKS = AdapterUtils.MEKANISM_BASE_TICKS_REQUIRED;
private static final ResourceLocation SOURCE_ID =
Expand Down Expand Up @@ -56,12 +56,12 @@ protected List<RecipeHolder<MekanismSyntheticRecipe>> createGeneratedRecipes(Lev
}

addFuelRecipe(result, pairedFuelWaterInputs,
List.of(ExtraGases.RICH_SILICON_LIQUID_FUEL.getStack(1),
ExtraGases.RICH_URANIUM_LIQUID_FUEL.getStack(1)),
List.of(ExtraGases.RICH_NAQUADAH_FUEL.getStack(1),
ExtraGases.RICH_URANIUM_FUEL.getStack(1)),
MekanismChemicalRecipeSupport.saturatingMultiply(waterPerFuel, 2L),
"rich_fuels_water");
addFuelRecipe(result, singleFuelWaterInputs,
List.of(ExtraGases.SILICON_URANIUM_FUEL.getStack(1)),
List.of(ExtraGases.NAQUADAH_URANIUM_FUEL.getStack(1)),
waterPerFuel, "naquadah_uranium_fuel_water");
addWaterOnlyRecipe(result);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ public class ConfigManager {
.defineInRange("botany_pot_growth_multiplier", 1, 1, Integer.MAX_VALUE);

MATRIX_PATTERN_COUNT = SERVER_BUILDER
.comment("矩阵样板槽位倍数 - 减少数量时请保持槽位空!否则可能会造成样板丢失")
.comment("矩阵样板槽位倍数 - 新放置的矩阵样板方块生效;已放置的方块保持原有容量",
"减少数量时请保持槽位空!否则可能会造成样板丢失")
.defineInRange("matrix_pattern_count", 1, 1, 100);
SERVER_BUILDER.pop();

Expand Down Expand Up @@ -803,6 +804,8 @@ public class ConfigManager {
"enable_ufo_recipe_conversion", true);
COMMON_BUILDER.pop();

// Options for adapters excluded by gradle/forge1201-pack-profile.json stay in the
// spec for cross-pack config compatibility; the registry skips their absent classes.
RECIPE_CONVERSION_OPTIONS = Map.ofEntries(
Map.entry("thermal", ENABLE_THERMAL_RECIPE_CONVERSION),
Map.entry("bloodmagic", ENABLE_BLOOD_MAGIC_RECIPE_CONVERSION),
Expand Down Expand Up @@ -1013,6 +1016,15 @@ public static int getMaxUpgrade() {
return getConfigValue(MAX_UPGRADE);
}

/** Returns the loaded max_upgrade, or null while the server config is not available yet. */
public static Integer getLoadedMaxUpgrade() {
try {
return MAX_UPGRADE.get();
} catch (IllegalStateException | NullPointerException ignored) {
return null;
}
}

// 万象炉AE网络抽电配置
public static boolean isFurnaceDrawAppfluxEnergyEnabled() {
return getConfigValue(FURNACE_DRAW_APPFLUX_ENERGY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.sorrowmist.useless.content.blockentities.multiblock.PassiveCraftingHatchBlockEntity;
import com.sorrowmist.useless.compat.draconicevolution.DraconicOpStorageCompat;
import com.sorrowmist.useless.compat.fluxnetworks.FluxNetworksEnergyCompat;
import com.sorrowmist.useless.compat.forge1201.OptionalIntegration;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.eventbus.api.IEventBus;
Expand Down Expand Up @@ -109,6 +110,10 @@ private static void registerCapabilities(RegisterCapabilitiesEvent event) {
* JVM must not resolve AppMek's capability types on a server that does not install AppMek.
*/
private static void invokeOptionalCapabilityLoader(String className, RegisterCapabilitiesEvent event) {
if (!OptionalIntegration.hasClass(className)) {
UselessMod.LOGGER.debug("Optional capability loader {} is not present in this build", className);
return;
}
try {
Class<?> loader = Class.forName(className, true, ModBlockEntities.class.getClassLoader());
loader.getMethod("registerCapabilities", RegisterCapabilitiesEvent.class).invoke(null, event);
Expand Down
Loading
Loading