diff --git a/core-api/build.gradle.kts b/core-api/build.gradle.kts index 8a0d0719..acd51912 100644 --- a/core-api/build.gradle.kts +++ b/core-api/build.gradle.kts @@ -6,7 +6,7 @@ val versionMain: String = System.getenv("VERSION") ?: "9.9.9" plugins { id("java-library") id("maven-publish") - alias(neoforged.plugins.moddev) + alias(neoforged.plugins.neogradle) } sourceSets { @@ -24,6 +24,7 @@ base { } java { + toolchain.vendor.set(JvmVendorSpec.JETBRAINS) toolchain.languageVersion.set(JavaLanguageVersion.of(21)) withJavadocJar() } diff --git a/core-api/src/main/java/dev/compactmods/machines/api/room/RoomTranslations.java b/core-api/src/main/java/dev/compactmods/machines/api/room/RoomTranslations.java index e2b25347..7b5e416a 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/room/RoomTranslations.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/room/RoomTranslations.java @@ -24,7 +24,7 @@ public interface RoomTranslations { Function PLAYER_NOT_IN_COMPACT_DIM = (player) -> Component.translatableWithFallback(IDs.Errors.PLAYER_NOT_IN_COMPACT_DIM, "", player.getDisplayName()); Function UNKNOWN_ROOM_BY_PLAYER_CHUNK = (player) -> Component - .translatableWithFallback(IDs.Errors.UNKNOWN_ROOM_BY_PLAYER_CHUNK, "Room not found at chunk: %s", player.chunkPosition()) + .translatableWithFallback(IDs.Errors.UNKNOWN_ROOM_BY_PLAYER_CHUNK, "Room not found at chunk: %s", player.chunkPosition().toString()) .withStyle(ChatFormatting.DARK_RED); Function UNKNOWN_ROOM_BY_CODE = (roomCode) -> Component diff --git a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgrade.java b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgrade.java index c209e330..13c7ae05 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgrade.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgrade.java @@ -1,33 +1,13 @@ package dev.compactmods.machines.api.room.upgrade; -import com.mojang.serialization.Codec; -import dev.compactmods.machines.api.CompactMachines; import dev.compactmods.machines.api.room.upgrade.events.RoomUpgradeEvent; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; import net.minecraft.world.item.component.TooltipProvider; import java.util.stream.Stream; public interface RoomUpgrade extends TooltipProvider { - Codec DISPATCH_CODEC = Codec.lazyInitialized(() -> { - @SuppressWarnings("unchecked") final var reg = (Registry>) BuiltInRegistries.REGISTRY.get(CompactMachines.modRL("room_upgrades")); - - if (reg != null) { - var upgradeRegistry = reg.byNameCodec(); - return upgradeRegistry.dispatchStable(RoomUpgrade::getType, RoomUpgradeDefinition::codec); - } - - throw new RuntimeException("Room upgrade registry not registered yet; calling too early?"); - }); - - RoomUpgradeDefinition getType(); - - StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(DISPATCH_CODEC); + RoomUpgradeDefinition getType(); default Stream gatherEvents() { return Stream.empty(); diff --git a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeCodecs.java b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeCodecs.java new file mode 100644 index 00000000..65cb9060 --- /dev/null +++ b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeCodecs.java @@ -0,0 +1,23 @@ +package dev.compactmods.machines.api.room.upgrade; + +import com.mojang.serialization.Codec; +import dev.compactmods.machines.api.CompactMachines; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +public interface RoomUpgradeCodecs { + Codec DISPATCH_CODEC = Codec.lazyInitialized(() -> { + @SuppressWarnings("unchecked") final var reg = (Registry>) BuiltInRegistries.REGISTRY.get(CompactMachines.modRL("room_upgrades")); + + if (reg != null) { + var upgradeRegistry = reg.byNameCodec(); + return upgradeRegistry.dispatchStable(RoomUpgrade::getType, RoomUpgradeDefinition::codec); + } + + throw new RuntimeException("Room upgrade registry not registered yet; calling too early?"); + }); + StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(DISPATCH_CODEC); +} diff --git a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeDefinition.java b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeDefinition.java index e7002da4..fcba670b 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeDefinition.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/RoomUpgradeDefinition.java @@ -9,5 +9,4 @@ public record RoomUpgradeDefinition(MapCodec codec) { public static final ResourceKey>> REG_KEY = ResourceKey .createRegistryKey(CompactMachines.modRL("room_upgrades")); - } \ No newline at end of file diff --git a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/components/RoomUpgradeList.java b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/components/RoomUpgradeList.java index 3769c700..c815844a 100644 --- a/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/components/RoomUpgradeList.java +++ b/core-api/src/main/java/dev/compactmods/machines/api/room/upgrade/components/RoomUpgradeList.java @@ -2,6 +2,7 @@ import com.mojang.serialization.Codec; import dev.compactmods.machines.api.room.upgrade.RoomUpgrade; +import dev.compactmods.machines.api.room.upgrade.RoomUpgradeCodecs; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.ByteBufCodecs; @@ -21,11 +22,11 @@ */ public record RoomUpgradeList(List upgrades) implements TooltipProvider { - public static final Codec CODEC = RoomUpgrade.DISPATCH_CODEC.listOf() + public static final Codec CODEC = RoomUpgradeCodecs.DISPATCH_CODEC.listOf() .xmap(RoomUpgradeList::new, RoomUpgradeList::upgrades); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - RoomUpgrade.STREAM_CODEC.apply(ByteBufCodecs.list()), RoomUpgradeList::upgrades, + RoomUpgradeCodecs.STREAM_CODEC.apply(ByteBufCodecs.list()), RoomUpgradeList::upgrades, RoomUpgradeList::new ); diff --git a/gradle.properties b/gradle.properties index 8a1da38d..7954b942 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,8 +6,8 @@ org.gradle.parallel=true # org.gradle.caching=true # org.gradle.configuration-cache=true -neoForge.parchment.minecraftVersion=1.20.6 -neoForge.parchment.mappingsVersion=2024.05.01 +neogradle.subsystems.parchment.minecraftVersion=1.21 +neogradle.subsystems.parchment.mappingsVersion=2024.07.28 neogradle.subsystems.conventions.runs.create-default-run-per-type=false diff --git a/neoforge-datagen/build.gradle.kts b/neoforge-datagen/build.gradle.kts index 46e03270..8f97952a 100644 --- a/neoforge-datagen/build.gradle.kts +++ b/neoforge-datagen/build.gradle.kts @@ -5,7 +5,7 @@ plugins { id("eclipse") id("idea") id("maven-publish") - alias(neoforged.plugins.moddev) + alias(neoforged.plugins.neogradle) } val modId: String = "compactmachines" @@ -17,6 +17,7 @@ project.evaluationDependsOn(coreApi.path) project.evaluationDependsOn(mainProject.path) java { + toolchain.vendor.set(JvmVendorSpec.JETBRAINS) toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } @@ -44,10 +45,10 @@ runs { // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. systemProperty("forge.enabledGameTestNamespaces", modId) - programArguments.addAll("--mod", modId) - programArguments.addAll("--all") - programArguments.addAll("--output", mainProject.file("src/generated/resources").absolutePath) - programArguments.addAll("--existing", mainProject.file("src/main/resources").absolutePath) + arguments.addAll("--mod", modId) + arguments.addAll("--all") + arguments.addAll("--output", mainProject.file("src/generated/resources").absolutePath) + arguments.addAll("--existing", mainProject.file("src/main/resources").absolutePath) } } @@ -61,19 +62,13 @@ repositories { password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN") } } - - // https://github.com/neoforged/NeoForge/pull/1303 - maven("https://prmaven.neoforged.net/NeoForge/pr1303") { - content { - includeModule("net.neoforged", "neoforge") - } - } } dependencies { - implementation("net.neoforged:neoforge:21.0.159-pr-1303-feat-recipe-provider-lookup") compileOnly(coreApi) compileOnly(mainProject) + + compileOnly(neoforged.neoforge) } tasks.compileJava { diff --git a/neoforge-datagen/src/main/java/dev.compactmods.machines.datagen/lang/EnglishLangGenerator.java b/neoforge-datagen/src/main/java/dev.compactmods.machines.datagen/lang/EnglishLangGenerator.java index c03f42e7..16e2c4aa 100644 --- a/neoforge-datagen/src/main/java/dev.compactmods.machines.datagen/lang/EnglishLangGenerator.java +++ b/neoforge-datagen/src/main/java/dev.compactmods.machines.datagen/lang/EnglishLangGenerator.java @@ -37,7 +37,7 @@ protected void addTranslations() { // Room Translations add(RoomTranslations.IDs.ROOM_SPAWNPOINT_SET, "New spawn point set."); add(RoomTranslations.IDs.MACHINE_ROOM_INFO, "Machine at %1$s is bound to a %2$s size room at %3$s"); - add(RoomTranslations.IDs.PLAYER_ROOM_INFO, "Player '%1$s' is inside a %3$s room at %2$s."); + add(RoomTranslations.IDs.PLAYER_ROOM_INFO, "Player '%1$s' is inside room %2$s."); // Room Errors add(RoomTranslations.IDs.Errors.CANNOT_ENTER_ROOM, "You fumble with the shrinking device, to no avail. It refuses to work."); diff --git a/neoforge-main/build.gradle.kts b/neoforge-main/build.gradle.kts index 87c6bed9..067ec0f7 100644 --- a/neoforge-main/build.gradle.kts +++ b/neoforge-main/build.gradle.kts @@ -1,252 +1,270 @@ -@file:Suppress("SpellCheckingInspection") - -import java.text.SimpleDateFormat -import java.util.* - -var envVersion: String = System.getenv("VERSION") ?: "9.9.9" -if (envVersion.startsWith("v")) - envVersion = envVersion.trimStart('v') - -val modId: String = "compactmachines" -val isRelease: Boolean = (System.getenv("RELEASE") ?: "false").equals("true", true) - -val coreApi = project(":core-api") - -plugins { - java - id("idea") - id("eclipse") - id("maven-publish") - alias(neoforged.plugins.moddev) -} - -project.evaluationDependsOn(coreApi.path) - -base { - archivesName.set(modId) - group = "dev.compactmods.compactmachines" - version = envVersion -} - -java { - // toolchain.vendor.set(JvmVendorSpec.JETBRAINS) - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) -} - -sourceSets.main { - java { - srcDir("src/main/java") - } - - resources { - srcDir("src/main/resources") - srcDir("src/generated/resources") - } -} - -sourceSets.test { - java { - srcDir("src/test/java") - } - - resources { - srcDir("src/test/resources") - } -} - -minecraft { - this.modIdentifier = modId - this.accessTransformers { - this.file(project.file("src/main/resources/META-INF/accesstransformer.cfg")) - } -} - -runs { - configureEach { - this.modSource(coreApi.sourceSets.main.get()) - - dependencies { - runtime(libraries.feather) - runtime(libraries.jnanoid) - } - -// if (!System.getenv().containsKey("CI")) { -// // JetBrains Runtime Hotswap -// // jvmArgument("-XX:+AllowEnhancedClassRedefinition") -// } - } - - this.create("client") { - client() - - this.workingDirectory.set(file("runs/client")) - - // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. - systemProperty("forge.enabledGameTestNamespaces", modId) - - programArguments.addAll("--username", "Nano") - programArguments.addAll("--width", "1920") - programArguments.addAll("--height", "1080") - } - - create("server") { - server() - workingDirectory.set(file("runs/server")) - - systemProperty("forge.enabledGameTestNamespaces", modId) - programArgument("nogui") - - environmentVariable("CM_TEST_RESOURCES", file("src/test/resources").path) - - sourceSets.add(project.sourceSets.test.get()) - } - - create("gameTestServer") { - gameTest() - workingDirectory.set(file("runs/gametest")) - - systemProperty("forge.enabledGameTestNamespaces", modId) - environmentVariable("CM_TEST_RESOURCES", file("src/test/resources").path) - - sourceSets.add(project.sourceSets.test.get()) - } -} - -repositories { - mavenLocal() - mavenCentral { - name = "Central" - content { - includeGroup("com.aventrix.jnanoid") - } - } - - maven("https://maven.pkg.github.com/compactmods/feather") { - name = "Github PKG Core" - credentials { - username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") - password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN") - } - - content { - includeGroup("dev.compactmods") - } - } - - maven("https://maven.blamejared.com/") { - // location of the maven that hosts JEI files since January 2023 - name = "Jared's maven" - } - - maven("https://www.cursemaven.com") { - content { - includeGroup("curse.maven") - } - } - - maven("https://modmaven.dev") { - // location of a maven mirror for JEI files, as a fallback - name = "ModMaven" - } -} - -dependencies { - // Core Projects and Libraries - this { - compileOnly(libraries.jnanoid) - jarJar(libraries.jnanoid) - - compileOnly(coreApi) - testCompileOnly(coreApi) - jarJar(coreApi) - - compileOnly(libraries.feather) - jarJar(libraries.feather) { isTransitive = false } - } - - implementation(neoforged.neoforge) - - runtimeOnly(neoforged.testframework) - testImplementation(neoforged.testframework) - testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") - - - // Mods -// compileOnly(mods.bundles.jei) -// compileOnly(mods.jade) -} - -tasks.withType { - useJUnitPlatform() -} - -tasks.withType { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -tasks.withType { - options.encoding = "UTF-8" - options.compilerArgs.addAll(arrayOf("-Xmaxerrs", "9000")) -} - -tasks.withType { - - val gitVersion = providers.exec { - commandLine("git", "rev-parse", "HEAD") - }.standardOutput.asText.get() - - manifest { - val now = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(Date()) - attributes( - mapOf( - "Specification-Title" to "Compact Machines", - "Specification-Vendor" to "CompactMods", - "Specification-Version" to "2", - "Implementation-Title" to "Compact Machines", - "Implementation-Version" to archiveVersion, - "Implementation-Vendor" to "CompactMods", - "Implementation-Timestamp" to now, - "Minecraft-Version" to mojang.versions.minecraft.get(), - "NeoForge-Version" to neoforged.versions.neoforge.get(), - "Main-Commit" to gitVersion - ) - ) - } -} - -tasks.withType().configureEach { - var replaceProperties: Map = mapOf( - "minecraft_version" to mojang.versions.minecraft.get(), - "neo_version" to neoforged.versions.neoforge.get(), - "minecraft_version_range" to mojang.versions.minecraftRange.get(), - "neo_version_range" to neoforged.versions.neoforgeRange.get(), - "loader_version_range" to "[1,)", - "mod_id" to modId, - "mod_version" to envVersion - ) - - inputs.properties(replaceProperties) - filesMatching("META-INF/neoforge.mods.toml") { - expand(replaceProperties) - } -} - -val PACKAGES_URL = System.getenv("GH_PKG_URL") ?: "https://maven.pkg.github.com/compactmods/compactmachines" -publishing { - publications.register("compactmachines") { - artifactId = "$modId-neoforge" - from(components.getByName("java")) - } - - repositories { - // GitHub Packages - maven(PACKAGES_URL) { - name = "GitHubPackages" - credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") - } - } - } +@file:Suppress("SpellCheckingInspection") + +import org.slf4j.event.Level +import java.text.SimpleDateFormat +import java.util.* + +var envVersion: String = System.getenv("VERSION") ?: "9.9.9" +if (envVersion.startsWith("v")) + envVersion = envVersion.trimStart('v') + +val modId: String = "compactmachines" +val isRelease: Boolean = (System.getenv("RELEASE") ?: "false").equals("true", true) + +val coreApi = project(":core-api") + +plugins { + java + id("idea") + id("eclipse") + id("maven-publish") + alias(neoforged.plugins.moddev) +} + +project.evaluationDependsOn(coreApi.path) + +base { + archivesName.set(modId) + group = "dev.compactmods.compactmachines" + version = envVersion +} + +java { + toolchain.vendor.set(JvmVendorSpec.JETBRAINS) + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +sourceSets.main { + java { + srcDir("src/main/java") + } + + resources { + srcDir("src/main/resources") + srcDir("src/generated/resources") + } +} + +sourceSets.test { + java { + srcDir("src/test/java") + } + + resources { + srcDir("src/test/resources") + } +} + +neoForge { + version = neoforged.versions.neoforge + + this.mods.create(modId) { + modSourceSets.add(sourceSets.main) + modSourceSets.add(sourceSets.test) + this.dependency(coreApi) + } + + unitTest { + enable() + testedMod = mods.named(modId) + } + + runs { + // applies to all the run configs below + configureEach { + + logLevel.set(Level.DEBUG) + + sourceSet = project.sourceSets.main + + if (!System.getenv().containsKey("CI")) { + // JetBrains Runtime Hotswap + // jvmArgument("-XX:+AllowEnhancedClassRedefinition") + } + } + + create("client") { + client() + gameDirectory.set(file("runs/client")) + + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty("forge.enabledGameTestNamespaces", modId) + + programArguments.addAll("--username", "Nano") + programArguments.addAll("--width", "1920") + programArguments.addAll("--height", "1080") + } + + create("client2") { + client() + gameDirectory.set(file("runs/client")) + + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty("forge.enabledGameTestNamespaces", modId) + + programArguments.addAll("--username", "Nano2") + programArguments.addAll("--width", "1920") + programArguments.addAll("--height", "1080") + } + + create("server") { + server() + gameDirectory.set(file("runs/server")) + + systemProperty("forge.enabledGameTestNamespaces", modId) + programArgument("nogui") + + environment.put("CM_TEST_RESOURCES", file("src/test/resources").path) + + sourceSet = project.sourceSets.test + // sourceSets.add(project.sourceSets.test.get()) + } + + create("gameTestServer") { + type = "gameTestServer" + gameDirectory.set(file("runs/gametest")) + + systemProperty("forge.enabledGameTestNamespaces", modId) + environment.put("CM_TEST_RESOURCES", file("src/test/resources").path) + + sourceSet = project.sourceSets.test + // sourceSets.add(project.sourceSets.test.get()) + } + } +} + +repositories { + mavenLocal() + mavenCentral { + name = "Central" + content { + includeGroup("com.aventrix.jnanoid") + } + } + + maven("https://maven.pkg.github.com/compactmods/compactmachines-core") { + name = "Github PKG Core" + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN") + } + } + + maven("https://maven.blamejared.com/") { + // location of the maven that hosts JEI files since January 2023 + name = "Jared's maven" + } + + maven("https://www.cursemaven.com") { + content { + includeGroup("curse.maven") + } + } + + maven("https://modmaven.dev") { + // location of a maven mirror for JEI files, as a fallback + name = "ModMaven" + } +} + +dependencies { + // Core Projects and Libraries + this { + compileOnly(libraries.jnanoid) + testImplementation(libraries.jnanoid) + jarJar(libraries.jnanoid) + + compileOnly(coreApi) + testCompileOnly(coreApi) + + compileOnly(libraries.feather) + testImplementation(libraries.feather) + jarJar(libraries.feather) { isTransitive = false } + } + + runtimeOnly(neoforged.testframework) + testImplementation(neoforged.testframework) + testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + additionalRuntimeClasspath(libraries.feather) + additionalRuntimeClasspath(libraries.jnanoid) + + // Mods +// compileOnly(mods.bundles.jei) +// compileOnly(mods.jade) +} + +tasks.withType { + useJUnitPlatform() +} + +tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +tasks.withType { + options.encoding = "UTF-8" + options.compilerArgs.addAll(arrayOf("-Xmaxerrs", "9000")) +} + +tasks.withType { + + val gitVersion = providers.exec { + commandLine("git", "rev-parse", "HEAD") + }.standardOutput.asText.get() + + manifest { + val now = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(Date()) + attributes( + mapOf( + "Specification-Title" to "Compact Machines", + "Specification-Vendor" to "CompactMods", + "Specification-Version" to "2", + "Implementation-Title" to "Compact Machines", + "Implementation-Version" to archiveVersion, + "Implementation-Vendor" to "CompactMods", + "Implementation-Timestamp" to now, + "Minecraft-Version" to mojang.versions.minecraft.get(), + "NeoForge-Version" to neoforged.versions.neoforge.get(), + "Main-Commit" to gitVersion + ) + ) + } +} + +tasks.withType().configureEach { + var replaceProperties: Map = mapOf( + "minecraft_version" to mojang.versions.minecraft.get(), + "neo_version" to neoforged.versions.neoforge.get(), + "minecraft_version_range" to mojang.versions.minecraftRange.get(), + "neo_version_range" to neoforged.versions.neoforgeRange.get(), + "loader_version_range" to "[1,)", + "mod_id" to modId, + "mod_version" to envVersion + ) + + inputs.properties(replaceProperties) + filesMatching("META-INF/neoforge.mods.toml") { + expand(replaceProperties) + } +} + +val PACKAGES_URL = System.getenv("GH_PKG_URL") ?: "https://maven.pkg.github.com/compactmods/compactmachines" +publishing { + publications.register("compactmachines") { + artifactId = "$modId-neoforge" + from(components.getByName("java")) + } + + repositories { + // GitHub Packages + maven(PACKAGES_URL) { + name = "GitHubPackages" + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } } \ No newline at end of file diff --git a/neoforge-main/build.neogradle.gradle.kts.disabled b/neoforge-main/build.neogradle.gradle.kts.disabled new file mode 100644 index 00000000..9806422d --- /dev/null +++ b/neoforge-main/build.neogradle.gradle.kts.disabled @@ -0,0 +1,257 @@ +@file:Suppress("SpellCheckingInspection") + +import java.text.SimpleDateFormat +import java.util.* + +var envVersion: String = System.getenv("VERSION") ?: "9.9.9" +if (envVersion.startsWith("v")) + envVersion = envVersion.trimStart('v') + +val modId: String = "compactmachines" +val isRelease: Boolean = (System.getenv("RELEASE") ?: "false").equals("true", true) + +val coreApi = project(":core-api") + +plugins { + java + id("idea") + id("eclipse") + id("maven-publish") + alias(neoforged.plugins.moddev) +} + +base { + archivesName.set(modId) + group = "dev.compactmods.compactmachines" + version = envVersion +} + +java { + toolchain.vendor.set(JvmVendorSpec.JETBRAINS) + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +sourceSets.main { + java { + srcDir("src/main/java") + } + + resources { + srcDir("src/main/resources") + srcDir("src/generated/resources") + } +} + +sourceSets.test { + java { + srcDir("src/test/java") + } + + resources { + srcDir("src/test/resources") + } +} + +minecraft { + this.modIdentifier = modId + this.accessTransformers { + this.file(project.file("src/main/resources/META-INF/accesstransformer.cfg")) + } +} + +runs { + configureEach { + this.modSource(coreApi.sourceSets.main.get()) + + dependencies { + runtime(libraries.feather) + runtime(libraries.jnanoid) + } + +// if (!System.getenv().containsKey("CI")) { +// // JetBrains Runtime Hotswap +// // jvmArgument("-XX:+AllowEnhancedClassRedefinition") +// } + } + + this.create("client") { + client() + + this.workingDirectory.set(file("runs/client")) + + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty("forge.enabledGameTestNamespaces", modId) + + arguments.addAll("--username", "Nano") + arguments.addAll("--width", "1920") + arguments.addAll("--height", "1080") + } + + create("server") { + server() + workingDirectory.set(file("runs/server")) + + systemProperty("forge.enabledGameTestNamespaces", modId) + argument("nogui") + + environmentVariable("CM_TEST_RESOURCES", file("src/test/resources").path) + + sourceSets.add(project.sourceSets.test.get()) + } + + create("gameTestServer") { + gameTest() + workingDirectory.set(file("runs/gametest")) + + systemProperty("forge.enabledGameTestNamespaces", modId) + environmentVariable("CM_TEST_RESOURCES", file("src/test/resources").path) + + sourceSets.add(project.sourceSets.test.get()) + } + + create("junit") { + junit() + workingDirectory.set(file("runs/junit")) + + this.isJUnit = true + } +} + +repositories { + mavenLocal() + mavenCentral { + name = "Central" + content { + includeGroup("com.aventrix.jnanoid") + } + } + + maven("https://maven.pkg.github.com/compactmods/feather") { + name = "Github PKG Core" + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN") + } + + content { + includeGroup("dev.compactmods") + } + } + + maven("https://maven.blamejared.com/") { + // location of the maven that hosts JEI files since January 2023 + name = "Jared's maven" + } + + maven("https://www.cursemaven.com") { + content { + includeGroup("curse.maven") + } + } + + maven("https://modmaven.dev") { + // location of a maven mirror for JEI files, as a fallback + name = "ModMaven" + } +} + +dependencies { + // Core Projects and Libraries + this { + compileOnly(libraries.jnanoid) + jarJar(libraries.jnanoid) + + compileOnly(coreApi) + testCompileOnly(coreApi) + jarJar(coreApi) + + compileOnly(libraries.feather) + jarJar(libraries.feather) { isTransitive = false } + } + + implementation(neoforged.neoforge) + + runtimeOnly(neoforged.testframework) + testImplementation(neoforged.testframework) + testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + + // Mods +// compileOnly(mods.bundles.jei) +// compileOnly(mods.jade) +} + +tasks.withType { + useJUnitPlatform() +} + +tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +tasks.withType { + options.encoding = "UTF-8" + options.compilerArgs.addAll(arrayOf("-Xmaxerrs", "9000")) +} + +tasks.withType { + + val gitVersion = providers.exec { + commandLine("git", "rev-parse", "HEAD") + }.standardOutput.asText.get() + + manifest { + val now = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(Date()) + attributes( + mapOf( + "Specification-Title" to "Compact Machines", + "Specification-Vendor" to "CompactMods", + "Specification-Version" to "2", + "Implementation-Title" to "Compact Machines", + "Implementation-Version" to archiveVersion, + "Implementation-Vendor" to "CompactMods", + "Implementation-Timestamp" to now, + "Minecraft-Version" to mojang.versions.minecraft.get(), + "NeoForge-Version" to neoforged.versions.neoforge.get(), + "Main-Commit" to gitVersion + ) + ) + } +} + +tasks.withType().configureEach { + var replaceProperties: Map = mapOf( + "minecraft_version" to mojang.versions.minecraft.get(), + "neo_version" to neoforged.versions.neoforge.get(), + "minecraft_version_range" to mojang.versions.minecraftRange.get(), + "neo_version_range" to neoforged.versions.neoforgeRange.get(), + "loader_version_range" to "[1,)", + "mod_id" to modId, + "mod_version" to envVersion + ) + + inputs.properties(replaceProperties) + filesMatching("META-INF/neoforge.mods.toml") { + expand(replaceProperties) + } +} + +val PACKAGES_URL = System.getenv("GH_PKG_URL") ?: "https://maven.pkg.github.com/compactmods/compactmachines" +publishing { + publications.register("compactmachines") { + artifactId = "$modId-neoforge" + from(components.getByName("java")) + } + + repositories { + // GitHub Packages + maven(PACKAGES_URL) { + name = "GitHubPackages" + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/client/keybinds/room/RoomUpgradeUIMapping.java b/neoforge-main/src/main/java/dev/compactmods/machines/client/keybinds/room/RoomUpgradeUIMapping.java index 3a05ccf5..cc41fa3c 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/client/keybinds/room/RoomUpgradeUIMapping.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/client/keybinds/room/RoomUpgradeUIMapping.java @@ -36,7 +36,7 @@ public static void handle() { final var player = Minecraft.getInstance().player; if (level != null && level.dimension().equals(CompactDimension.LEVEL_KEY)) { final var currentRoom = player.getData(Rooms.DataAttachments.CURRENT_ROOM_CODE); -// PacketDistributor.sendToServer(new PlayerRequestedUpgradeUIPacket(currentRoom, true)); + PacketDistributor.sendToServer(new PlayerRequestedUpgradeUIPacket(currentRoom, true)); } } } diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/command/Commands.java b/neoforge-main/src/main/java/dev/compactmods/machines/command/Commands.java index 30b6ce61..b1553292 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/command/Commands.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/command/Commands.java @@ -13,6 +13,7 @@ import dev.compactmods.machines.room.upgrade.RoomUpgrades; import dev.compactmods.machines.room.upgrade.example.TreeCutterUpgrade; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.tags.ItemTags; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -44,21 +45,19 @@ public static void onCommandsRegister(final RegisterCommandsEvent event) { Commands.CM_COMMAND_ROOT.then(CMGiveMachineSubcommand.make()); Commands.CM_COMMAND_ROOT.then(SpawnSubcommand.make()); -// CM_COMMAND_ROOT.then(net.minecraft.commands.Commands.literal("test").executes(ctx -> { -// final var player = ctx.getSource().getPlayerOrException(); -// -// final var diamondAxe = new ItemStack(Items.DIAMOND_AXE); -// -// final var treecutter = new TreeCutterUpgrade(); -// -// final var upgrades = new RoomUpgradeList(List.of(treecutter)); -// -// diamondAxe.set(RoomUpgrades.UPGRADE_LIST_COMPONENT, upgrades); -// -// player.addItem(diamondAxe); -// -// return 0; -// })); + CM_COMMAND_ROOT.then(net.minecraft.commands.Commands.literal("test").executes(ctx -> { + final var player = ctx.getSource().getPlayerOrException(); + + final var axe = player.getMainHandItem(); + + if(!axe.is(ItemTags.AXES)) + return -1; + + final var treecutter = new TreeCutterUpgrade(); + final var upgrades = new RoomUpgradeList(List.of(treecutter)); + axe.set(RoomUpgrades.UPGRADE_LIST_COMPONENT, upgrades); + return 0; + })); event.getDispatcher().register(Commands.CM_COMMAND_ROOT); } diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/command/subcommand/CMRoomsSubcommand.java b/neoforge-main/src/main/java/dev/compactmods/machines/command/subcommand/CMRoomsSubcommand.java index c9c602f9..507fdd07 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/command/subcommand/CMRoomsSubcommand.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/command/subcommand/CMRoomsSubcommand.java @@ -87,7 +87,7 @@ private static int fetchByChunkPos(CommandContext ctx) { .findRoomByChunk(chunkPos.toChunkPos()) // FIXME Translations - .map(code -> Component.translatableWithFallback("commands.cm.room_by_chunk", "Room at chunk %s has ID: %s", chunkPos, code)) + .map(code -> Component.translatableWithFallback("commands.cm.room_by_chunk", "Room at chunk %s has ID: %s", chunkPos.toString(), code)) .orElse(Component.literal("Room not found at chunk: " + chunkPos)); ctx.getSource().sendSuccess(() -> m, false); diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/network/CMNetworks.java b/neoforge-main/src/main/java/dev/compactmods/machines/network/CMNetworks.java index 4f697024..ffbc9566 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/network/CMNetworks.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/network/CMNetworks.java @@ -18,7 +18,7 @@ public static void onPacketRegistration(final RegisterPayloadHandlersEvent paylo main.playToServer(PlayerRequestedRoomUIPacket.TYPE, PlayerRequestedRoomUIPacket.STREAM_CODEC, PlayerRequestedRoomUIPacket.HANDLER); - // TODO Upgrades main.playToServer(PlayerRequestedUpgradeUIPacket.TYPE, PlayerRequestedUpgradeUIPacket.STREAM_CODEC, PlayerRequestedUpgradeUIPacket.HANDLER); + main.playToServer(PlayerRequestedUpgradeUIPacket.TYPE, PlayerRequestedUpgradeUIPacket.STREAM_CODEC, PlayerRequestedUpgradeUIPacket.HANDLER); main.playToClient(MachineColorSyncPacket.TYPE, MachineColorSyncPacket.STREAM_CODEC, MachineColorSyncPacket.HANDLER); } diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/player/PlayerEntryPointHistoryManager.java b/neoforge-main/src/main/java/dev/compactmods/machines/player/PlayerEntryPointHistoryManager.java index 41268b47..c1fe577d 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/player/PlayerEntryPointHistoryManager.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/player/PlayerEntryPointHistoryManager.java @@ -113,6 +113,7 @@ public void popHistory(Player player, int steps) { public Optional lastHistory(Player player) { final var lastEntry = latestEntryPoints.get(player.getUUID()); + if(lastEntry == null) return Optional.empty(); return graph.outboundEdges(lastEntry, RoomReferenceNode.class, PlayerRoomEntryEdge.class) .max(Comparator.comparing(PlayerRoomEntryEdge::entryTime)) .map(PlayerEntryPointHistoryManager::fromEdge); diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/room/RoomEventHandler.java b/neoforge-main/src/main/java/dev/compactmods/machines/room/RoomEventHandler.java index 5b4993b4..94f9b9e8 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/room/RoomEventHandler.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/room/RoomEventHandler.java @@ -38,15 +38,12 @@ public static void entityChangedDimensions(final EntityTravelToDimensionEvent di public static void entityJoined(final EntityJoinLevelEvent evt) { Entity ent = evt.getEntity(); - boolean isPlayer = ent instanceof Player; - boolean isCompact = CompactDimension.isLevelCompact(ent.level()); - // no-op clients and non-compact dimensions, we only care about server spawns - if (!isCompact || ent.level().isClientSide) + if (!CompactDimension.isLevelCompact(ent.level()) || ent.level().isClientSide) return; // Handle players - if (isPlayer && ent instanceof ServerPlayer serverPlayer) { + if (ent instanceof ServerPlayer serverPlayer) { // FIXME sync current room info to client player // final var roomProvider = CompactRoomProvider.instance(serverPlayer.getLevel()); // roomProvider.findByChunk(serverPlayer.chunkPosition()).ifPresent(roomInfo -> { @@ -68,7 +65,7 @@ public static void checkSpawn(final FinalizeSpawnEvent evt) { Entity ent = evt.getEntity(); // Early exit if spawning in non-CM dimensions - if (!ent.level().dimension().equals(CompactDimension.LEVEL_KEY)) return; + if (!CompactDimension.isLevelCompact(ent.level())) return; if (!positionInsideRoom(ent, target)) evt.setSpawnCancelled(true); @@ -94,7 +91,7 @@ public static void entityTeleport(final EntityTeleportEvent evt) { */ private static boolean positionInsideRoom(Entity entity, Vec3 target) { final var level = entity.level(); - if (!level.dimension().equals(CompactDimension.LEVEL_KEY)) return false; + if (!CompactDimension.isLevelCompact(entity.level())) return false; return CompactMachines.roomApi().chunkManager() .findRoomByChunk(entity.chunkPosition()) diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/room/ui/preview/MachineRoomScreen.java b/neoforge-main/src/main/java/dev/compactmods/machines/room/ui/preview/MachineRoomScreen.java index 0a9e43ca..8486304d 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/room/ui/preview/MachineRoomScreen.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/room/ui/preview/MachineRoomScreen.java @@ -68,7 +68,7 @@ protected void init() { PacketDistributor.sendToServer(new PlayerRequestedUpgradeUIPacket(menu.getRoom(), false)); }).build(); -// addRenderableWidget(upgradeScreenBtn); + addRenderableWidget(upgradeScreenBtn); } @Override diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/ItemStackHandlerCodec.java b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/ItemStackHandlerCodec.java new file mode 100644 index 00000000..2f68eb59 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/ItemStackHandlerCodec.java @@ -0,0 +1,12 @@ +package dev.compactmods.machines.room.upgrade.example; + +import com.mojang.datafixers.kinds.*; +import com.mojang.serialization.*; +import com.mojang.serialization.codecs.*; +import net.neoforged.neoforge.items.*; + +public class ItemStackHandlerCodec { + public static Codec codec(int slots) { + return null; + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/RoomItemBufferUpgrade.java b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/RoomItemBufferUpgrade.java new file mode 100644 index 00000000..2f1d84bc --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/RoomItemBufferUpgrade.java @@ -0,0 +1,39 @@ +package dev.compactmods.machines.room.upgrade.example; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.compactmods.machines.api.room.upgrade.RoomUpgrade; +import dev.compactmods.machines.api.room.upgrade.RoomUpgradeDefinition; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.TooltipFlag; +import net.neoforged.neoforge.items.ItemStackHandler; + +import java.util.function.Consumer; + +public class RoomItemBufferUpgrade implements RoomUpgrade { + + private final ItemStackHandler inventory; + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( + ItemStackHandlerCodec.codec(9).fieldOf("inventory").forGetter(x -> x.inventory) + ).apply(i, RoomItemBufferUpgrade::new)); + + public RoomItemBufferUpgrade() { + this.inventory = new ItemStackHandler(9); + } + + public RoomItemBufferUpgrade(ItemStackHandler inventory) { + this.inventory = inventory; + } + + @Override + public RoomUpgradeDefinition getType() { + return null; + } + + @Override + public void addToTooltip(Item.TooltipContext tooltipContext, Consumer consumer, TooltipFlag tooltipFlag) { + + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/TreeCutterUpgrade.java b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/TreeCutterUpgrade.java index cbf3b302..450be10d 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/TreeCutterUpgrade.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/room/upgrade/example/TreeCutterUpgrade.java @@ -1,74 +1,138 @@ package dev.compactmods.machines.room.upgrade.example; +import com.google.common.base.Objects; import com.mojang.serialization.MapCodec; import dev.compactmods.machines.api.room.RoomInstance; import dev.compactmods.machines.api.room.upgrade.RoomUpgrade; import dev.compactmods.machines.api.room.upgrade.RoomUpgradeDefinition; import dev.compactmods.machines.api.room.upgrade.events.RoomUpgradeEvent; import dev.compactmods.machines.api.room.upgrade.events.lifecycle.UpgradeTickedEventListener; +import dev.compactmods.machines.api.util.AABBHelper; +import dev.compactmods.machines.machine.Machines; import dev.compactmods.machines.room.upgrade.RoomUpgrades; +import dev.compactmods.machines.util.item.ItemHandlerScan; +import dev.compactmods.machines.util.item.ItemHandlerUtil; +import it.unimi.dsi.fastutil.Pair; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.util.CommonColors; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.ItemHandlerHelper; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; public class TreeCutterUpgrade implements RoomUpgrade { - public static final MapCodec CODEC = MapCodec.unit(TreeCutterUpgrade::new); - - @Override - public void addToTooltip(@NotNull Item.TooltipContext ctx, Consumer tooltips, @NotNull TooltipFlag flags) { - final var c = Component.literal("Tree Cutter") - .withColor(CommonColors.LIGHT_GRAY); - - tooltips.accept(c); - } - - @Override - public Stream gatherEvents() { - final UpgradeTickedEventListener ticker = TreeCutterUpgrade::onTick; - return Stream.of(ticker); - } - - @Override - public RoomUpgradeDefinition getType() { - return RoomUpgrades.TREECUTTER.get(); - } - - public static void onTick(ServerLevel level, RoomInstance room, ItemStack upgrade) { - final var innerBounds = room.boundaries().innerBounds(); - - final var everythingLoaded = room.boundaries() - .innerChunkPositions() - .allMatch(cp -> level.shouldTickBlocksAt(cp.toLong())); - - // TODO - Implement upgrade cooldowns (i.e. retry in 100 ticks if room isn't loaded) - if (!everythingLoaded) return; - - final var logs = BlockPos.betweenClosedStream(innerBounds) - .filter(pos -> { - final var state = level.getBlockState(pos); - return state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - }) - .map(BlockPos::immutable) - .limit(5) - .collect(Collectors.toUnmodifiableSet()); - - if (!logs.isEmpty()) { - logs.forEach(pos -> level.destroyBlock(pos, true)); - upgrade.hurtAndBreak(logs.size(), level, null, (item) -> { - upgrade.shrink(1); - }); - } - } + public static final MapCodec CODEC = MapCodec.unit(TreeCutterUpgrade::new); + + @Override + public void addToTooltip(@NotNull Item.TooltipContext ctx, Consumer tooltips, @NotNull TooltipFlag flags) { + final var c = Component.literal("Tree Cutter") + .withColor(CommonColors.LIGHT_GRAY); + + tooltips.accept(c); + } + + @Override + public Stream gatherEvents() { + final UpgradeTickedEventListener ticker = TreeCutterUpgrade::onTick; + return Stream.of(ticker); + } + + @Override + public RoomUpgradeDefinition getType() { + return RoomUpgrades.TREECUTTER.get(); + } + + public static void onTick(ServerLevel level, RoomInstance room, ItemStack upgrade) { + final var innerBounds = room.boundaries().innerBounds(); + + final var everythingLoaded = room.boundaries() + .innerChunkPositions() + .allMatch(cp -> level.shouldTickBlocksAt(cp.toLong())); + + // TODO - Implement upgrade cooldowns (i.e. retry in 100 ticks if room isn't loaded) + if (!everythingLoaded) return; + + var energyHandler = upgrade.getCapability(Capabilities.EnergyStorage.ITEM); + + boolean doItemDamage = false; + boolean preferEnergy = false; + int maxAllowed = 0; + if (upgrade.isDamageableItem()) { + doItemDamage = true; + var durabilityLeft = upgrade.getMaxDamage() - upgrade.getDamageValue(); + maxAllowed = Math.clamp(durabilityLeft, 0, 5); + } + + if (energyHandler != null && energyHandler.canExtract()) { + doItemDamage = true; + preferEnergy = true; + maxAllowed = Math.clamp(energyHandler.getEnergyStored() / 10, 0, 5); + } + + final var logs = BlockPos.betweenClosedStream(innerBounds) + .map(pos -> { + final var state = level.getBlockState(pos); + return Pair.of(pos.immutable(), state); + }) + .filter(pair -> pair.right().is(BlockTags.LOGS) || pair.right().is(BlockTags.LEAVES)) + .limit(maxAllowed) + .collect(Collectors.toUnmodifiableSet()); + + final var numLogs = logs.size(); + + if (!logs.isEmpty()) { + + final var bounds = room.boundaries().innerBounds(); + final var maybeChest = BlockPos.containing(AABBHelper.minCorner(bounds)); + + var itemHandler = level.getCapability(Capabilities.ItemHandler.BLOCK, maybeChest, null); + if (itemHandler == null) + itemHandler = level.getCapability(Capabilities.ItemHandler.BLOCK, maybeChest, Direction.UP); + + for (Pair pos : logs) { + final var blockEntity = level.getBlockEntity(pos.left()); + final var drops = Block.getDrops(pos.right(), level, pos.left(), blockEntity, null, upgrade); + level.destroyBlock(pos.left(), false); + + if (!drops.isEmpty()) { + if (itemHandler != null) { + var failed = ItemHandlerUtil.insertMultipleStacks(itemHandler, drops); + + for (var failedStack : failed) { + Block.popResource(level, maybeChest.above(), failedStack); + } + } else { + for (var failedStack : drops) { + Block.popResource(level, maybeChest.above(), failedStack); + } + } + } + } + + + if (preferEnergy) { + energyHandler.extractEnergy(numLogs * 10, false); + } else { + upgrade.hurtAndBreak(numLogs, level, null, (item) -> { + upgrade.shrink(1); + }); + } + } + } } diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/server/ServerConfig.java b/neoforge-main/src/main/java/dev/compactmods/machines/server/ServerConfig.java index 31a02be4..d6df5acd 100644 --- a/neoforge-main/src/main/java/dev/compactmods/machines/server/ServerConfig.java +++ b/neoforge-main/src/main/java/dev/compactmods/machines/server/ServerConfig.java @@ -55,8 +55,8 @@ private static void generateConfig() { DAMAGE_PLAYERS_OUTSIDE_MACHINE = builder .comment("Specify if we want to damage players that are outside the room boundaries") - .comment("default: false") - .define("damagePlayersOutOfBounds", false); + .comment("default: true") + .define("damagePlayersOutOfBounds", true); builder.pop(); diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/util/codec/ItemStackHandlerCodec.java b/neoforge-main/src/main/java/dev/compactmods/machines/util/codec/ItemStackHandlerCodec.java new file mode 100644 index 00000000..90fff9ba --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/machines/util/codec/ItemStackHandlerCodec.java @@ -0,0 +1,64 @@ +package dev.compactmods.machines.util.codec; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.RegistryOps; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.ItemStackHandler; + +public class ItemStackHandlerCodec implements Codec { + + public static final ItemStackHandlerCodec INSTANCE = new ItemStackHandlerCodec(); + + @Override + public DataResult> decode(DynamicOps ops, T input) { + if (!(ops instanceof RegistryOps regOps) || !(regOps.lookupProvider instanceof RegistryOps.HolderLookupAdapter adapter)) + return DataResult.error(() -> "Was not passed registry ops for serialization; cannot continue."); + + final var map = ops.getMap(input).getOrThrow(); + + int maxSize = Codec.INT.fieldOf("Size") + .decode(ops, map) + .getOrThrow(); + + ItemStackHandler handler = new ItemStackHandler(maxSize); + CompoundTag.CODEC.listOf() + .fieldOf("Items") + .decode(ops, map) + .ifSuccess(itemTags -> { + itemTags.forEach(itemTag -> { + int slot = itemTag.getInt("Slot"); + var stack = ItemStack.parseOptional(adapter.lookupProvider, itemTag); + handler.setStackInSlot(slot, stack); + }); + }); + + return DataResult.success(Pair.of(handler, input)); + } + + @Override + public DataResult encode(ItemStackHandler input, DynamicOps ops, T prefix) { + if (!(ops instanceof RegistryOps regOps) || !(regOps.lookupProvider instanceof RegistryOps.HolderLookupAdapter adapter)) + return DataResult.error(() -> "Was not passed registry ops for serialization; cannot continue."); + + var list = ops.listBuilder(); + for (int i = 0; i < input.getSlots(); i++) { + final var inSlot = input.getStackInSlot(i); + if (!inSlot.isEmpty()) { + CompoundTag itemTag = new CompoundTag(); + itemTag.putInt("Slot", i); + var encodedStack = inSlot.save(adapter.lookupProvider, itemTag); + if (encodedStack instanceof CompoundTag ct) + list.add(CompoundTag.CODEC.encode(ct, ops, ops.empty())); + } + } + + return ops.mapBuilder() + .add("Items", list.build(ops.empty())) + .add("Size", ops.createInt(input.getSlots())) + .build(prefix); + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerScan.java b/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerScan.java new file mode 100644 index 00000000..0d498737 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerScan.java @@ -0,0 +1,43 @@ +package dev.compactmods.machines.util.item; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.primitives.ImmutableIntArray; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.IItemHandler; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.Set; + +public record ItemHandlerScan(Set stacking, Set empty) { + + public static ItemHandlerScan scanInventory(IItemHandler inventory, ItemStack stack) { + var stackingBuilder = ImmutableSet.builder(); + var emptyBuilder = ImmutableSet.builder(); + for(int i = 0; i < inventory.getSlots(); i++) { + var inSlot = inventory.getStackInSlot(i); + if(inSlot.isEmpty()) { + emptyBuilder.add(i); + continue; + } + + if(!inSlot.isStackable()) + continue; + + if(!ItemStack.isSameItemSameComponents(inSlot, stack)) + continue; + + var canActuallyInsert = inventory.insertItem(i, stack.copyWithCount(1), true).isEmpty(); + if(canActuallyInsert) + stackingBuilder.add(i); + } + + return new ItemHandlerScan(stackingBuilder.build(), emptyBuilder.build()); + } + + public boolean hasSpaceAvailable() { + return !stacking.isEmpty() || !empty.isEmpty(); + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerUtil.java b/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerUtil.java new file mode 100644 index 00000000..2f5d14bc --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/machines/util/item/ItemHandlerUtil.java @@ -0,0 +1,55 @@ +package dev.compactmods.machines.util.item; + +import com.google.common.collect.ImmutableList; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.IItemHandler; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +public class ItemHandlerUtil { + + public static List insertMultipleStacks(IItemHandler itemHandler, List drops) { + Deque remainingDrops = new ArrayDeque<>(drops); + final var failedToPush = ImmutableList.builder(); + + while (!remainingDrops.isEmpty()) { + var workingStack = remainingDrops.pop(); + var scan = ItemHandlerScan.scanInventory(itemHandler, workingStack); + if (!scan.hasSpaceAvailable()) { + failedToPush.add(workingStack); + continue; + } + + // Try stackable slots first, then empty + var stackableSlots = new ArrayDeque(scan.stacking()); + stackableSlots.addAll(scan.empty()); + + boolean doneWithStack = false; + while(!doneWithStack) { + // No more space + if(stackableSlots.isEmpty()) { + failedToPush.addAll(remainingDrops); + remainingDrops.clear(); + break; + } + + var working = stackableSlots.peek(); + workingStack = itemHandler.insertItem(working, workingStack, false); + if (workingStack.isEmpty()) { + doneWithStack = true; + break; + } + + stackableSlots.pop(); + if(stackableSlots.isEmpty()) { + failedToPush.add(workingStack.copy()); + doneWithStack = true; + } + } + } + + return failedToPush.build(); + } +} diff --git a/neoforge-main/src/main/resources/META-INF/accesstransformer.cfg b/neoforge-main/src/main/resources/META-INF/accesstransformer.cfg index 2aa3a898..c539504e 100644 --- a/neoforge-main/src/main/resources/META-INF/accesstransformer.cfg +++ b/neoforge-main/src/main/resources/META-INF/accesstransformer.cfg @@ -12,4 +12,9 @@ public net.minecraft.world.level.border.WorldBorder getListeners()Ljava/util/Lis #public net.minecraft.server.MinecraftServer executor #public net.minecraft.server.MinecraftServer progressListenerFactory #public net.minecraft.server.MinecraftServer storageSource -#public-f net.minecraft.core.RegistryAccess$ImmutableRegistryAccess registries # registries \ No newline at end of file +#public-f net.minecraft.core.RegistryAccess$ImmutableRegistryAccess registries # registries + +# Registries and Codecs +public net.minecraft.resources.RegistryOps lookupProvider +public net.minecraft.resources.RegistryOps$HolderLookupAdapter +public net.minecraft.resources.RegistryOps$HolderLookupAdapter lookupProvider \ No newline at end of file diff --git a/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/ItemHandlingTests.java b/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/ItemHandlingTests.java new file mode 100644 index 00000000..61a8c4ec --- /dev/null +++ b/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/ItemHandlingTests.java @@ -0,0 +1,123 @@ +package dev.compactmods.machines.test.junit; + +import dev.compactmods.machines.test.junit.asserts.ItemStackAssertions; +import dev.compactmods.machines.util.item.ItemHandlerUtil; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.neoforged.neoforge.items.ItemStackHandler; +import net.neoforged.testframework.junit.EphemeralTestServerProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.List; + +@ExtendWith(EphemeralTestServerProvider.class) +public class ItemHandlingTests { + + private static ItemStack sticks(int size) { + return new ItemStack(Items.STICK, size); + } + + private static ItemStack logs(int size) { + return new ItemStack(Items.OAK_LOG, size); + } + + private static List getTreeDrops() { + return List.of(logs(5), sticks(2)); + } + + @Test + public void fillsFirstAvailableEmptySlots() { + var drops = getTreeDrops(); + var fakeChest = new ItemStackHandler(5); + + var failed = ItemHandlerUtil.insertMultipleStacks(fakeChest, drops); + + // Failed = Empty + // Oak Logs = Slot 0 (took empty slot) + // Sticks = Slot 1 (took empty slot) + Assertions.assertEquals(0, failed.size()); + + ItemStackAssertions.assertSlot(fakeChest, 0, logs(5)); + ItemStackAssertions.assertSlot(fakeChest, 1, sticks(2)); + + ItemStackAssertions.assertSlotEmpty(fakeChest, 2); + ItemStackAssertions.assertSlotEmpty(fakeChest, 3); + ItemStackAssertions.assertSlotEmpty(fakeChest, 4); + } + + @Test + public void stacksOntoExistingSlotsFirst() { + var drops = getTreeDrops(); + var fakeChest = new ItemStackHandler(5); + fakeChest.insertItem(1, new ItemStack(Items.STICK, 1), false); + fakeChest.insertItem(2, new ItemStack(Items.OAK_LOG, 1), false); + var failed = ItemHandlerUtil.insertMultipleStacks(fakeChest, drops); + + // Failed = Empty + // Oak Logs = Slot 2 (stacked on existing) + // Sticks = Slot 1 (stacked on existing) + Assertions.assertEquals(0, failed.size()); + + ItemStackAssertions.assertSlot(fakeChest, 0, ItemStack.EMPTY); + ItemStackAssertions.assertSlot(fakeChest, 1, sticks(3)); + ItemStackAssertions.assertSlot(fakeChest, 2, logs(6)); + ItemStackAssertions.assertSlot(fakeChest, 3, ItemStack.EMPTY); + ItemStackAssertions.assertSlot(fakeChest, 4, ItemStack.EMPTY); + } + + @Test + public void overflowFillsFromFirstEmptySlot() { + var drops = getTreeDrops(); + var fakeChest = new ItemStackHandler(5); + fakeChest.insertItem(3, new ItemStack(Items.STICK, 63), false); + var failed = ItemHandlerUtil.insertMultipleStacks(fakeChest, drops); + + // Failed = Empty + // Oak Logs = Slot 0 (took empty slot) + // Sticks = Slots 1 + 3 (stacked on existing - 3 should be full, 4 should be empty) + Assertions.assertEquals(0, failed.size()); + + ItemStackAssertions.assertSlot(fakeChest, 0, logs(5)); + ItemStackAssertions.assertSlot(fakeChest, 1, sticks(1)); + ItemStackAssertions.assertSlot(fakeChest, 2, ItemStack.EMPTY); + ItemStackAssertions.assertSlot(fakeChest, 3, sticks(64)); + ItemStackAssertions.assertSlot(fakeChest, 4, ItemStack.EMPTY); + } + + @Test + public void stacksExistingAndFillsFirstEmptySlots() { + var drops = getTreeDrops(); + var fakeChest = new ItemStackHandler(5); + fakeChest.insertItem(2, new ItemStack(Items.OAK_LOG, 1), false); + + var failed = ItemHandlerUtil.insertMultipleStacks(fakeChest, drops); + + // Failed = Empty + // Oak Logs = Slot 2 (stacked on existing) + // Sticks = Slot 0 (took empty slot) + Assertions.assertEquals(0, failed.size()); + + ItemStackAssertions.assertSlot(fakeChest, 0, sticks(2)); + ItemStackAssertions.assertSlot(fakeChest, 1, ItemStack.EMPTY); + ItemStackAssertions.assertSlot(fakeChest, 2, logs(6)); + ItemStackAssertions.assertSlot(fakeChest, 3, ItemStack.EMPTY); + ItemStackAssertions.assertSlot(fakeChest, 4, ItemStack.EMPTY); + } + + @Test + public void doesNotOverfill() { + var drops = getTreeDrops(); + var fakeChest = new ItemStackHandler(1); + fakeChest.insertItem(0, new ItemStack(Items.OAK_LOG, 64), false); + + var failed = ItemHandlerUtil.insertMultipleStacks(fakeChest, drops); + + // Failed = 2 Stacks + Assertions.assertEquals(2, failed.size()); + + ItemStackAssertions.assertListIndex(failed, 0, logs(5)); + ItemStackAssertions.assertListIndex(failed, 1, sticks(2)); + } +} diff --git a/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/asserts/ItemStackAssertions.java b/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/asserts/ItemStackAssertions.java new file mode 100644 index 00000000..e78c581a --- /dev/null +++ b/neoforge-main/src/test/java/dev/compactmods/machines/test/junit/asserts/ItemStackAssertions.java @@ -0,0 +1,25 @@ +package dev.compactmods.machines.test.junit.asserts; + +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.IItemHandler; +import org.junit.jupiter.api.Assertions; + +import java.util.List; + +public class ItemStackAssertions { + + public static void assertSlotEmpty(IItemHandler handler, int index) { + final var actual = handler.getStackInSlot(index); + Assertions.assertEquals(ItemStack.EMPTY, actual, "Expected slot %s to be empty; got %s.".formatted(index, actual)); + } + + public static void assertSlot(IItemHandler handler, int index, ItemStack expected) { + final var actual = handler.getStackInSlot(index); + Assertions.assertTrue(ItemStack.matches(expected, actual), "Expected slot %s to be equal to expected stack; got %s.".formatted(index, actual)); + } + + public static void assertListIndex(List items, int index, ItemStack expected) { + final var actual = items.get(index); + Assertions.assertTrue(ItemStack.matches(expected, actual), "Expected slot %s to be equal to expected stack; got %s.".formatted(index, actual)); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index eef7def0..c147e03b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,16 +1,20 @@ dependencyResolutionManagement { versionCatalogs.create("neoforged") { - version("neoforge", "21.0.143") - version("neogradle", "7.0.154") + version("neoforge", "21.1.22") + version("neogradle", "7.0.161") + version("mdg", "1.0.14") version("neoforgeRange") { - require("[21.0,22)") - prefer("21.0.143") + require("[21.1,22)") + prefer("21.1.22") } - plugin("moddev", "net.neoforged.gradle.userdev") + plugin("neogradle", "net.neoforged.gradle.userdev") .versionRef("neogradle") + plugin("moddev", "net.neoforged.moddev") + .versionRef("mdg") + library("neoforge", "net.neoforged", "neoforge") .versionRef("neoforge") @@ -19,10 +23,10 @@ dependencyResolutionManagement { } versionCatalogs.create("mojang") { - version("minecraft", "1.21.0") + version("minecraft", "1.21.1") version("minecraftRange") { - this.require("[1.21, 1.21.1)") - this.prefer("1.21") + this.require("[1.21, 1.21.2)") + this.prefer("1.21.1") } }