diff --git a/build.gradle.kts b/build.gradle.kts index d9a35588..7981f50c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } // maven.repo.local is set within the Julia script in the website branch -tasks.create("publishAllMavens") { +tasks.register("publishAllMavens") { dependsOn(":forge:publishAllMavens") dependsOn(":neoforge:publishAllMavens") } diff --git a/buildSrc/src/main/kotlin/kff.combined-conventions.gradle.kts b/buildSrc/src/main/kotlin/kff.combined-conventions.gradle.kts index 22c078a7..d3d3ecac 100644 --- a/buildSrc/src/main/kotlin/kff.combined-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/kff.combined-conventions.gradle.kts @@ -18,17 +18,4 @@ project.tasks.withType { zipTree(project(":neoforge:$subproject").tasks.getByName("jar", Jar::class).archiveFile), ) }) - - manifest { - attributes( - "Specification-Title" to "Kotlin for Forge", - "Specification-Vendor" to "Forge", - "Specification-Version" to "1", - "Implementation-Title" to project.name, - "Implementation-Version" to project.version, - "Implementation-Vendor" to "thedarkcolour", - "Implementation-Timestamp" to LocalDateTime.now(), - "Automatic-Module-Name" to "thedarkcolour.kotlinforforge.$subproject", - ) - } -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/kff.common-conventions.gradle.kts b/buildSrc/src/main/kotlin/kff.common-conventions.gradle.kts index b1b62d62..3b8f9348 100644 --- a/buildSrc/src/main/kotlin/kff.common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/kff.common-conventions.gradle.kts @@ -1,12 +1,13 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import thedarkcolour.kotlinforforge.plugin.alias +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile import thedarkcolour.kotlinforforge.plugin.getPropertyString -project.plugins.apply(JavaPlugin::class) -project.plugins.apply(IdeaPlugin::class) -project.plugins.apply(MavenPublishPlugin::class) -project.plugins.apply(alias("kotlinJvm", project)) +plugins { + java + idea + `maven-publish` + kotlin("jvm") +} val jvmTarget = JvmTarget.JVM_17 @@ -32,7 +33,7 @@ project.tasks { expand(replacements) } } - withType { + withType { compilerOptions.jvmTarget.set(jvmTarget) compilerOptions.freeCompilerArgs.set(listOf("-Xexplicit-api=warning", "-Xjvm-default=all")) } diff --git a/combined/build.gradle.kts b/combined/build.gradle.kts index 582984a3..93dcea4d 100644 --- a/combined/build.gradle.kts +++ b/combined/build.gradle.kts @@ -113,8 +113,8 @@ modrinth { loaders.add("neoforge") uploadFile.provider(project(":combined").tasks.jarJar) } -tasks.create("publishModPlatforms") { - finalizedBy(tasks.create("printPublishingMessage") { +tasks.register("publishModPlatforms") { + finalizedBy(tasks.register("printPublishingMessage") { doFirst { println("Publishing Kotlin for Forge ${getPropertyString("kff_version")} to Modrinth and CurseForge") } diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 92520688..686d1ad2 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import thedarkcolour.kotlinforforge.plugin.getKffMaxVersion import thedarkcolour.kotlinforforge.plugin.getPropertyString @@ -106,15 +105,17 @@ tasks { } } - withType { - compilerOptions.jvmTarget.set(JvmTarget.JVM_17) - } - assemble { dependsOn(jarJar) } } +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + } +} + publishing { publications { register("maven") { @@ -126,9 +127,9 @@ publishing { } // maven.repo.local is set within the Julia script in the website branch -tasks.create("publishAllMavens") { +tasks.register("publishAllMavens") { dependsOn(":forge:publishToMavenLocal") dependsOn(":forge:kfflib:publishToMavenLocal") dependsOn(":forge:kfflang:publishToMavenLocal") dependsOn(":forge:kffmod:publishToMavenLocal") -} \ No newline at end of file +} diff --git a/forge/kfflang/build.gradle.kts b/forge/kfflang/build.gradle.kts index 3bf66e7f..347681f6 100644 --- a/forge/kfflang/build.gradle.kts +++ b/forge/kfflang/build.gradle.kts @@ -1,3 +1,4 @@ +import net.minecraftforge.gradle.common.util.RunConfig import java.time.LocalDateTime plugins { @@ -12,7 +13,7 @@ minecraft { copyIdeResources.set(true) runs { - create("client") { + create("client", Action { workingDirectory(project.file("run")) ideaModule = "KotlinForForge.forge.kfflang.test" @@ -29,7 +30,7 @@ minecraft { source(sourceSets.test.get()) } } - } + }) create("server") { workingDirectory(project.file("run/server")) diff --git a/forge/kfflib/build.gradle.kts b/forge/kfflib/build.gradle.kts index 10999341..d0ae7d83 100644 --- a/forge/kfflib/build.gradle.kts +++ b/forge/kfflib/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.time.LocalDateTime plugins { @@ -118,10 +117,12 @@ tasks { ) } } +} - // Only require the lang provider to use explicit visibility modifiers, not the test mod - withType { - kotlinOptions.freeCompilerArgs = listOf("-Xexplicit-api=warning", "-Xjvm-default=all") +kotlin { + compilerOptions { + // Only require the lang provider to use explicit visibility modifiers, not the test mod + freeCompilerArgs = listOf("-Xexplicit-api=warning", "-Xjvm-default=all") } } diff --git a/forge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt b/forge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt index 30992c2c..5ff91bbd 100644 --- a/forge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt +++ b/forge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt @@ -4,13 +4,13 @@ import net.minecraft.gametest.framework.GameTest import net.minecraft.gametest.framework.GameTestAssertException import net.minecraft.gametest.framework.GameTestHelper import net.minecraft.resources.ResourceLocation +import net.minecraftforge.gametest.GameTestDontPrefix import net.minecraftforge.gametest.GameTestHolder -import net.minecraftforge.gametest.PrefixGameTestTemplate import net.minecraftforge.registries.ForgeRegistries @GameTestHolder(KFFLibTest.ID) public object InGameTester { - @PrefixGameTestTemplate(false) + @GameTestDontPrefix @GameTest(template = "dummy") @JvmStatic public fun testBlock(helper: GameTestHelper) { @@ -20,4 +20,4 @@ public object InGameTester { } } } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 4a83c9d8..86680f83 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ forge_version=49.0.8 min_neo_version = 20.2.3-beta neo_version = 20.4.22-beta -kff_version=4.11.0 +kff_version=4.12.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fd6f914a..33e4098c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,8 @@ [versions] -kotlin = "2.0.0" -coroutines = "1.8.1" -serialization = "1.6.3" +jba = "23.0.0" +kotlin = "2.2.10" +coroutines = "1.10.2" +serialization = "1.9.0" neogradle = "7.0.138" neoforge = "20.4.22-beta" forgegradle = "6.0.25" @@ -28,4 +29,4 @@ kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } neogradle = { id = "net.neoforged.gradle.userdev", version.ref = "neogradle" } forgegradle = { id = "net.minecraftforge.gradle", version.ref = "forgegradle" } minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" } -cursegradle = { id = "com.matthewprenger.cursegradle", version.ref = "cursegradle" } \ No newline at end of file +cursegradle = { id = "com.matthewprenger.cursegradle", version.ref = "cursegradle" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f..e6441136 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 20db9ad5..ca025c83 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..b740cf13 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..25da30db 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index 64700510..344aea49 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -58,7 +58,7 @@ fun DependencyHandler.include(dep: ModuleDependency): ModuleDependency { } // maven.repo.local is set within the Julia script in the website branch -tasks.create("publishAllMavens") { +tasks.register("publishAllMavens") { dependsOn(":neoforge:publishToMavenLocal") dependsOn(":neoforge:kfflib:publishToMavenLocal") dependsOn(":neoforge:kfflang:publishToMavenLocal") diff --git a/neoforge/kfflang/build.gradle.kts b/neoforge/kfflang/build.gradle.kts index 35f3f666..cef02f55 100644 --- a/neoforge/kfflang/build.gradle.kts +++ b/neoforge/kfflang/build.gradle.kts @@ -1,5 +1,4 @@ import net.neoforged.gradle.dsl.common.extensions.RunnableSourceSet -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.time.LocalDateTime plugins { @@ -9,14 +8,20 @@ plugins { // Tells NeoGradle to treat this source set as a separate mod sourceSets["test"].extensions.getByType().configure { run -> run.modIdentifier("kfflangtest") } -val nonmclibs: Configuration by configurations.creating {} +val nonmclibs: Configuration by configurations.creating { + resolutionStrategy.eachDependency { + if (requested.group == "org.jetbrains" && requested.name == "annotations") { + useVersion(libs.versions.jba.get()) + because("JPMS automatic module name") + } + } +} runs { configureEach { - modSource(sourceSets["main"]) - //modSource(sourceSets["test"]) + modSource(sourceSets["test"]) dependencies { - runtime((nonmclibs)) + runtime.add(nonmclibs) } } create("client") @@ -54,10 +59,12 @@ tasks { ) } } +} - // Only require the lang provider to use explicit visibility modifiers, not the test mod - withType { - kotlinOptions.freeCompilerArgs = listOf("-Xexplicit-api=warning", "-Xjvm-default=all") +kotlin { + compilerOptions { + // Only require the lang provider to use explicit visibility modifiers, not the test mod + freeCompilerArgs = listOf("-Xexplicit-api=warning", "-Xjvm-default=all") } } diff --git a/neoforge/kfflang/src/test/kotlin/thedarkcolour/kotlinforforgetest/KFFLangTest.kt b/neoforge/kfflang/src/test/kotlin/thedarkcolour/kotlinforforgetest/KFFLangTest.kt index acd0aaa2..a4b320e8 100644 --- a/neoforge/kfflang/src/test/kotlin/thedarkcolour/kotlinforforgetest/KFFLangTest.kt +++ b/neoforge/kfflang/src/test/kotlin/thedarkcolour/kotlinforforgetest/KFFLangTest.kt @@ -16,12 +16,12 @@ import org.apache.logging.log4j.Logger * check out the [KotlinModdingSkeleton repository](https://github.com/thedarkcolour/KotlinModdingSkeleton). */ @Mod(KFFLangTest.ID) -object KFFLangTest { - const val ID = "kfflangtest" +public object KFFLangTest { + public const val ID: String = "kfflangtest" - val LOGGER: Logger = LogManager.getLogger(ID) + private val LOGGER: Logger = LogManager.getLogger(ID) init { LOGGER.log(Level.INFO, "Hello world from Kotlin for forge Language provider!") } -} \ No newline at end of file +} diff --git a/neoforge/kfflib/build.gradle.kts b/neoforge/kfflib/build.gradle.kts index 41fa4e14..89b2ee07 100644 --- a/neoforge/kfflib/build.gradle.kts +++ b/neoforge/kfflib/build.gradle.kts @@ -4,6 +4,23 @@ plugins { id("kff.neoforge-conventions") } +val nonmclibs: Configuration by configurations.creating { + resolutionStrategy.eachDependency { + if (requested.group == "org.jetbrains" && requested.name == "annotations") { + useVersion(libs.versions.jba.get()) + because("JPMS automatic module name") + } + } +} + +val gameTestServer by runs.creating { + systemProperty("neoforge.enabledGameTestNamespaces", "kfflibtest") + modSources(sourceSets["test"]) + dependencies { + runtime.add(nonmclibs) + } +} + dependencies { implementation("net.neoforged:neoforge:${project.properties["neo_version"]}") @@ -15,6 +32,15 @@ dependencies { api(libs.kotlinx.coroutines.jdk8) api(libs.kotlinx.serialization.json) + nonmclibs(libs.kotlin.stdlib) + nonmclibs(libs.kotlin.stdlib.jdk8) + nonmclibs(libs.kotlin.stdlib.jdk7) + nonmclibs(libs.kotlin.reflect) + nonmclibs(libs.kotlinx.coroutines.core) + nonmclibs(libs.kotlinx.coroutines.core.jvm) + nonmclibs(libs.kotlinx.coroutines.jdk8) + nonmclibs(libs.kotlinx.serialization.json) + implementation(projects.neoforge.kfflang) } diff --git a/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt new file mode 100644 index 00000000..57d56a6c --- /dev/null +++ b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/InGameTester.kt @@ -0,0 +1,23 @@ +package thedarkcolour.kfflibtest + +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestAssertException +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.resources.ResourceLocation +import net.neoforged.neoforge.gametest.GameTestHolder +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate + +@GameTestHolder(KFFLibTest.ID) +public object InGameTester { + @PrefixGameTestTemplate(false) + @GameTest(template = "dummy") + @JvmStatic + public fun testBlock(helper: GameTestHelper) { + helper.succeedIf { + if (!BuiltInRegistries.BLOCK.containsKey(ResourceLocation(KFFLibTest.ID, "example_block"))) { + throw GameTestAssertException("Block is not registered correctly!") + } + } + } +} diff --git a/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/KFFLibTest.kt b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/KFFLibTest.kt new file mode 100644 index 00000000..feb8018e --- /dev/null +++ b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/KFFLibTest.kt @@ -0,0 +1,40 @@ +package thedarkcolour.kfflibtest + +import net.neoforged.fml.common.Mod +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger +import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS + +/** + * Set `modLoader` in mods.toml to + * `"kotlinforforge"` and loaderVersion to `"[3,)"`. + * + * Make sure to use [MOD_CONTEXT] + * instead of [FMLJavaModLoadingContext]. + * + * For a more detailed example mod, + * check out the [KotlinModdingSkeleton repository](https://github.com/thedarkcolour/KotlinModdingSkeleton). + */ +@Mod(KFFLibTest.ID) +public object KFFLibTest { + internal const val ID = "kfflibtest" + + internal val LOGGER: Logger = LogManager.getLogger(ID) + + init { + LOGGER.log(Level.INFO, "Hello world!") + + ModBlocks.REGISTRY.register(MOD_BUS) + + repeat(30) { + testVec2() + testVec3i() + testVec3() + testVector3d() + testVector3f() + testVector4f() + testVector4d() + } + } +} diff --git a/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/ModBlocks.kt b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/ModBlocks.kt new file mode 100644 index 00000000..f30ebfc3 --- /dev/null +++ b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/ModBlocks.kt @@ -0,0 +1,17 @@ +package thedarkcolour.kfflibtest + +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.state.BlockBehaviour +import net.minecraft.world.level.material.MapColor +import net.neoforged.neoforge.registries.DeferredRegister +import thedarkcolour.kotlinforforge.neoforge.forge.getValue +import java.util.function.Supplier + +internal object ModBlocks { + internal val REGISTRY = DeferredRegister.create(BuiltInRegistries.BLOCK, KFFLibTest.ID) + + internal val EXAMPLE_BLOCK by REGISTRY.register("example_block", Supplier { + Block(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).strength(4.0f)) + }) +} diff --git a/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/VectorTester.kt b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/VectorTester.kt new file mode 100644 index 00000000..a41abd2e --- /dev/null +++ b/neoforge/kfflib/src/test/kotlin/thedarkcolour/kfflibtest/VectorTester.kt @@ -0,0 +1,510 @@ +package thedarkcolour.kfflibtest + +import net.minecraft.core.Vec3i +import net.minecraft.util.Mth +import net.minecraft.world.phys.Vec2 +import net.minecraft.world.phys.Vec3 +import org.joml.* +import thedarkcolour.kotlinforforge.neoforge.forge.vectorutil.v2d.* +import thedarkcolour.kotlinforforge.neoforge.forge.vectorutil.v3d.* +import thedarkcolour.kotlinforforge.neoforge.forge.vectorutil.v4d.* +import kotlin.IllegalStateException +import kotlin.random.Random +import kotlin.random.nextInt + +internal fun testVec2() { + val x1 = nextFloat() + val y1 = nextFloat() + + val x2 = nextFloat() + val y2 = nextFloat() + + val v1 = Vec2(x1, y1) + val v2 = Vec2(x2, y2) + + val (a, b) = v1 + requireEquality(a, x1) { "Vec2 deconstruction for x has failed!" } + requireEquality(b, y1) { "Vec2 deconstruction for y has failed!" } + + requireEquality(x1, v1[0]) { "Vec2 index access for x has failed! "} + requireEquality(y1, v1[1]) { "Vec2 index access for y has failed! "} + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vec2 iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vec2 iterator y has failed! "} + } + if (index >= 2) { + throw IllegalStateException("Vec2 has no 3rd element!") + } + } + + // I can't believe Vec2#equals does not override Object#equals + // It's entirely separate method with same name + requireEquality(v1 + v2, Vec2(x1 + x2, y1 + y2), Vec2::equals) { "Vec2 addition has failed!" } + requireEquality(v1 - v2, Vec2(x1 - x2, y1 - y2), Vec2::equals) { "Vec2 subtraction has failed!" } + requireEquality(-v1, Vec2(-x1, -y1), Vec2::equals) { "Vec2 unaryMinus has failed!" } + + val scalar = nextFloat() + requireEquality(v1 * scalar, Vec2(x1 * scalar, y1 * scalar), Vec2::equals) { "Vec2 scalar multiplication has failed! " } + requireEquality(v1 * v2, Vec2(x1 * x2, y1 * y2), Vec2::equals) { "Vec2 * Vec2 multiplication has failed! " } + + requireEquality(v1 / scalar, Vec2(x1 / scalar, y1 / scalar), Vec2::equals) { "Vec2 scalar division has failed! " } + requireEquality(v1 / v2, Vec2(x1 / x2, y1 / y2), Vec2::equals) { "Vec2 / Vec2 division has failed! " } + + requireEquality(v1.deepCopy(), v1, Vec2::equals) { "Vec2 cloning has failed! " } + + requireEquality(v1, Vec2(x1, y1), Vec2::equals) { "v1 has been mutated!" } + requireEquality(v2, Vec2(x2, y2), Vec2::equals) { "v2 has been mutated!" } +} + +internal fun testVec3i() { + val (x1, y1, z1) = listOf(nextInt(), nextInt(), nextInt()) + val (x2, y2, z2) = listOf(nextInt(), nextInt(), nextInt()) + + val v1 = Vec3i(x1, y1, z1) + val v2 = Vec3i(x2, y2, z2) + + val (a, b, c) = v1 + + requireEquality(a, x1) { "Vec3i deconstruction for x has failed!" } + requireEquality(b, y1) { "Vec3i deconstruction for y has failed!" } + requireEquality(c, z1) { "Vec3i deconstruction for z has failed!" } + + requireEquality(v1[0], x1) { "Vec3i index access for x has failed!" } + requireEquality(v1[1], y1) { "Vec3i index access for y has failed!" } + requireEquality(v1[2], z1) { "Vec3i index access for z has failed!" } + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vec3i iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vec3i iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vec3i iterator z has failed! "} + } + if (index >= 3) { + throw IllegalStateException("Vec3i has no 4th element!") + } + } + + requireEquality(v1 + v2, Vec3i(x1 + x2, y1 + y2, z1 + z2)) { "Vec3i addition has failed!" } + requireEquality(v1 - v2, Vec3i(x1 - x2, y1 - y2, z1 - z2)) { "Vec3i subtraction has failed!" } + requireEquality(-v1, Vec3i(-x1, -y1, -z1)) { "Vec3i unaryMinus has failed!" } + + val scalar = nextInt() + requireEquality(v1 * scalar, Vec3i(x1 * scalar, y1 * scalar, z1 * scalar)) { "Vec3i scalar multiplication has failed! " } + requireEquality(v1 * v2, Vec3i(x1 * x2, y1 * y2, z1 * z2)) { "Vec3i * Vec3i multiplication has failed! " } + + requireEquality(v1 / scalar, Vec3i(x1 / scalar, y1 / scalar, z1 / scalar)) { "Vec3i scalar division has failed! " } + requireEquality(v1 / v2, Vec3i(x1 / x2, y1 / y2, z1 / z2)) { "Vec3i / Vec3i division has failed! " } + + requireEquality(v1.deepCopy(), Vec3i(x1, y1, z1)) { "Vec3i cloning has failed! " } + + requireEquality(v1, Vec3i(x1, y1, z1)) { "v1 has been mutated!" } + requireEquality(v2, Vec3i(x2, y2, z2)) { "v2 has been mutated!" } + + val vec3 = Vec3(x1.toDouble(), y1.toDouble(), z1.toDouble()) + val vector3f = Vector3f(x1.toFloat(), y1.toFloat(), z1.toFloat()) + val vector3d = Vector3d(x1.toDouble(), y1.toDouble(), z1.toDouble()) + + requireEquality(v1, vec3.toVec3i()) { "Vec3 -> Vec3i conversion has failed!" } + requireEquality(v1, vector3f.toVec3i()) { "Vector3f -> Vec3i conversion has failed!" } + requireEquality(v1, vector3d.toVec3i()) { "Vector3d -> Vec3i conversion has failed!" } +} + +internal fun testVec3() { + val (x1, y1, z1) = listOf(nextDouble(), nextDouble(), nextDouble()) + val (x2, y2, z2) = listOf(nextDouble(), nextDouble(), nextDouble()) + + val v1 = Vec3(x1, y1, z1) + val v2 = Vec3(x2, y2, z2) + + val (a, b, c) = v1 + + requireEquality(a, x1) { "Vec3 deconstruction for x has failed!" } + requireEquality(b, y1) { "Vec3 deconstruction for y has failed!" } + requireEquality(c, z1) { "Vec3 deconstruction for z has failed!" } + + requireEquality(v1[0], x1) { "Vec3 index access for x has failed!" } + requireEquality(v1[1], y1) { "Vec3 index access for y has failed!" } + requireEquality(v1[2], z1) { "Vec3 index access for z has failed!" } + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vec3 iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vec3 iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vec3 iterator z has failed! "} + } + if (index >= 3) { + throw IllegalStateException("Vec3 has no 4th element!") + } + } + + requireEquality(v1 + v2, Vec3(x1 + x2, y1 + y2, z1 + z2)) { "Vec3 addition has failed!" } + requireEquality(v1 - v2, Vec3(x1 - x2, y1 - y2, z1 - z2)) { "Vec3 subtraction has failed!" } + requireEquality(-v1, Vec3(-x1, -y1, -z1)) { "Vec3 unaryMinus has failed!" } + + val scalar = nextDouble() + requireEquality(v1 * scalar, Vec3(x1 * scalar, y1 * scalar, z1 * scalar)) { "Vec3 multiplication has failed! " } + requireEquality(v1 * v2, Vec3(x1 * x2, y1 * y2, z1 * z2)) { "Vec3 multiplication has failed! " } + + requireEquality(v1 / scalar, Vec3(x1 / scalar, y1 / scalar, z1 / scalar)) { "Vec3 scalar division has failed! " } + requireEquality(v1 / v2, Vec3(x1 / x2, y1 / y2, z1 / z2)) { "Vec3 / Vec3 division has failed! " } + + requireEquality(v1.deepCopy(), v1) { "Vec3 cloning has failed! " } + + requireEquality(v1, Vec3(x1, y1, z1)) { "v1 has been mutated!" } + requireEquality(v2, Vec3(x2, y2, z2)) { "v2 has been mutated!" } + + val vec3i = Vec3i(Mth.floor(x1), Mth.floor(y1), Mth.floor(z1)) + val vector3f = Vector3f(x1.toFloat(), y1.toFloat(), z1.toFloat()) + val vector3d = Vector3d(x1, y1, z1) + + requireEquality(v1, vec3i.toVec3()) { "Vec3i -> Vec3 conversion has failed!" } + requireEquality(v1, vector3f.toVec3()) { "Vector3f -> Vec3 conversion has failed!" } + requireEquality(v1, vector3d.toVec3()) { "Vector3d -> Vec3 conversion has failed!" } +} + +internal fun testVector3d() { + val (x1, y1, z1) = listOf(nextDouble(), nextDouble(), nextDouble()) + val (x2, y2, z2) = listOf(nextDouble(), nextDouble(), nextDouble()) + val tester: (Vector3dc, Vector3dc) -> Boolean = { a, b -> + a == b || a.distanceSquared(b) < 0.01 + } + + val v1: Vector3dc = Vector3d(x1, y1, z1) + val v2: Vector3dc = Vector3d(x2, y2, z2) + + val (a, b, c) = v1 + + requireEquality(a, x1) { "Vector3d deconstruction for x has failed!" } + requireEquality(b, y1) { "Vector3d deconstruction for y has failed!" } + requireEquality(c, z1) { "Vector3d deconstruction for z has failed!" } + + requireEquality(v1[0], x1) { "Vector3d index access for x has failed!" } + requireEquality(v1[1], y1) { "Vector3d index access for y has failed!" } + requireEquality(v1[2], z1) { "Vector3d index access for z has failed!" } + + val v3 = Vector3d() + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vector3d iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vector3d iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vector3d iterator z has failed! "} + } + if (index >= 3) { + throw IllegalStateException("Vector3d has no 4th element!") + } + v3[index] = element + } + + requireEquality(v1, v3) { "Vector3d index setter has failed! "} + + requireEquality(v1 + v2, Vector3d(x1 + x2, y1 + y2, z1 + z2)) { "Vector3d addition has failed!" } + requireEquality(v1 - v2, Vector3d(x1 - x2, y1 - y2, z1 - z2)) { "Vector3d subtraction has failed!" } + requireEquality(-v1, Vector3d(-x1, -y1, -z1)) { "Vector3d unaryMinus has failed!" } + + val scalar = nextDouble() + requireEquality(v1 * scalar, Vector3d(x1 * scalar, y1 * scalar, z1 * scalar), tester) { "Vector3d scalar multiplication has failed! " } + requireEquality(v1 * v2, Vector3d(x1 * x2, y1 * y2, z1 * z2), tester) { "Vector3d * Vector3d multiplication has failed! " } + + requireEquality(v1 / scalar, Vector3d(x1 / scalar, y1 / scalar, z1 / scalar), tester) { "Vector3d scalar division has failed! " } + requireEquality(v1 / v2, Vector3d(x1 / x2, y1 / y2, z1 / z2), tester) { "Vector3d / Vector3d division has failed! " } + + requireEquality(v1.deepCopy(), v1) { "Vector3d cloning has failed!" } + + val plusAssign = v1.deepCopy() + plusAssign += v2 + requireEquality(v1 + v2, plusAssign) { "Vector3d addition&assign has failed!" } + + val minusAssign = v1.deepCopy() + minusAssign -= v2 + requireEquality(v1 - v2, minusAssign) { "Vector3d subtraction&assign has failed!" } + + val timesAssign = v1.deepCopy() + timesAssign *= scalar + requireEquality(v1 * scalar, timesAssign) { "Vector3d multiplication&assign has failed!" } + + val divAssign = v1.deepCopy() + divAssign /= scalar + requireEquality(v1 / scalar, divAssign) { "Vector3d division&assign has failed!" } + + requireEquality(v1, Vector3d(x1, y1, z1)) { "v1 has been mutated!" } + requireEquality(v2, Vector3d(x2, y2, z2)) { "v2 has been mutated!" } + + val vec3i = Vec3i(Mth.floor(x1), Mth.floor(y1), Mth.floor(z1)) + val vector3f = Vector3f(x1.toFloat(), y1.toFloat(), z1.toFloat()) + val vec3 = Vec3(x1, y1, z1) + + requireEquality(v1, vec3i.toVector3d()) { "Vec3i -> Vector3d conversion has failed!" } + requireEquality(v1, vector3f.toVector3d()) { "Vector3f -> Vector3d conversion has failed!" } + requireEquality(v1, vec3.toVector3d()) { "Vec3 -> Vector3d conversion has failed!" } + + KFFLibTest.LOGGER.info("Vector3d test succeed") +} + +internal fun testVector3f() { + val (x1, y1, z1) = listOf(nextFloat(), nextFloat(), nextFloat()) + val (x2, y2, z2) = listOf(nextFloat(), nextFloat(), nextFloat()) + val tester: (Vector3fc, Vector3fc) -> Boolean = { a, b -> + a == b || a.distanceSquared(b) < 0.01 + } + + val v1: Vector3fc = Vector3f(x1, y1, z1) + val v2: Vector3fc = Vector3f(x2, y2, z2) + + val (a, b, c) = v1 + + requireEquality(a, x1) { "Vector3f deconstruction for x has failed!" } + requireEquality(b, y1) { "Vector3f deconstruction for y has failed!" } + requireEquality(c, z1) { "Vector3f deconstruction for z has failed!" } + + requireEquality(v1[0], x1) { "Vector3f index access for x has failed!" } + requireEquality(v1[1], y1) { "Vector3f index access for y has failed!" } + requireEquality(v1[2], z1) { "Vector3f index access for z has failed!" } + + val v3 = Vector3f() + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vector3f iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vector3f iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vector3f iterator z has failed! "} + } + if (index >= 3) { + throw IllegalStateException("Vector3f has no 4th element!") + } + v3[index] = element + } + + requireEquality(v1, v3) { "Vector3f index setter has failed! "} + + requireEquality(v1 + v2, Vector3f(x1 + x2, y1 + y2, z1 + z2)) { "Vector3f addition has failed!" } + requireEquality(v1 - v2, Vector3f(x1 - x2, y1 - y2, z1 - z2)) { "Vector3f subtraction has failed!" } + requireEquality(-v1, Vector3f(-x1, -y1, -z1)) { "Vector3f unaryMinus has failed!" } + + val scalar = nextFloat() + requireEquality(v1 * scalar, Vector3f(x1 * scalar, y1 * scalar, z1 * scalar), tester) { "Vector3f multiplication has failed! " } + requireEquality(v1 * v2, Vector3f(x1 * x2, y1 * y2, z1 * z2), tester) { "Vector3f * Vector3f multiplication has failed! " } + requireEquality(v1.deepCopy(), v1) { "Vector3f cloning has failed! " } + + requireEquality(v1 / scalar, Vector3f(x1 / scalar, y1 / scalar, z1 / scalar), tester) { "Vector3f scalar division has failed! " } + requireEquality(v1 / v2, Vector3f(x1 / x2, y1 / y2, z1 / z2), tester) { "Vector3f / Vector3f division has failed! " } + + val plusAssign = v1.deepCopy() + plusAssign += v2 + requireEquality(v1 + v2, plusAssign) { "Vector3f addition&assign has failed!" } + + val minusAssign = v1.deepCopy() + minusAssign -= v2 + requireEquality(v1 - v2, minusAssign) { "Vector3f subtraction&assign has failed!" } + + val timesAssign = v1.deepCopy() + timesAssign *= scalar + requireEquality(v1 * scalar, timesAssign) { "Vector3f multiplication&assign has failed!" } + + val divAssign = v1.deepCopy() + divAssign /= scalar + requireEquality(v1 / scalar, divAssign) { "Vector3f division&assign has failed!" } + + requireEquality(v1, Vector3f(x1, y1 ,z1)) { "v1 has been mutated!" } + requireEquality(v2, Vector3f(x2, y2, z2)) { "v2 has been mutated!" } + + val vec3i = Vec3i(x1.toInt(), y1.toInt(), z1.toInt()) + val vector3d = Vector3d(x1.toDouble(), y1.toDouble(), z1.toDouble()) + val vec3 = Vec3(x1.toDouble(), y1.toDouble(), z1.toDouble()) + + requireEquality(v1, vec3i.toVector3f()) { "Vec3i -> Vector3f conversion has failed!" } + requireEquality(v1, vector3d.toVector3f()) { "Vector3f -> Vector3f conversion has failed!" } + requireEquality(v1, vec3.toVector3f()) { "Vec3 -> Vector3f conversion has failed!" } +} + +internal fun testVector4f() { + val (x1, y1, z1, w1) = listOf(nextFloat(), nextFloat(), nextFloat(), nextFloat()) + val (x2, y2, z2, w2) = listOf(nextFloat(), nextFloat(), nextFloat(), nextFloat()) + val tester: (Vector4fc, Vector4fc) -> Boolean = { a: Vector4fc, b: Vector4fc -> + a == b || a.distanceSquared(b) < 0.01 + } + + val v1: Vector4fc = Vector4f(x1, y1, z1, w1) + val v2: Vector4fc = Vector4f(x2, y2, z2, w2) + + val (a, b, c, d) = v1 + + requireEquality(a, x1) { "Vector4f deconstruction for x has failed!" } + requireEquality(b, y1) { "Vector4f deconstruction for y has failed!" } + requireEquality(c, z1) { "Vector4f deconstruction for z has failed!" } + requireEquality(d, w1) { "Vector4f deconstruction for w has failed!" } + + requireEquality(v1[0], x1) { "Vector4f index access for x has failed!" } + requireEquality(v1[1], y1) { "Vector4f index access for y has failed!" } + requireEquality(v1[2], z1) { "Vector4f index access for z has failed!" } + requireEquality(v1[3], w1) { "Vector4f index access for z has failed!" } + + val v3 = Vector4f() + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vector4f iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vector4f iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vector4f iterator z has failed! "} + } + if (index == 3) { + requireEquality(w1, element) { "Vector4f iterator w has failed! "} + } + if (index >= 4) { + throw IllegalStateException("Vector4f has no 5th element!") + } + v3[index] = element + } + + requireEquality(v1, v3) { "Vector4f index setter has failed! "} + + requireEquality(v1 + v2, Vector4f(x1 + x2, y1 + y2, z1 + z2, w1 + w2)) { "Vector4f addition has failed!" } + requireEquality(v1 - v2, Vector4f(x1 - x2, y1 - y2, z1 - z2, w1 - w2)) { "Vector4f subtraction has failed!" } + requireEquality(-v1, Vector4f(-x1, -y1, -z1, -w1)) { "Vector4f unaryMinus has failed!" } + + val scalar = nextFloat() + requireEquality(v1 * scalar, Vector4f(x1 * scalar, y1 * scalar, z1 * scalar, w1 * scalar), tester) { "Vector4f scala multiplication has failed! " } + requireEquality(v1 * v2, Vector4f(x1 * x2, y1 * y2, z1 * z2, w1 * w2), tester) { "Vector4f * Vector4f multiplication has failed! " } + + requireEquality(v1 / scalar, Vector4f(x1 / scalar, y1 / scalar, z1 / scalar, w1 / scalar), tester) { "Vector4f scala division has failed! " } + requireEquality(v1 / v2, Vector4f(x1 / x2, y1 / y2, z1 / z2, w1 / w2), tester) { "Vector4f / Vector4f division has failed! " } + + requireEquality(v1.deepCopy(), v1) { "Vector4f cloning has failed! " } + + requireEquality(v1, Vector4f(x1, y1, z1, w1)) { "v1 has been mutated!" } + requireEquality(v2, Vector4f(x2, y2, z2, w2)) { "v2 has been mutated!" } + + val plusAssign = v1.deepCopy() + plusAssign += v2 + requireEquality(v1 + v2, plusAssign) { "Vector4f addition&assign has failed!" } + + val minusAssign = v1.deepCopy() + minusAssign -= v2 + requireEquality(v1 - v2, minusAssign) { "Vector4f subtraction&assign has failed!" } + + val timesAssign = v1.deepCopy() + timesAssign *= scalar + requireEquality(v1 * scalar, timesAssign) { "Vector4f multiplication&assign has failed!" } + + val divAssign = v1.deepCopy() + divAssign /= scalar + requireEquality(v1 / scalar, divAssign) { "Vector4f division&assign has failed!" } + + KFFLibTest.LOGGER.info("Vector4f test succeed") +} + +internal fun testVector4d() { + val (x1, y1, z1, w1) = listOf(nextDouble(), nextDouble(), nextDouble(), nextDouble()) + val (x2, y2, z2, w2) = listOf(nextDouble(), nextDouble(), nextDouble(), nextDouble()) + val tester: (Vector4dc, Vector4dc) -> Boolean = { a, b -> + a == b || a.distanceSquared(b) < 0.01 + } + + val v1: Vector4dc = Vector4d(x1, y1, z1, w1) + val v2: Vector4dc = Vector4d(x2, y2, z2, w2) + + val (a, b, c, d) = v1 + + requireEquality(a, x1) { "Vector4d deconstruction for x has failed!" } + requireEquality(b, y1) { "Vector4d deconstruction for y has failed!" } + requireEquality(c, z1) { "Vector4d deconstruction for z has failed!" } + requireEquality(d, w1) { "Vector4d deconstruction for w has failed!" } + + requireEquality(v1[0], x1) { "Vector4d index access for x has failed!" } + requireEquality(v1[1], y1) { "Vector4d index access for y has failed!" } + requireEquality(v1[2], z1) { "Vector4d index access for z has failed!" } + requireEquality(v1[3], w1) { "Vector4d index access for z has failed!" } + + val v3 = Vector4d() + + for ((index, element) in v1.iterator().withIndex()) { + if (index == 0) { + requireEquality(x1, element) { "Vector4d iterator x has failed! "} + } + if (index == 1) { + requireEquality(y1, element) { "Vector4d iterator y has failed! "} + } + if (index == 2) { + requireEquality(z1, element) { "Vector4d iterator z has failed! "} + } + if (index == 3) { + requireEquality(w1, element) { "Vector4d iterator w has failed! "} + } + if (index >= 4) { + throw IllegalStateException("Vector4d has no 5th element!") + } + v3[index] = element + } + + requireEquality(v1, v3) { "Vector4d index setter has failed! "} + + requireEquality(v1 + v2, Vector4d(x1 + x2, y1 + y2, z1 + z2, w1 + w2)) { "Vector4d addition has failed!" } + requireEquality(v1 - v2, Vector4d(x1 - x2, y1 - y2, z1 - z2, w1 - w2)) { "Vector4d subtraction has failed!" } + requireEquality(-v1, Vector4d(-x1, -y1, -z1, -w1)) { "Vector4d unaryMinus has failed!" } + + val scalar = nextDouble() + requireEquality(v1 * scalar, Vector4d(x1 * scalar, y1 * scalar, z1 * scalar, w1 * scalar), tester) { "Vector4d scala multiplication has failed! " } + requireEquality(v1 * v2, Vector4d(x1 * x2, y1 * y2, z1 * z2, w1 * w2), tester) { "Vector4d * Vector4d multiplication has failed! " } + + requireEquality(v1 / scalar, Vector4d(x1 / scalar, y1 / scalar, z1 / scalar, w1 / scalar), tester) { "Vector4d scala division has failed! " } + requireEquality(v1 / v2, Vector4d(x1 / x2, y1 / y2, z1 / z2, w1 / w2), tester) { "Vector4d / Vector4d division has failed! " } + + requireEquality(v1.deepCopy(), v1) { "Vector4d cloning has failed! " } + + requireEquality(v1, Vector4d(x1, y1, z1, w1)) { "v1 has been mutated!" } + requireEquality(v2, Vector4d(x2, y2, z2, w2)) { "v2 has been mutated!" } + + val plusAssign = v1.deepCopy() + plusAssign += v2 + requireEquality(v1 + v2, plusAssign) { "Vector4d addition&assign has failed!" } + + val minusAssign = v1.deepCopy() + minusAssign -= v2 + requireEquality(v1 - v2, minusAssign) { "Vector4d subtraction&assign has failed!" } + + val timesAssign = v1.deepCopy() + timesAssign *= scalar + requireEquality(v1 * scalar, timesAssign) { "Vector4d multiplication&assign has failed!" } + + val divAssign = v1.deepCopy() + divAssign /= scalar + requireEquality(v1 / scalar, divAssign) { "Vector4d division&assign has failed!" } + + KFFLibTest.LOGGER.info("Vector4d test succeed") +} + +private fun nextInt() = Random.nextInt(-5000..5000) + +private fun nextFloat() = nextInt().toFloat() + +private fun nextDouble() = nextInt().toDouble() + +private fun requireEquality(a: T, b: T, tester: (T, T) -> Boolean = { f, s -> f == s}, message: () -> String) { + if (!tester(a, b)) { + throw IllegalStateException("${message()}\nLeft: $a\nRight: $b") + } +} diff --git a/neoforge/kfflib/src/test/resources/META-INF/mods.toml b/neoforge/kfflib/src/test/resources/META-INF/mods.toml new file mode 100644 index 00000000..cf03801c --- /dev/null +++ b/neoforge/kfflib/src/test/resources/META-INF/mods.toml @@ -0,0 +1,22 @@ +modLoader="kotlinforforge" +loaderVersion="[4,)" +license="LGPL v2.1" + +[[mods]] +displayName="Kotlin for Forge Library Test" +modId="kfflibtest" +version="${kff_version}" +authors="TheDarkColour" +credits="Herobrine knows all. Thanks to Username404-59 and TheOnlyTails on GitHub" +issueTrackerURL="https://github.com/thedarkcolour/KotlinForForge/issues" +description=''' +Kotlin for Forge. Allows mods to use the Kotlin programming language. +''' + +[[dependencies.kotlinforforge]] +modId="minecraft" +mandatory=true +type="required" +versionRange="[${min_mc_version},${unsupported_mc_version})" +ordering="NONE" +side="BOTH" diff --git a/neoforge/kfflib/src/test/resources/data/kfflibtest/structures/dummy.nbt b/neoforge/kfflib/src/test/resources/data/kfflibtest/structures/dummy.nbt new file mode 100644 index 00000000..42e2684a Binary files /dev/null and b/neoforge/kfflib/src/test/resources/data/kfflibtest/structures/dummy.nbt differ diff --git a/neoforge/kfflib/src/test/resources/pack.mcmeta b/neoforge/kfflib/src/test/resources/pack.mcmeta new file mode 100644 index 00000000..17e4fa33 --- /dev/null +++ b/neoforge/kfflib/src/test/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Kotlin for Forge Library Test resources", + "pack_format": 7 + } +} \ No newline at end of file