diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf1ba8..2b47cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Before a release, `./gradlew releaseChangelog` turns *Unreleased* into the relea ## [Unreleased] +### Added + +- NEEPMeat compatibility (Fabric 1.20.1): vascular conduits power electric lights (1 eJ = 1 E); a full electric light takes no power from the network + ## [1.0.0] - 2026-11-03 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 260b689..7c2ee95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,8 @@ Core gameplay: Jade creates every plugin it finds on dedicated servers too, where the client class cannot be loaded (Fabric refuses it, NeoForge 1.21.11 fails on its client types), so `OMLJadeClientPlugin` is not registered with Jade: `OMLJadePlugin.registerClient`, which Jade calls only on a client, creates it by name and forwards to it (fixed on 2026-10-27: before, dedicated servers with Jade logged errors, and on 1.21.11 they crashed). The NeoForge and Forge `client` source sets get Jade through `clientCompileOnly` extending `compileOnly`. Jade is not on the NeoForge GameTest server: there it sends a packet to the tests' vanilla mock players, which NeoForge refuses. +- **NEEPMeat** (Fabric 1.20.1 only, Modrinth `maven.modrinth:neepmeat`, version in `versions/1.20.1-fabric/gradle.properties`; requested by the user on 2026-11-04): its vascular network charges the electric lights through OMLib (`platform/fabric/FabricNeepMeatPower`, registered by `FabricBlockLookups` when NEEPMeat is loaded; 1 eJ = 1 E, a full buffer takes nothing), so the mod has no code of its own for it. + Never bundled (`suggests` in `fabric.mod.json`); `modCompileOnly` for the game test `gametest/platform/fabric/NeepMeatGameTest` (in `src/gametest/mc1.20.1-1.20.1`; the other nodes drop its line from the test mod's `fabric-gametest` entrypoints), and `modLocalRuntime` with the mods nested in its jar (Loom leaves them out of its remapped copy; OMLib's `nestedModJars`) and **GeckoLib**, which it requires, for the dev runs and the game test server. - **NeoForge** 21.1.x (1.21.1) and 21.11.x (1.21.11), built with **ModDevGradle** (approved by the user; build-time only). On NeoForge, the Fabric-only APIs above are replaced by NeoForge's own (capabilities, events, payload registration); see ยง4 "Loaders". - **Forge** 47.x (1.20.1 only), built with the legacy variant of ModDevGradle (`net.neoforged.moddev.legacyforge`, same artifact and version as `moddev`; build-time only), requested by the user. diff --git a/README.md b/README.md index e5a1683..927b583 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ On a server, the server's settings are sent to the players. With [Jade](https://modrinth.com/mod/jade) installed, looking at a light shows its mode and whether it is inverted. Jade is not required. +### Optional: NEEPMeat + +On Fabric 1.20.1, [NEEPMeat](https://modrinth.com/mod/neepmeat)'s vascular conduits power the electric lights, 1 eJ per energy unit. +A full light takes no power from the network. + ## Requirements - Fabric: Minecraft 1.20.1 (Java 17), 1.21.1 or 1.21.11 (Java 21), with [Fabric Loader](https://fabricmc.net/) and [Fabric API](https://modrinth.com/mod/fabric-api) diff --git a/build.fabric.gradle.kts b/build.fabric.gradle.kts index e97530f..a72a89d 100644 --- a/build.fabric.gradle.kts +++ b/build.fabric.gradle.kts @@ -1,3 +1,5 @@ +import java.util.concurrent.Callable + // Fabric nodes (Loom). Shared logic (Java, metadata templating, tests, publishing) is in buildSrc/mod-common. plugins { id("mod-common") @@ -67,6 +69,28 @@ dependencies { modCompileOnly("maven.modrinth:jade:${property("jade_version")}") modLocalRuntime("maven.modrinth:jade:${property("jade_version")}") + // NEEPMeat (1.20.1 only; the other nodes have no neepmeat_version): OMLib's FabricNeepMeatPower lets its vascular + // network charge the electric lights. Not bundled, the mod needs nothing of it; NEEPMeat (with the mods nested in its + // jar, which Loom leaves out of its remapped copy: nestedModJars in omlib/build-logic) and GeckoLib (which it + // requires, also with the library nested in its jar) are in the dev runs and on the game test server, where + // gametest/platform/fabric/NeepMeatGameTest checks a floodlight + findProperty("neepmeat_version")?.let { version -> + val neepmeat = "maven.modrinth:neepmeat:$version" + modCompileOnly(neepmeat) + modLocalRuntime(neepmeat) + // resolved lazily, when Loom resolves modLocalRuntime (earlier, it would use the repository before Loom sets it up) + val geckolib = "maven.modrinth:geckolib:${property("geckolib_version")}" + modLocalRuntime(geckolib) + // GeckoLib nests a library too (mclib; without it the client's resource reload fails) + val nested = layout.buildDirectory.dir("nested-mods") + modLocalRuntime(files(Callable { + listOf(neepmeat, geckolib).flatMap { notation -> + val jar = configurations.detachedConfiguration(dependencies.create(notation)).apply { isTransitive = false }.singleFile + nestedModJars(jar, nested.get().asFile) + } + })) + } + // Architecture rules (src/test/.../architecture): they read the compiled main and client classes testImplementation("com.tngtech.archunit:archunit-junit5:${property("archunit_version")}") testRuntimeOnly(sourceSets["client"].output) @@ -75,6 +99,14 @@ dependencies { testImplementation("nl.jqno.equalsverifier:equalsverifier:${property("equalsverifier_version")}") } +// NeepMeatGameTest exists only where NEEPMeat does (src/gametest/mc1.20.1-1.20.1): the other nodes drop its line from +// the test mod's fabric-gametest entrypoints +if (findProperty("neepmeat_version") == null) { + tasks.named("processGametestResources") { + filesMatching("fabric.mod.json") { filter { line -> if ("NeepMeatGameTest" in line) null else line } } + } +} + // ---- Test coverage (JaCoCo): check prints the line coverage of core ---- // Both the unit tests and the game test server record coverage. Active version only (all Fabric nodes share the source). // Report: versions//build/reports/jacoco/test/html/index.html diff --git a/docs/architecture.md b/docs/architecture.md index 5a12b37..85f7b1f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -211,6 +211,7 @@ Outside access goes through the loader: The adapters are all OMLib's: - Energy: `core.energy.EnergyAccess`, exposed as Team Reborn Energy on Fabric (`FabricEnergyStorages`) and as an FE capability on NeoForge (`NeoForgeEnergyHandler`; on 1.21.11 an `EnergyHandler` of NeoForge's transfer API, in OMLib's version folder `src/main/mc1.21.11-`) and Forge (`ForgeEnergyHandler`). + On Fabric 1.20.1 with NEEPMeat installed, OMLib also makes the lights sinks of its vascular network (`FabricNeepMeatPower`, 1 eJ = 1 E). Only inserting is possible; there is no extraction. - Items: `inventory.ItemAccess.insertOnly`, exposed as `ItemStorage.SIDED` on Fabric (`FabricItemStorages`) and as `IItemHandler` on NeoForge and Forge (NeoForge 1.21.11: `ResourceHandler`). The NeoForge capabilities are registered by `NeoForgeCapabilities`, one class per version folder of OMLib. diff --git a/omlib b/omlib index f3afdaf..552f6b6 160000 --- a/omlib +++ b/omlib @@ -1 +1 @@ -Subproject commit f3afdaf453f2ec5eeb1e9c7cf48bc63bd7dbe885 +Subproject commit 552f6b67c864739e281bc05cc6f17f314da17cdf diff --git a/src/gametest/mc1.20.1-1.20.1/java/omtteam/openmodularlighting/gametest/platform/fabric/NeepMeatGameTest.java b/src/gametest/mc1.20.1-1.20.1/java/omtteam/openmodularlighting/gametest/platform/fabric/NeepMeatGameTest.java new file mode 100644 index 0000000..50d95c2 --- /dev/null +++ b/src/gametest/mc1.20.1-1.20.1/java/omtteam/openmodularlighting/gametest/platform/fabric/NeepMeatGameTest.java @@ -0,0 +1,70 @@ +package omtteam.openmodularlighting.gametest.platform.fabric; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import omtteam.omlib.core.energy.EnergyStore; +import omtteam.openmodularlighting.block.FloodlightBlock; +import omtteam.openmodularlighting.blockentity.ElectricFloodlightBlockEntity; +import omtteam.openmodularlighting.gametest.OMLGameTests; +import omtteam.openmodularlighting.registry.ModBlocks; + +/** + * NEEPMeat (Fabric 1.20.1 only): its vascular network charges an electric floodlight at 1 eJ = 1 E (OMLib's + * {@code FabricNeepMeatPower}; the sink's rules are tested in OpenModularTurrets). Only this node has NEEPMeat on the + * game test server; the other Fabric nodes leave the class out of the test mod's entrypoints. + */ +@SuppressWarnings("unused") +public class NeepMeatGameTest implements OMLGameTests { + private static final BlockPos EMITTER = new BlockPos(1, 1, 3); + private static final BlockPos CONDUIT = EMITTER.east(); + private static final BlockPos LIGHT = CONDUIT.east(); + /** Power of the emitter, eJ per tick. */ + private static final int EMITTER_POWER = 1000; + + /** + * Power emitter, vascular conduit, electric floodlight (off, so it uses nothing): the conduit finds the light as a + * sink when placed, and the light is charged with the emitter's whole output, in whole ticks of it. + */ + @GameTest(template = EMPTY_STRUCTURE, timeoutTicks = 200) + public void vascularNetworkChargesAFloodlight(GameTestHelper helper) { + helper.setBlock(EMITTER, block("power_emitter")); + // its saved power (the block entity's class extends one of a library nested in NEEPMeat's jar, not on the + // compile classpath) + CompoundTag power = new CompoundTag(); + power.putInt("power", EMITTER_POWER); + helper.getBlockEntity(EMITTER).load(power); + helper.setBlock(LIGHT, ModBlocks.ELECTRIC_FLOODLIGHT.defaultBlockState().setValue(FloodlightBlock.FACING, Direction.UP)); + EnergyStore energy = ((ElectricFloodlightBlockEntity) helper.getBlockEntity(LIGHT)).getEnergyStore(); + placeAsPlayer(helper, CONDUIT, block("vascular_conduit")); + helper.succeedWhen(() -> { + long amount = energy.getAmount(); + helper.assertTrue(amount >= EMITTER_POWER * 20L, "The floodlight holds only " + amount + " E"); + helper.assertTrue(amount % EMITTER_POWER == 0, + "The floodlight holds " + amount + " E, not whole ticks of " + EMITTER_POWER + " eJ"); + helper.setBlock(CONDUIT, Blocks.AIR); + helper.setBlock(EMITTER, Blocks.AIR); + helper.setBlock(LIGHT, Blocks.AIR); + }); + } + + private static Block block(String id) { + return BuiltInRegistries.BLOCK.get(new ResourceLocation("neepmeat", id)); + } + + /** Places a block like a player does: its state from its neighbours, then its placement callback. */ + private static void placeAsPlayer(GameTestHelper helper, BlockPos pos, Block block) { + BlockPos absolute = helper.absolutePos(pos); + BlockState state = Block.updateFromNeighbourShapes(block.defaultBlockState(), helper.getLevel(), absolute); + helper.getLevel().setBlockAndUpdate(absolute, state); + block.setPlacedBy(helper.getLevel(), absolute, state, null, ItemStack.EMPTY); + } +} diff --git a/src/gametest/resources/fabric.mod.json b/src/gametest/resources/fabric.mod.json index 998c229..30786bb 100644 --- a/src/gametest/resources/fabric.mod.json +++ b/src/gametest/resources/fabric.mod.json @@ -17,6 +17,7 @@ "omtteam.openmodularlighting.gametest.UvFloodlightGameTest", "omtteam.openmodularlighting.gametest.GrowLightGameTest", "omtteam.openmodularlighting.gametest.JadeTooltipGameTest", + "omtteam.openmodularlighting.gametest.platform.fabric.NeepMeatGameTest", "omtteam.openmodularlighting.gametest.CamoGameTest" ] }, diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 4bce6aa..967e91a 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -37,6 +37,7 @@ "omlib": ">=${omlib_version}" }, "suggests": { - "jade": "*" + "jade": "*", + "neepmeat": "*" } } diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index af20882..e1da326 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -4,3 +4,7 @@ fabric_api_version=0.92.12+1.20.1 java_version=17 team_reborn_energy_version=3.0.0 jade_version=11.13.3+fabric +# NEEPMeat (Fabric, 1.20.1 only; Modrinth): its vascular network charges the electric lights (OMLib); in the dev runs and +# on the game test server with GeckoLib, which it requires (4.8.4 Fabric) +neepmeat_version=0.34.0-beta+1.20.1 +geckolib_version=SD2lOScS