From ec73d3e8691dd0076d8fe35ebb12eb98b22cb69c Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Thu, 29 Aug 2024 21:17:32 -0400 Subject: [PATCH 01/20] refactor: change native library name to xxdc_oss_tictactoe --- .../org/xxdc/oss/example/interop/TicTacToeLibrary.java | 2 +- app/src/main/rust/Cargo.toml | 9 +++++++-- .../test/java/org/xxdc/oss/example/TcpTransportTest.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java index cc21a41..f4be3a4 100644 --- a/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java +++ b/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java @@ -20,7 +20,7 @@ public final class TicTacToeLibrary { private static final Logger log = System.getLogger(MethodHandles.lookup().lookupClass().getName()); - static final String LIBRARY_NAME = "tictactoe"; + static final String LIBRARY_NAME = "xxdc_oss_tictactoe"; private final Arena arena = Arena.ofAuto(); diff --git a/app/src/main/rust/Cargo.toml b/app/src/main/rust/Cargo.toml index f5d4c18..aa49fec 100644 --- a/app/src/main/rust/Cargo.toml +++ b/app/src/main/rust/Cargo.toml @@ -1,7 +1,10 @@ [package] -name = "tictactoe" -version = "0.1.0" +name = "xxdc-oss-tictactoe" +version = "1.0.0" +authors = ["Brian Corbin "] edition = "2021" +description = "A tic-tac-toe game library written in rust built for FFI." +license = "MIT" [dependencies] @@ -9,3 +12,5 @@ edition = "2021" path = "tictactoe/lib.rs" crate-type = ["cdylib"] +[package.metadata] +repository = "https://github.com/briancorbinxyz/overengineering-tictactoe" \ No newline at end of file diff --git a/app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java index ccecaa1..0b23ad6 100644 --- a/app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java +++ b/app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java @@ -24,7 +24,7 @@ public class TcpTransportTest { private static final int SERVER_PORT = getAvailablePort(); - @Test + @Test(timeOut = 30000) public void testCanCreateClientServerBotGame() throws Exception { createClientServerGame(BotPlayer::new, BotPlayer::new); } From 5bf233ca971d7e12de96923f3d08a26338c4f57a Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Thu, 29 Aug 2024 22:20:37 -0400 Subject: [PATCH 02/20] refactor: split libraries and apis from executable app --- app/build.gradle.kts | 133 +----------- lib/src/build.gradle.kts | 189 ++++++++++++++++++ lib/src/main/java/org/.DS_Store | Bin 0 -> 8196 bytes .../java/org/xxdc/oss/example/BotPlayer.java | 0 .../main/java/org/xxdc/oss/example/Game.java | 0 .../java/org/xxdc/oss/example/GameBoard.java | 0 .../oss/example/GameBoardDefaultImpl.java | 0 .../xxdc/oss/example/GameBoardNativeImpl.java | 0 .../org/xxdc/oss/example/GamePersistence.java | 0 .../oss/example/GameServiceException.java | 0 .../java/org/xxdc/oss/example/GameState.java | 0 .../org/xxdc/oss/example/HumanPlayer.java | 0 .../oss/example/InvalidMoveException.java | 0 .../xxdc/oss/example/JsonSerializable.java | 0 .../org/xxdc/oss/example/MessageHandler.java | 0 .../java/org/xxdc/oss/example/Player.java | 0 .../java/org/xxdc/oss/example/PlayerNode.java | 0 .../org/xxdc/oss/example/PlayerNodes.java | 0 .../org/xxdc/oss/example/PlayerPrinter.java | 0 .../oss/example/RemoteMessageHandler.java | 0 .../oss/example/SecureMessageHandler.java | 0 .../org/xxdc/oss/example/bot/AlphaBeta.java | 0 .../org/xxdc/oss/example/bot/BotStrategy.java | 0 .../oss/example/bot/BotStrategyConfig.java | 0 .../java/org/xxdc/oss/example/bot/MaxN.java | 0 .../org/xxdc/oss/example/bot/Minimax.java | 0 .../oss/example/bot/MonteCarloTreeSearch.java | 0 .../org/xxdc/oss/example/bot/Paranoid.java | 0 .../java/org/xxdc/oss/example/bot/Random.java | 0 .../xxdc/oss/example/interop/PlayerIds.java | 0 .../example/interop/TicTacToeGameBoard.java | 0 .../oss/example/interop/TicTacToeLibrary.java | 0 .../example/security/KyberKEMProvider.java | 0 .../oss/example/security/KyberKEMSpi.java | 0 .../example/security/KyberParametersSpi.java | 0 .../oss/example/security/KyberParams.java | 0 .../transport/TransportConfiguration.java | 0 .../example/transport/TransportException.java | 0 .../example/transport/TransportServer.java | 0 .../oss/example/transport/Transports.java | 0 .../example/transport/tcp/TcpProtocol.java | 0 .../transport/tcp/TcpTransportClient.java | 0 .../transport/tcp/TcpTransportServer.java | 0 .../META-INF/services/javax.crypto.KEMSpi | 1 + {app => lib}/src/test/java/GameBoardTest.java | 0 .../org/xxdc/oss/example/AlphaBetaTest.java | 0 .../java/org/xxdc/oss/example/AppTest.java | 0 .../oss/example/GameBoardNativeImplTest.java | 0 .../xxdc/oss/example/GamePerformanceTest.java | 0 .../org/xxdc/oss/example/GameStateTest.java | 0 .../java/org/xxdc/oss/example/GameTest.java | 0 .../LocalAlwaysInetAddressResolver.java | 0 ...ocalAlwaysInetAddressResolverProvider.java | 0 .../java/org/xxdc/oss/example/MaxNTest.java | 0 .../org/xxdc/oss/example/MinimaxTest.java | 0 .../oss/example/MonteCarloTreeSearchTest.java | 0 .../org/xxdc/oss/example/ParanoidTest.java | 0 .../oss/example/SecureConnectionTest.java | 0 .../org/xxdc/oss/example/TcpProtocolTest.java | 0 .../xxdc/oss/example/TcpTransportTest.java | 0 .../java/org/xxdc/oss/example/TestData.java | 0 .../oss/example/interop/PlayerIdsTest.java | 0 .../interop/TicTacToeGameBoardTest.java | 0 .../example/interop/TicTacToeLibraryTest.java | 0 .../interop/benchmark/PlayerIdsBenchmark.java | 0 .../java.net.spi.InetAddressResolverProvider | 1 + lib/src/test/resources/logback-test.xml | 20 ++ {app => native}/src/main/rust/Cargo.toml | 0 native/src/main/rust/build.gradle.kts | 154 ++++++++++++++ .../src/main/rust/tictactoe/.DS_Store | Bin .../src/main/rust/tictactoe/lib.rs | 0 settings.gradle.kts | 2 + 72 files changed, 370 insertions(+), 130 deletions(-) create mode 100644 lib/src/build.gradle.kts create mode 100644 lib/src/main/java/org/.DS_Store rename {app => lib}/src/main/java/org/xxdc/oss/example/BotPlayer.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/Game.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GameBoard.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GamePersistence.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GameServiceException.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/GameState.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/HumanPlayer.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/InvalidMoveException.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/JsonSerializable.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/MessageHandler.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/Player.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/PlayerNode.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/PlayerNodes.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/PlayerPrinter.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/MaxN.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/Minimax.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/Paranoid.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/bot/Random.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/security/KyberParams.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/TransportException.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/TransportServer.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/Transports.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java (100%) rename {app => lib}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java (100%) create mode 100644 lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi rename {app => lib}/src/test/java/GameBoardTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/AppTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/GameStateTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/GameTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/MaxNTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/MinimaxTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/ParanoidTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/TcpTransportTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/TestData.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java (100%) rename {app => lib}/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java (100%) rename {app/src/main => lib/src/test}/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java (100%) create mode 100644 lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider create mode 100644 lib/src/test/resources/logback-test.xml rename {app => native}/src/main/rust/Cargo.toml (100%) create mode 100644 native/src/main/rust/build.gradle.kts rename {app => native}/src/main/rust/tictactoe/.DS_Store (100%) rename {app => native}/src/main/rust/tictactoe/lib.rs (100%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c54d41b..a4f2f17 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,67 +16,6 @@ repositories { val jdkVersion = "22" -// JDK22: Foreign Function Interface (FFI) -// Support building native Rust library using Cargo: -// https://doc.rust-lang.org/cargo/getting-started/installation.html -val osName = System.getProperty("os.name").lowercase() - -val cargoBuildDir = file("${layout.buildDirectory.get()}/cargo") - -val libPath = when { - osName.contains("win") -> "${cargoBuildDir}/debug" - osName.contains("mac") -> "${cargoBuildDir}/debug" - osName.contains("nux") -> "${cargoBuildDir}/debug" - else -> throw GradleException("Unsupported OS") -} -val libName = System.mapLibraryName("tictactoe") - -// -// Rust Build Start -// -tasks.register("formatRust") { - workingDir = file("src/main/rust") - commandLine = listOf("cargo", "fmt") -} -// alias for formatRust -tasks.register("cargoFmt") { - dependsOn("formatRust") -} - -tasks.register("buildRust") { - // https://doc.rust-lang.org/cargo/reference/environment-variables.html - environment("CARGO_TARGET_DIR", cargoBuildDir.absolutePath) - workingDir = file("src/main/rust") - commandLine = listOf("cargo", "build") -} -// alias for buildRust -tasks.register("cargoBuild") { - dependsOn("buildRust") -} - -tasks.named("compileJava") { - dependsOn("buildRust") -} -// clean up the Rust build artifacts -tasks.register("cleanRust") { - delete(cargoBuildDir) -} -tasks.named("clean") { - dependsOn("cleanRust") -} -// Format Rust and java code together -tasks.named("spotlessApply") { - dependsOn("formatRust") -} -tasks.named("jar") { - from("${cargoBuildDir}/debug") { - include(libName) - } -} -// -// Rust Build End -// - // Automatic code formatting before compile tasks.named("compileJava") { dependsOn("spotlessApply") @@ -87,6 +26,7 @@ dependencies { // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on // -> JDK API -> Bouncycastle implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + implementation(project(":lib")) // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback @@ -101,14 +41,6 @@ dependencies { annotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37") } -// Run JMH benchmark -// ./gradlew jmh -tasks.register("jmh") { - mainClass.set("org.openjdk.jmh.Main") - classpath = sourceSets["main"].runtimeClasspath - args = listOf("org.xxdc.oss.example.interop.benchmark.PlayerIdsBenchmark") -} - testing { suites { // Configure the built-in test suite @@ -186,7 +118,7 @@ publishing { publications { create("maven") { groupId = "org.xxdc.oss.example" - artifactId = "tictactoe" + artifactId = "tictactoe-app" version = "1.0.0-jdk$jdkVersion" from(components["java"]) pom { @@ -214,63 +146,4 @@ publishing { } } } -} - -tasks.withType().all { - systemProperty( - "java.library.path", libPath - ) - - // JDK22: Foreign Function Interface (FFI) - // Resolves Warning: - // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called - // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module - // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module - // WARNING: Restricted methods will be blocked in a future release unless native access is enabled - jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") - environment("PATH", libPath) // For Windows - environment("LD_LIBRARY_PATH", libPath) // For Linux - environment("DYLD_LIBRARY_PATH", libPath) // For macOS -} - -tasks.named("run") { - systemProperty( - "java.library.path", libPath - ) - - // JDK22: Foreign Function Interface (FFI) - // Resolves Warning: - // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called - // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module - // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module - // WARNING: Restricted methods will be blocked in a future release unless native access is enabled - jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") - environment("PATH", libPath) // For Windows - environment("LD_LIBRARY_PATH", libPath) // For Linux - environment("DYLD_LIBRARY_PATH", libPath) // For macOS -} - - -/// Install a pre-commit hook to run the Gradle task "spotlessApply" before committing changes. -tasks.register("installGitHook") { - doLast { - val hooksDir = file("${rootDir}/.git/hooks") - val preCommitFile = File(hooksDir, "pre-commit") - - if (!preCommitFile.exists()) { - preCommitFile.writeText( - """ - #!/bin/sh - ./gradlew spotlessApply - """.trimIndent() - ) - preCommitFile.setExecutable(true) - println("Pre-commit hook installed.") - } else { - println("Pre-commit hook already exists.") - } - } -} -tasks.named("build") { - dependsOn("installGitHook") -} +} \ No newline at end of file diff --git a/lib/src/build.gradle.kts b/lib/src/build.gradle.kts new file mode 100644 index 0000000..bfe1f75 --- /dev/null +++ b/lib/src/build.gradle.kts @@ -0,0 +1,189 @@ +import java.io.File + +plugins { + `java-library` + id("org.graalvm.buildtools.native") version "0.10.2" + id("com.diffplug.spotless") version "7.0.0.BETA1" + `maven-publish` +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() + gradlePluginPortal() +} + +val jdkVersion = "22" + +// Automatic code formatting before compile +tasks.named("compileJava") { + dependsOn("spotlessApply") +} + +dependencies { + // JDK21: KEM SPI (Third-Party) + // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on + // -> JDK API -> Bouncycastle + implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + + // JDK9: Platform Logging (Third-Party) + // -> JDK API -> SLF4J -> Logback + testRuntimeOnly("ch.qos.logback:logback-classic:1.5.6") + testRuntimeOnly("org.slf4j:slf4j-api:2.0.13") + testRuntimeOnly("org.slf4j:slf4j-jdk-platform-logging:2.0.13") + + + // JDK23: JMH (Third-Party) Not required, added for benchmarking + // https://github.com/openjdk/jmh + testImplementation("org.openjdk.jmh:jmh-core:1.37") + annotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37") +} + +// Run JMH benchmark +// ./gradlew jmh +tasks.register("jmh") { + mainClass.set("org.openjdk.jmh.Main") + classpath = sourceSets["test"].runtimeClasspath + args = listOf("org.xxdc.oss.example.interop.benchmark.PlayerIdsBenchmark") +} + +testing { + suites { + // Configure the built-in test suite + val test by getting(JvmTestSuite::class) { + // Use TestNG test framework + useTestNG("7.5.1") + } + } +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(jdkVersion) + } + withJavadocJar() + withSourcesJar() +} + +// Code formatting (./gradlew spotlessApply) +spotless { + java { + googleJavaFormat("1.23.0") + .reflowLongStrings() + } +} + +// Allow GraalVM native AOT compilation +graalvmNative { + binaries { + all { + javaLauncher = javaToolchains.launcherFor { + // NB: On MacOS ARM ARCH the native-image implementation is not available + // for the versions of GRAAL_VM Community edition - selecting Oracle + languageVersion = JavaLanguageVersion.of(jdkVersion) + vendor = JvmVendorSpec.matching("Oracle") + // languageVersion = JavaLanguageVersion.of(17) + // vendor = JvmVendorSpec.GRAAL_VM + } + } + } +} + +application { + // Define the main class for the application. + mainClass = "org.xxdc.oss.example.App" + // JDK22: Foreign Function Interface (FFI) + // Resolves Warning: + // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called + // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module + // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module + // WARNING: Restricted methods will be blocked in a future release unless native access is enabled + applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") +} + +tasks.run.configure { + // Override the empty stream to allow for interactive runs with gradlew run + standardInput = System.`in` +} + +// https://docs.gradle.org/current/userguide/publishing_maven.html +publishing { + repositories { + // Publish to GitHub Packages + // https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } + } + publications { + create("maven") { + groupId = "org.xxdc.oss.example" + artifactId = "tictactoe" + version = "1.0.0-jdk$jdkVersion" + from(components["java"]) + pom { + name.set("tictactoe") + description.set("An Over-Engineered Tic Tac Toe game") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + licenses { + license { + name.set("MIT License") + url.set("https://opensource.org/licenses/MIT") + } + developers { + developer { + id.set("briancorbinxyz") + name.set("Brian Corbin") + email.set("mail@briancorbin.xyz") + } + } + } + scm { + connection.set("scm:git:git://github.com/briancorbinxyz/overengineering-tictactoe.git") + developerConnection.set("scm:git:ssh://github.com/briancorbinxyz/overengineering-tictactoe.git") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + } + } + } + } +} + +tasks.withType().all { + systemProperty( + "java.library.path", libPath + ) + + // JDK22: Foreign Function Interface (FFI) + // Resolves Warning: + // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called + // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module + // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module + // WARNING: Restricted methods will be blocked in a future release unless native access is enabled + jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") + environment("PATH", libPath) // For Windows + environment("LD_LIBRARY_PATH", libPath) // For Linux + environment("DYLD_LIBRARY_PATH", libPath) // For macOS +} + +tasks.named("run") { + systemProperty( + "java.library.path", libPath + ) + + // JDK22: Foreign Function Interface (FFI) + // Resolves Warning: + // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called + // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module + // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module + // WARNING: Restricted methods will be blocked in a future release unless native access is enabled + jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") + environment("PATH", libPath) // For Windows + environment("LD_LIBRARY_PATH", libPath) // For Linux + environment("DYLD_LIBRARY_PATH", libPath) // For macOS +} \ No newline at end of file diff --git a/lib/src/main/java/org/.DS_Store b/lib/src/main/java/org/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b82922a21c1adcd8f5ef95818550a19c545be7e6 GIT binary patch literal 8196 zcmeHMTWl0n7(U;$r85JSS+0d{*3}9KG_Xr47ZF&tMOpzXY|EAwD6^dbCd|&1o!M;> z(Rz6!7&V%R#CUmBgC@opLp1Thi29^yFlf9azUYH7KKP*V!T-#eZAwcYOpF?wlbrLP z|8{2of4={mo+)Dt*|L#ltdTJ$Q|DBhO~VZu*YiHDNui{cD9E3&VP-S4Fyajt>}l4K zAj&|LfhYq}2BHi^8Tc<`fX-~*n$tZX@JWU=6Vfq316Nv8+#V3RBD`WixRXA~&5340Iwq)aXAtfT;gb>GP!K$w z{E~q=Lq^bOj4}{qV0s34`P4I$jWV4TC*R-Q0%m+vYHI6d)l-{Ye>iu- z9m)B+U-q(kX@GM*u4@;nbG>lDGDilr1?{fm>y~4d0td@5$>dPpatwDQ>y{nE3w(1z zp->ytn$Da_r_$?Mk{i}Rl26inBOjcBNPCTFQJJ>&{X=TscZ`!(VRm=|EH@!h^u18UJ z4Y|(!C9^`&<%e9mTy#hi8`ABeG8vmYY&&N?V0wKP+3{JQZ;twW((kz?zdi8i*GGFo z>7X``FV#KlTdw0BFiRd$MY+_lDABy4CB0!wW@p#ledD*x)ne*=bz#0@c~-$Tdq*t8 zKXFj^hD^t>oa6nJKgYL@S!PLzNxv#Rj$Sm!V~ZP?ENzO%r&?E6=_?nkQ)SCPtu@QC zct$dQyP~}61=ALyP3m2;wp+SVVDNR zy<~A?ES`(^%G&4TqghVHB3#pJ5b&0pGbw?qAgRq6`eer20zoE5etI>rHFQ>WvO`o& z4m-^rXBXJ>>>_)Wy~QrG57?*dD*J+c$-ZLWu^-vb>^A@^=AZ$$U@;o86iv7jX{_RZD1KBA#F(brYGgH&6-Dw>@4$ z(%214D6wl-XV<;|b_rc;;6*cU{(^;zZoOmWn%0eX@0h@qKw=eqlR+Ke_&W3lI1XO@ zQ~-+VQWL!dbaQm3BGc+Qq~&pLoxDv=XmwNyf?c+p*jZEtf?c&bkr%-~22k{Ug z_z}YJIh@CncnVMBB3{6YcnL2DSbh`lPC@7H>(Pl%rlYfxD>=6794Ac`)7Lvi+~f?@ z2&R$W|98&({eQAcG>b!BgF+l?t8n_9U2iI|; k6aO%zc{0>xLOLcWX{i0<9|EHD|F8V@8lC?EN^id5PeuhfAOHXW literal 0 HcmV?d00001 diff --git a/app/src/main/java/org/xxdc/oss/example/BotPlayer.java b/lib/src/main/java/org/xxdc/oss/example/BotPlayer.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/BotPlayer.java rename to lib/src/main/java/org/xxdc/oss/example/BotPlayer.java diff --git a/app/src/main/java/org/xxdc/oss/example/Game.java b/lib/src/main/java/org/xxdc/oss/example/Game.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/Game.java rename to lib/src/main/java/org/xxdc/oss/example/Game.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameBoard.java b/lib/src/main/java/org/xxdc/oss/example/GameBoard.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameBoard.java rename to lib/src/main/java/org/xxdc/oss/example/GameBoard.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java b/lib/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java rename to lib/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java b/lib/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java rename to lib/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java diff --git a/app/src/main/java/org/xxdc/oss/example/GamePersistence.java b/lib/src/main/java/org/xxdc/oss/example/GamePersistence.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GamePersistence.java rename to lib/src/main/java/org/xxdc/oss/example/GamePersistence.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameServiceException.java b/lib/src/main/java/org/xxdc/oss/example/GameServiceException.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameServiceException.java rename to lib/src/main/java/org/xxdc/oss/example/GameServiceException.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameState.java b/lib/src/main/java/org/xxdc/oss/example/GameState.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameState.java rename to lib/src/main/java/org/xxdc/oss/example/GameState.java diff --git a/app/src/main/java/org/xxdc/oss/example/HumanPlayer.java b/lib/src/main/java/org/xxdc/oss/example/HumanPlayer.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/HumanPlayer.java rename to lib/src/main/java/org/xxdc/oss/example/HumanPlayer.java diff --git a/app/src/main/java/org/xxdc/oss/example/InvalidMoveException.java b/lib/src/main/java/org/xxdc/oss/example/InvalidMoveException.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/InvalidMoveException.java rename to lib/src/main/java/org/xxdc/oss/example/InvalidMoveException.java diff --git a/app/src/main/java/org/xxdc/oss/example/JsonSerializable.java b/lib/src/main/java/org/xxdc/oss/example/JsonSerializable.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/JsonSerializable.java rename to lib/src/main/java/org/xxdc/oss/example/JsonSerializable.java diff --git a/app/src/main/java/org/xxdc/oss/example/MessageHandler.java b/lib/src/main/java/org/xxdc/oss/example/MessageHandler.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/MessageHandler.java rename to lib/src/main/java/org/xxdc/oss/example/MessageHandler.java diff --git a/app/src/main/java/org/xxdc/oss/example/Player.java b/lib/src/main/java/org/xxdc/oss/example/Player.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/Player.java rename to lib/src/main/java/org/xxdc/oss/example/Player.java diff --git a/app/src/main/java/org/xxdc/oss/example/PlayerNode.java b/lib/src/main/java/org/xxdc/oss/example/PlayerNode.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/PlayerNode.java rename to lib/src/main/java/org/xxdc/oss/example/PlayerNode.java diff --git a/app/src/main/java/org/xxdc/oss/example/PlayerNodes.java b/lib/src/main/java/org/xxdc/oss/example/PlayerNodes.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/PlayerNodes.java rename to lib/src/main/java/org/xxdc/oss/example/PlayerNodes.java diff --git a/app/src/main/java/org/xxdc/oss/example/PlayerPrinter.java b/lib/src/main/java/org/xxdc/oss/example/PlayerPrinter.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/PlayerPrinter.java rename to lib/src/main/java/org/xxdc/oss/example/PlayerPrinter.java diff --git a/app/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java b/lib/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java rename to lib/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java diff --git a/app/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java b/lib/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java rename to lib/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java b/lib/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java rename to lib/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java b/lib/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java rename to lib/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java b/lib/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java rename to lib/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/MaxN.java b/lib/src/main/java/org/xxdc/oss/example/bot/MaxN.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/MaxN.java rename to lib/src/main/java/org/xxdc/oss/example/bot/MaxN.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/Minimax.java b/lib/src/main/java/org/xxdc/oss/example/bot/Minimax.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/Minimax.java rename to lib/src/main/java/org/xxdc/oss/example/bot/Minimax.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java b/lib/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java rename to lib/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/Paranoid.java b/lib/src/main/java/org/xxdc/oss/example/bot/Paranoid.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/Paranoid.java rename to lib/src/main/java/org/xxdc/oss/example/bot/Paranoid.java diff --git a/app/src/main/java/org/xxdc/oss/example/bot/Random.java b/lib/src/main/java/org/xxdc/oss/example/bot/Random.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/bot/Random.java rename to lib/src/main/java/org/xxdc/oss/example/bot/Random.java diff --git a/app/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java b/lib/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java rename to lib/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java diff --git a/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java b/lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java rename to lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java diff --git a/app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java rename to lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java diff --git a/app/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java b/lib/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java rename to lib/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java diff --git a/app/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java b/lib/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java rename to lib/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java diff --git a/app/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java b/lib/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java rename to lib/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java diff --git a/app/src/main/java/org/xxdc/oss/example/security/KyberParams.java b/lib/src/main/java/org/xxdc/oss/example/security/KyberParams.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/security/KyberParams.java rename to lib/src/main/java/org/xxdc/oss/example/security/KyberParams.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java b/lib/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java rename to lib/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/TransportException.java b/lib/src/main/java/org/xxdc/oss/example/transport/TransportException.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/TransportException.java rename to lib/src/main/java/org/xxdc/oss/example/transport/TransportException.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/TransportServer.java b/lib/src/main/java/org/xxdc/oss/example/transport/TransportServer.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/TransportServer.java rename to lib/src/main/java/org/xxdc/oss/example/transport/TransportServer.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/Transports.java b/lib/src/main/java/org/xxdc/oss/example/transport/Transports.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/Transports.java rename to lib/src/main/java/org/xxdc/oss/example/transport/Transports.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java b/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java rename to lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java b/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java rename to lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java diff --git a/app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java rename to lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java diff --git a/lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi b/lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi new file mode 100644 index 0000000..be5e21c --- /dev/null +++ b/lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi @@ -0,0 +1 @@ +org.xxdc.oss.example.KyberKEM \ No newline at end of file diff --git a/app/src/test/java/GameBoardTest.java b/lib/src/test/java/GameBoardTest.java similarity index 100% rename from app/src/test/java/GameBoardTest.java rename to lib/src/test/java/GameBoardTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java b/lib/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java rename to lib/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/AppTest.java b/lib/src/test/java/org/xxdc/oss/example/AppTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/AppTest.java rename to lib/src/test/java/org/xxdc/oss/example/AppTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java b/lib/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java rename to lib/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java b/lib/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java rename to lib/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/GameStateTest.java b/lib/src/test/java/org/xxdc/oss/example/GameStateTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/GameStateTest.java rename to lib/src/test/java/org/xxdc/oss/example/GameStateTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/GameTest.java b/lib/src/test/java/org/xxdc/oss/example/GameTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/GameTest.java rename to lib/src/test/java/org/xxdc/oss/example/GameTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java b/lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java rename to lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java diff --git a/app/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java b/lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java rename to lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java diff --git a/app/src/test/java/org/xxdc/oss/example/MaxNTest.java b/lib/src/test/java/org/xxdc/oss/example/MaxNTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/MaxNTest.java rename to lib/src/test/java/org/xxdc/oss/example/MaxNTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/MinimaxTest.java b/lib/src/test/java/org/xxdc/oss/example/MinimaxTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/MinimaxTest.java rename to lib/src/test/java/org/xxdc/oss/example/MinimaxTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java b/lib/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java rename to lib/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/ParanoidTest.java b/lib/src/test/java/org/xxdc/oss/example/ParanoidTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/ParanoidTest.java rename to lib/src/test/java/org/xxdc/oss/example/ParanoidTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java b/lib/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java rename to lib/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java b/lib/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java rename to lib/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/lib/src/test/java/org/xxdc/oss/example/TcpTransportTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/TcpTransportTest.java rename to lib/src/test/java/org/xxdc/oss/example/TcpTransportTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/TestData.java b/lib/src/test/java/org/xxdc/oss/example/TestData.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/TestData.java rename to lib/src/test/java/org/xxdc/oss/example/TestData.java diff --git a/app/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java b/lib/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java rename to lib/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java b/lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java rename to lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java diff --git a/app/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java b/lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java similarity index 100% rename from app/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java rename to lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java diff --git a/app/src/main/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java b/lib/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java rename to lib/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java diff --git a/lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider b/lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider new file mode 100644 index 0000000..a3aeb3e --- /dev/null +++ b/lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider @@ -0,0 +1 @@ +org.xxdc.oss.example.LocalAlwaysInetAddressResolverProvider \ No newline at end of file diff --git a/lib/src/test/resources/logback-test.xml b/lib/src/test/resources/logback-test.xml new file mode 100644 index 0000000..ad6ebf5 --- /dev/null +++ b/lib/src/test/resources/logback-test.xml @@ -0,0 +1,20 @@ + + + + + + + + + + %boldWhite(%date) %yellow([%thread]) %highlight(%-5level) %cyan(%-20logger{20}) - %msg %n + + + ${log.level:-DEBUG} + + + + + + + \ No newline at end of file diff --git a/app/src/main/rust/Cargo.toml b/native/src/main/rust/Cargo.toml similarity index 100% rename from app/src/main/rust/Cargo.toml rename to native/src/main/rust/Cargo.toml diff --git a/native/src/main/rust/build.gradle.kts b/native/src/main/rust/build.gradle.kts new file mode 100644 index 0000000..076ad4b --- /dev/null +++ b/native/src/main/rust/build.gradle.kts @@ -0,0 +1,154 @@ +import java.io.File + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + library +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() + gradlePluginPortal() +} + +val jdkVersion = "22" + +// JDK22: Foreign Function Interface (FFI) +// Support building native Rust library using Cargo: +// https://doc.rust-lang.org/cargo/getting-started/installation.html +val osName = System.getProperty("os.name").lowercase() + +val cargoBuildDir = file("${layout.buildDirectory.get()}/cargo") + +val libPath = when { + osName.contains("win") -> "${cargoBuildDir}/debug" + osName.contains("mac") -> "${cargoBuildDir}/debug" + osName.contains("nux") -> "${cargoBuildDir}/debug" + else -> throw GradleException("Unsupported OS") +} +val libName = System.mapLibraryName("tictactoe") + +// +// Rust Build Start +// +tasks.register("formatRust") { + workingDir = file("src/main/rust") + commandLine = listOf("cargo", "fmt") +} +// alias for formatRust +tasks.register("cargoFmt") { + dependsOn("formatRust") +} + +tasks.register("buildRust") { + // https://doc.rust-lang.org/cargo/reference/environment-variables.html + environment("CARGO_TARGET_DIR", cargoBuildDir.absolutePath) + workingDir = file("src/main/rust") + commandLine = listOf("cargo", "build") +} +// alias for buildRust +tasks.register("cargoBuild") { + dependsOn("buildRust") +} + +tasks.named("compileJava") { + dependsOn("buildRust") +} +// clean up the Rust build artifacts +tasks.register("cleanRust") { + delete(cargoBuildDir) +} +tasks.named("clean") { + dependsOn("cleanRust") +} +// Force a build before running the application +tasks.named("build") { + dependsOn("formatRust") +} +tasks.named("jar") { + from("${cargoBuildDir}/debug") { + include(libName) + } +} +// +// Rust Build End +// +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(jdkVersion) + } + withJavadocJar() + withSourcesJar() +} + +// https://docs.gradle.org/current/userguide/publishing_maven.html +publishing { + repositories { + // Publish to GitHub Packages + // https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } + } + publications { + create("maven") { + groupId = "org.xxdc.oss.example" + artifactId = "native-tictactoe" + version = "1.0.0-jdk$jdkVersion" + from(components["java"]) + pom { + name.set("tictactoe") + description.set("An Over-Engineered Tic Tac Toe game") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + licenses { + license { + name.set("MIT License") + url.set("https://opensource.org/licenses/MIT") + } + developers { + developer { + id.set("briancorbinxyz") + name.set("Brian Corbin") + email.set("mail@briancorbin.xyz") + } + } + } + scm { + connection.set("scm:git:git://github.com/briancorbinxyz/overengineering-tictactoe.git") + developerConnection.set("scm:git:ssh://github.com/briancorbinxyz/overengineering-tictactoe.git") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + } + } + } + } +} + +/// Install a pre-commit hook to run the Gradle task "spotlessApply" before committing changes. +tasks.register("installGitHook") { + doLast { + val hooksDir = file("${rootDir}/.git/hooks") + val preCommitFile = File(hooksDir, "pre-commit") + + if (!preCommitFile.exists()) { + preCommitFile.writeText( + """ + #!/bin/sh + ./gradlew spotlessApply + """.trimIndent() + ) + preCommitFile.setExecutable(true) + println("Pre-commit hook installed.") + } else { + println("Pre-commit hook already exists.") + } + } +} +tasks.named("build") { + dependsOn("installGitHook") +} diff --git a/app/src/main/rust/tictactoe/.DS_Store b/native/src/main/rust/tictactoe/.DS_Store similarity index 100% rename from app/src/main/rust/tictactoe/.DS_Store rename to native/src/main/rust/tictactoe/.DS_Store diff --git a/app/src/main/rust/tictactoe/lib.rs b/native/src/main/rust/tictactoe/lib.rs similarity index 100% rename from app/src/main/rust/tictactoe/lib.rs rename to native/src/main/rust/tictactoe/lib.rs diff --git a/settings.gradle.kts b/settings.gradle.kts index 9d67d42..6fc2c33 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,4 +12,6 @@ plugins { } rootProject.name = "overengineering-tictactoe" +include("lib") include("app") +include("native") From b95c8eda46abba416eb044abc186973a37fc6eed Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Thu, 29 Aug 2024 23:44:53 -0400 Subject: [PATCH 03/20] refactor: fix build dependency with api project --- {lib/src => api}/build.gradle.kts | 55 +----------------- {lib => api}/src/main/java/org/.DS_Store | Bin .../java/org/xxdc/oss/example/BotPlayer.java | 0 .../main/java/org/xxdc/oss/example/Game.java | 0 .../java/org/xxdc/oss/example/GameBoard.java | 0 .../oss/example/GameBoardDefaultImpl.java | 0 .../xxdc/oss/example/GameBoardNativeImpl.java | 0 .../org/xxdc/oss/example/GamePersistence.java | 0 .../oss/example/GameServiceException.java | 0 .../java/org/xxdc/oss/example/GameState.java | 0 .../org/xxdc/oss/example/HumanPlayer.java | 0 .../oss/example/InvalidMoveException.java | 0 .../xxdc/oss/example/JsonSerializable.java | 0 .../org/xxdc/oss/example/MessageHandler.java | 0 .../java/org/xxdc/oss/example/Player.java | 0 .../java/org/xxdc/oss/example/PlayerNode.java | 0 .../org/xxdc/oss/example/PlayerNodes.java | 0 .../org/xxdc/oss/example/PlayerPrinter.java | 0 .../oss/example/RemoteMessageHandler.java | 0 .../oss/example/SecureMessageHandler.java | 0 .../org/xxdc/oss/example/bot/AlphaBeta.java | 0 .../org/xxdc/oss/example/bot/BotStrategy.java | 0 .../oss/example/bot/BotStrategyConfig.java | 0 .../java/org/xxdc/oss/example/bot/MaxN.java | 0 .../org/xxdc/oss/example/bot/Minimax.java | 0 .../oss/example/bot/MonteCarloTreeSearch.java | 0 .../org/xxdc/oss/example/bot/Paranoid.java | 0 .../java/org/xxdc/oss/example/bot/Random.java | 0 .../xxdc/oss/example/interop/PlayerIds.java | 0 .../example/interop/TicTacToeGameBoard.java | 0 .../oss/example/interop/TicTacToeLibrary.java | 0 .../example/security/KyberKEMProvider.java | 0 .../oss/example/security/KyberKEMSpi.java | 0 .../example/security/KyberParametersSpi.java | 0 .../oss/example/security/KyberParams.java | 0 .../transport/TransportConfiguration.java | 0 .../example/transport/TransportException.java | 0 .../example/transport/TransportServer.java | 0 .../oss/example/transport/Transports.java | 0 .../example/transport/tcp/TcpProtocol.java | 0 .../transport/tcp/TcpTransportClient.java | 0 .../transport/tcp/TcpTransportServer.java | 0 .../META-INF/services/javax.crypto.KEMSpi | 0 {lib => api}/src/test/java/GameBoardTest.java | 0 .../org/xxdc/oss/example/AlphaBetaTest.java | 0 .../oss/example/GameBoardNativeImplTest.java | 0 .../xxdc/oss/example/GamePerformanceTest.java | 0 .../org/xxdc/oss/example/GameStateTest.java | 0 .../java/org/xxdc/oss/example/GameTest.java | 0 .../LocalAlwaysInetAddressResolver.java | 0 ...ocalAlwaysInetAddressResolverProvider.java | 0 .../java/org/xxdc/oss/example/MaxNTest.java | 0 .../org/xxdc/oss/example/MinimaxTest.java | 0 .../oss/example/MonteCarloTreeSearchTest.java | 0 .../org/xxdc/oss/example/ParanoidTest.java | 0 .../oss/example/SecureConnectionTest.java | 0 .../org/xxdc/oss/example/TcpProtocolTest.java | 0 .../xxdc/oss/example/TcpTransportTest.java | 0 .../java/org/xxdc/oss/example/TestData.java | 0 .../oss/example/interop/PlayerIdsTest.java | 0 .../interop/TicTacToeGameBoardTest.java | 0 .../example/interop/TicTacToeLibraryTest.java | 0 .../interop/benchmark/PlayerIdsBenchmark.java | 0 .../java.net.spi.InetAddressResolverProvider | 0 .../src/test/resources/logback-test.xml | 0 app/build.gradle.kts | 14 ++--- app/src/main/resources/logback.xml | 2 +- .../java/org/xxdc/oss/example/AppTest.java | 0 buildSrc/build.gradle.kts | 13 +++++ buildSrc/settings.gradle.kts | 14 +++++ ...ic.java-application-conventions.gradle.kts | 11 ++++ ...ldlogic.java-common-conventions.gradle.kts | 37 ++++++++++++ ...dlogic.java-library-conventions.gradle.kts | 11 ++++ native/src/main/rust/build.gradle.kts | 2 +- settings.gradle.kts | 2 +- 75 files changed, 96 insertions(+), 65 deletions(-) rename {lib/src => api}/build.gradle.kts (66%) rename {lib => api}/src/main/java/org/.DS_Store (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/BotPlayer.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/Game.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GameBoard.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GamePersistence.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GameServiceException.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/GameState.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/HumanPlayer.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/InvalidMoveException.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/JsonSerializable.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/MessageHandler.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/Player.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/PlayerNode.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/PlayerNodes.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/PlayerPrinter.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/MaxN.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/Minimax.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/Paranoid.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/bot/Random.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/security/KyberParams.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/TransportException.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/TransportServer.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/Transports.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java (100%) rename {lib => api}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java (100%) rename {lib => api}/src/main/resources/META-INF/services/javax.crypto.KEMSpi (100%) rename {lib => api}/src/test/java/GameBoardTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/GameStateTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/GameTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/MaxNTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/MinimaxTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/ParanoidTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/TcpTransportTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/TestData.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java (100%) rename {lib => api}/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java (100%) rename {lib => api}/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider (100%) rename {lib => api}/src/test/resources/logback-test.xml (100%) rename {lib => app}/src/test/java/org/xxdc/oss/example/AppTest.java (100%) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts create mode 100644 buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts create mode 100644 buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts diff --git a/lib/src/build.gradle.kts b/api/build.gradle.kts similarity index 66% rename from lib/src/build.gradle.kts rename to api/build.gradle.kts index bfe1f75..7be1db1 100644 --- a/lib/src/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,12 +1,13 @@ import java.io.File plugins { - `java-library` - id("org.graalvm.buildtools.native") version "0.10.2" + id("buildlogic.java-library-conventions") id("com.diffplug.spotless") version "7.0.0.BETA1" `maven-publish` } +val libPath = "native/src/main/rust/target/debug" + repositories { // Use Maven Central for resolving dependencies. mavenCentral() @@ -74,39 +75,6 @@ spotless { } } -// Allow GraalVM native AOT compilation -graalvmNative { - binaries { - all { - javaLauncher = javaToolchains.launcherFor { - // NB: On MacOS ARM ARCH the native-image implementation is not available - // for the versions of GRAAL_VM Community edition - selecting Oracle - languageVersion = JavaLanguageVersion.of(jdkVersion) - vendor = JvmVendorSpec.matching("Oracle") - // languageVersion = JavaLanguageVersion.of(17) - // vendor = JvmVendorSpec.GRAAL_VM - } - } - } -} - -application { - // Define the main class for the application. - mainClass = "org.xxdc.oss.example.App" - // JDK22: Foreign Function Interface (FFI) - // Resolves Warning: - // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called - // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module - // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module - // WARNING: Restricted methods will be blocked in a future release unless native access is enabled - applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") -} - -tasks.run.configure { - // Override the empty stream to allow for interactive runs with gradlew run - standardInput = System.`in` -} - // https://docs.gradle.org/current/userguide/publishing_maven.html publishing { repositories { @@ -159,23 +127,6 @@ tasks.withType().all { "java.library.path", libPath ) - // JDK22: Foreign Function Interface (FFI) - // Resolves Warning: - // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called - // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module - // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module - // WARNING: Restricted methods will be blocked in a future release unless native access is enabled - jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") - environment("PATH", libPath) // For Windows - environment("LD_LIBRARY_PATH", libPath) // For Linux - environment("DYLD_LIBRARY_PATH", libPath) // For macOS -} - -tasks.named("run") { - systemProperty( - "java.library.path", libPath - ) - // JDK22: Foreign Function Interface (FFI) // Resolves Warning: // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called diff --git a/lib/src/main/java/org/.DS_Store b/api/src/main/java/org/.DS_Store similarity index 100% rename from lib/src/main/java/org/.DS_Store rename to api/src/main/java/org/.DS_Store diff --git a/lib/src/main/java/org/xxdc/oss/example/BotPlayer.java b/api/src/main/java/org/xxdc/oss/example/BotPlayer.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/BotPlayer.java rename to api/src/main/java/org/xxdc/oss/example/BotPlayer.java diff --git a/lib/src/main/java/org/xxdc/oss/example/Game.java b/api/src/main/java/org/xxdc/oss/example/Game.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/Game.java rename to api/src/main/java/org/xxdc/oss/example/Game.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GameBoard.java b/api/src/main/java/org/xxdc/oss/example/GameBoard.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GameBoard.java rename to api/src/main/java/org/xxdc/oss/example/GameBoard.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java b/api/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java rename to api/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java b/api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java rename to api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GamePersistence.java b/api/src/main/java/org/xxdc/oss/example/GamePersistence.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GamePersistence.java rename to api/src/main/java/org/xxdc/oss/example/GamePersistence.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GameServiceException.java b/api/src/main/java/org/xxdc/oss/example/GameServiceException.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GameServiceException.java rename to api/src/main/java/org/xxdc/oss/example/GameServiceException.java diff --git a/lib/src/main/java/org/xxdc/oss/example/GameState.java b/api/src/main/java/org/xxdc/oss/example/GameState.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/GameState.java rename to api/src/main/java/org/xxdc/oss/example/GameState.java diff --git a/lib/src/main/java/org/xxdc/oss/example/HumanPlayer.java b/api/src/main/java/org/xxdc/oss/example/HumanPlayer.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/HumanPlayer.java rename to api/src/main/java/org/xxdc/oss/example/HumanPlayer.java diff --git a/lib/src/main/java/org/xxdc/oss/example/InvalidMoveException.java b/api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/InvalidMoveException.java rename to api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java diff --git a/lib/src/main/java/org/xxdc/oss/example/JsonSerializable.java b/api/src/main/java/org/xxdc/oss/example/JsonSerializable.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/JsonSerializable.java rename to api/src/main/java/org/xxdc/oss/example/JsonSerializable.java diff --git a/lib/src/main/java/org/xxdc/oss/example/MessageHandler.java b/api/src/main/java/org/xxdc/oss/example/MessageHandler.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/MessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/MessageHandler.java diff --git a/lib/src/main/java/org/xxdc/oss/example/Player.java b/api/src/main/java/org/xxdc/oss/example/Player.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/Player.java rename to api/src/main/java/org/xxdc/oss/example/Player.java diff --git a/lib/src/main/java/org/xxdc/oss/example/PlayerNode.java b/api/src/main/java/org/xxdc/oss/example/PlayerNode.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/PlayerNode.java rename to api/src/main/java/org/xxdc/oss/example/PlayerNode.java diff --git a/lib/src/main/java/org/xxdc/oss/example/PlayerNodes.java b/api/src/main/java/org/xxdc/oss/example/PlayerNodes.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/PlayerNodes.java rename to api/src/main/java/org/xxdc/oss/example/PlayerNodes.java diff --git a/lib/src/main/java/org/xxdc/oss/example/PlayerPrinter.java b/api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/PlayerPrinter.java rename to api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java diff --git a/lib/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java diff --git a/lib/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java b/api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java rename to api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java rename to api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java rename to api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/MaxN.java b/api/src/main/java/org/xxdc/oss/example/bot/MaxN.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/MaxN.java rename to api/src/main/java/org/xxdc/oss/example/bot/MaxN.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/Minimax.java b/api/src/main/java/org/xxdc/oss/example/bot/Minimax.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/Minimax.java rename to api/src/main/java/org/xxdc/oss/example/bot/Minimax.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java b/api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java rename to api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/Paranoid.java b/api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/Paranoid.java rename to api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java diff --git a/lib/src/main/java/org/xxdc/oss/example/bot/Random.java b/api/src/main/java/org/xxdc/oss/example/bot/Random.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/bot/Random.java rename to api/src/main/java/org/xxdc/oss/example/bot/Random.java diff --git a/lib/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java b/api/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java rename to api/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java diff --git a/lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java rename to api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java diff --git a/lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java rename to api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java diff --git a/lib/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java rename to api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java diff --git a/lib/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java rename to api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java diff --git a/lib/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java b/api/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java rename to api/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java diff --git a/lib/src/main/java/org/xxdc/oss/example/security/KyberParams.java b/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/security/KyberParams.java rename to api/src/main/java/org/xxdc/oss/example/security/KyberParams.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java b/api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java rename to api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/TransportException.java b/api/src/main/java/org/xxdc/oss/example/transport/TransportException.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/TransportException.java rename to api/src/main/java/org/xxdc/oss/example/transport/TransportException.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/TransportServer.java b/api/src/main/java/org/xxdc/oss/example/transport/TransportServer.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/TransportServer.java rename to api/src/main/java/org/xxdc/oss/example/transport/TransportServer.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/Transports.java b/api/src/main/java/org/xxdc/oss/example/transport/Transports.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/Transports.java rename to api/src/main/java/org/xxdc/oss/example/transport/Transports.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java rename to api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java rename to api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java diff --git a/lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java similarity index 100% rename from lib/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java rename to api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java diff --git a/lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi b/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi similarity index 100% rename from lib/src/main/resources/META-INF/services/javax.crypto.KEMSpi rename to api/src/main/resources/META-INF/services/javax.crypto.KEMSpi diff --git a/lib/src/test/java/GameBoardTest.java b/api/src/test/java/GameBoardTest.java similarity index 100% rename from lib/src/test/java/GameBoardTest.java rename to api/src/test/java/GameBoardTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java b/api/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java rename to api/src/test/java/org/xxdc/oss/example/AlphaBetaTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java b/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java rename to api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java b/api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java rename to api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/GameStateTest.java b/api/src/test/java/org/xxdc/oss/example/GameStateTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/GameStateTest.java rename to api/src/test/java/org/xxdc/oss/example/GameStateTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/GameTest.java b/api/src/test/java/org/xxdc/oss/example/GameTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/GameTest.java rename to api/src/test/java/org/xxdc/oss/example/GameTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java b/api/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java rename to api/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolver.java diff --git a/lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java b/api/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java rename to api/src/test/java/org/xxdc/oss/example/LocalAlwaysInetAddressResolverProvider.java diff --git a/lib/src/test/java/org/xxdc/oss/example/MaxNTest.java b/api/src/test/java/org/xxdc/oss/example/MaxNTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/MaxNTest.java rename to api/src/test/java/org/xxdc/oss/example/MaxNTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/MinimaxTest.java b/api/src/test/java/org/xxdc/oss/example/MinimaxTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/MinimaxTest.java rename to api/src/test/java/org/xxdc/oss/example/MinimaxTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java b/api/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java rename to api/src/test/java/org/xxdc/oss/example/MonteCarloTreeSearchTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/ParanoidTest.java b/api/src/test/java/org/xxdc/oss/example/ParanoidTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/ParanoidTest.java rename to api/src/test/java/org/xxdc/oss/example/ParanoidTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java b/api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java rename to api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java b/api/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java rename to api/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/TcpTransportTest.java rename to api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/TestData.java b/api/src/test/java/org/xxdc/oss/example/TestData.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/TestData.java rename to api/src/test/java/org/xxdc/oss/example/TestData.java diff --git a/lib/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java b/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java rename to api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java b/api/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java rename to api/src/test/java/org/xxdc/oss/example/interop/TicTacToeGameBoardTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java b/api/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java rename to api/src/test/java/org/xxdc/oss/example/interop/TicTacToeLibraryTest.java diff --git a/lib/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java b/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java rename to api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java diff --git a/lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider b/api/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider similarity index 100% rename from lib/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider rename to api/src/test/resources/META-INF/services/java.net.spi.InetAddressResolverProvider diff --git a/lib/src/test/resources/logback-test.xml b/api/src/test/resources/logback-test.xml similarity index 100% rename from lib/src/test/resources/logback-test.xml rename to api/src/test/resources/logback-test.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4f2f17..9b54a95 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,7 +2,7 @@ import java.io.File plugins { // Apply the application plugin to add support for building a CLI application in Java. - application + id("buildlogic.java-application-conventions") id("org.graalvm.buildtools.native") version "0.10.2" id("com.diffplug.spotless") version "7.0.0.BETA1" `maven-publish` @@ -26,19 +26,13 @@ dependencies { // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on // -> JDK API -> Bouncycastle implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") - implementation(project(":lib")) + implementation(project(":api")) // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback runtimeOnly("ch.qos.logback:logback-classic:1.5.6") runtimeOnly("org.slf4j:slf4j-api:2.0.13") runtimeOnly("org.slf4j:slf4j-jdk-platform-logging:2.0.13") - - - // JDK23: JMH (Third-Party) Not required, added for benchmarking - // https://github.com/openjdk/jmh - implementation("org.openjdk.jmh:jmh-core:1.37") - annotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37") } testing { @@ -53,6 +47,8 @@ testing { // Apply a specific Java toolchain to ease working on different environments. java { + sourceCompatibility = JavaVersion.VERSION_22 + targetCompatibility = JavaVersion.VERSION_22 toolchain { languageVersion = JavaLanguageVersion.of(jdkVersion) } @@ -77,8 +73,6 @@ graalvmNative { // for the versions of GRAAL_VM Community edition - selecting Oracle languageVersion = JavaLanguageVersion.of(jdkVersion) vendor = JvmVendorSpec.matching("Oracle") - // languageVersion = JavaLanguageVersion.of(17) - // vendor = JvmVendorSpec.GRAAL_VM } } } diff --git a/app/src/main/resources/logback.xml b/app/src/main/resources/logback.xml index a33d8dd..acfc0cd 100644 --- a/app/src/main/resources/logback.xml +++ b/app/src/main/resources/logback.xml @@ -20,7 +20,7 @@ tic-tac-toe.log.json.%d{yyyy-MM-dd}.log 30 - 1GB + 100MB true diff --git a/lib/src/test/java/org/xxdc/oss/example/AppTest.java b/app/src/test/java/org/xxdc/oss/example/AppTest.java similarity index 100% rename from lib/src/test/java/org/xxdc/oss/example/AppTest.java rename to app/src/test/java/org/xxdc/oss/example/AppTest.java diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..83b59c1 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Support convention plugins written in Kotlin. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build. + `kotlin-dsl` +} + +repositories { + // Use the plugin portal to apply community plugins in convention plugins. + gradlePluginPortal() +} \ No newline at end of file diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..31bf56a --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This settings file is used to specify which projects to include in your build-logic build. + */ + +dependencyResolutionManagement { + // Reuse version catalog from the main build. + versionCatalogs { + create("libs", { from(files("../gradle/libs.versions.toml")) }) + } +} + +rootProject.name = "buildSrc" diff --git a/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts new file mode 100644 index 0000000..b9b34c9 --- /dev/null +++ b/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the common convention plugin for shared build configuration between library and application projects. + id("buildlogic.java-common-conventions") + + // Apply the application plugin to add support for building a CLI application in Java. + application +} diff --git a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts new file mode 100644 index 0000000..4551f9f --- /dev/null +++ b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts @@ -0,0 +1,37 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the java Plugin to add support for Java. + java +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + constraints { + // Define dependency versions as constraints + implementation("org.apache.commons:commons-text:1.11.0") + } + + // Use JUnit Jupiter for testing. + testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(22) + } +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts new file mode 100644 index 0000000..ab305d8 --- /dev/null +++ b/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + // Apply the common convention plugin for shared build configuration between library and application projects. + id("buildlogic.java-common-conventions") + + // Apply the java-library plugin for API and implementation separation. + `java-library` +} diff --git a/native/src/main/rust/build.gradle.kts b/native/src/main/rust/build.gradle.kts index 076ad4b..1fa0dcf 100644 --- a/native/src/main/rust/build.gradle.kts +++ b/native/src/main/rust/build.gradle.kts @@ -2,7 +2,7 @@ import java.io.File plugins { // Apply the application plugin to add support for building a CLI application in Java. - library + id("buildlogic.java-library-conventions") } repositories { diff --git a/settings.gradle.kts b/settings.gradle.kts index 6fc2c33..8956004 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,6 @@ plugins { } rootProject.name = "overengineering-tictactoe" -include("lib") +include("api") include("app") include("native") From 7381168fe37f0755f31d16cecdc8a428e9de65d5 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Fri, 30 Aug 2024 00:07:26 -0400 Subject: [PATCH 04/20] fix: ./gradlew jmh which was migrated to the test codebase in api --- .vscode/settings.json | 2 +- api/build.gradle.kts | 2 +- .../{ => org/xxdc/oss/example}/GameBoardTest.java | 5 ++--- .../interop/benchmark/PlayerIdsBenchmark.java | 12 ++++++------ 4 files changed, 10 insertions(+), 11 deletions(-) rename api/src/test/java/{ => org/xxdc/oss/example}/GameBoardTest.java (94%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 70291a0..6643f80 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,5 +29,5 @@ "org" ], "java.configuration.updateBuildConfiguration": "automatic", - "java.dependency.packagePresentation": "hierarchical", + "java.dependency.packagePresentation": "flat", } \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 7be1db1..15c4cbb 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { // JDK23: JMH (Third-Party) Not required, added for benchmarking // https://github.com/openjdk/jmh testImplementation("org.openjdk.jmh:jmh-core:1.37") - annotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37") + testAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37") } // Run JMH benchmark diff --git a/api/src/test/java/GameBoardTest.java b/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java similarity index 94% rename from api/src/test/java/GameBoardTest.java rename to api/src/test/java/org/xxdc/oss/example/GameBoardTest.java index 56b3418..fcb2c1d 100644 --- a/api/src/test/java/GameBoardTest.java +++ b/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java @@ -1,12 +1,11 @@ +package org.xxdc.oss.example; + import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.xxdc.oss.example.TestData.*; import org.testng.annotations.Test; -import org.xxdc.oss.example.GameBoard; -import org.xxdc.oss.example.GameBoardDefaultImpl; -import org.xxdc.oss.example.GameBoardNativeImpl; public class GameBoardTest { diff --git a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java b/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java index e63c2aa..53d11a3 100644 --- a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java +++ b/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java @@ -17,7 +17,7 @@ public class PlayerIdsBenchmark { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testPlayerIdsGetId() { PlayerIds ids = new PlayerIds(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.getNextId(); } } @@ -27,7 +27,7 @@ public void testPlayerIdsGetId() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testPlayerIdsGetAndIncrementId() { PlayerIds ids = new PlayerIds(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.getNextIdAndIncrement(); } } @@ -37,7 +37,7 @@ public void testPlayerIdsGetAndIncrementId() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testAtomicIntegerGetId() { AtomicInteger ids = new AtomicInteger(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.get(); } } @@ -47,7 +47,7 @@ public void testAtomicIntegerGetId() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testAtomicIntegerGetAndIncrement() { AtomicInteger ids = new AtomicInteger(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.getAndIncrement(); } } @@ -57,7 +57,7 @@ public void testAtomicIntegerGetAndIncrement() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testLockGetId() { Control ids = new Control(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.getId(); } } @@ -67,7 +67,7 @@ public void testLockGetId() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public void testLockGetAndIncrement() { Control ids = new Control(1); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { var id = ids.getAndIncrement(); } } From 9c4ca4e9d2be54ac0468a99c74f2df4c60a4d718 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Fri, 30 Aug 2024 11:12:37 -0400 Subject: [PATCH 05/20] refactor: load native library via native library ;-) --- api/build.gradle.kts | 23 ++++---- .../oss/example/{interop => }/PlayerIds.java | 2 +- .../example/interop/TicTacToeGameBoard.java | 1 + .../oss/example/interop/TicTacToeLibrary.java | 8 +-- .../oss/example/interop/PlayerIdsTest.java | 2 + .../interop/benchmark/PlayerIdsBenchmark.java | 2 +- app/build.gradle.kts | 27 +++++++++- build.gradle.kts | 0 native/{src/main/rust => }/build.gradle.kts | 52 ++++++++----------- .../oss/example/interop/NativeLoader.java | 37 +++++++++++++ 10 files changed, 105 insertions(+), 49 deletions(-) rename api/src/main/java/org/xxdc/oss/example/{interop => }/PlayerIds.java (94%) create mode 100644 build.gradle.kts rename native/{src/main/rust => }/build.gradle.kts (81%) create mode 100644 native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 15c4cbb..94da50f 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -12,6 +12,14 @@ repositories { // Use Maven Central for resolving dependencies. mavenCentral() gradlePluginPortal() + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } } val jdkVersion = "22" @@ -27,6 +35,10 @@ dependencies { // -> JDK API -> Bouncycastle implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + // Native Library (Rust) + runtimeOnly(project(":native")) + runtimeOnly("org.xxdc.oss.example:tictactoe-native-macos-aarch64:1.0.0") + // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback testRuntimeOnly("ch.qos.logback:logback-classic:1.5.6") @@ -92,12 +104,12 @@ publishing { publications { create("maven") { groupId = "org.xxdc.oss.example" - artifactId = "tictactoe" + artifactId = "tictactoe-api" version = "1.0.0-jdk$jdkVersion" from(components["java"]) pom { name.set("tictactoe") - description.set("An Over-Engineered Tic Tac Toe game") + description.set("An Over-Engineered Tic Tac Toe Game API") url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") licenses { license { @@ -123,10 +135,6 @@ publishing { } tasks.withType().all { - systemProperty( - "java.library.path", libPath - ) - // JDK22: Foreign Function Interface (FFI) // Resolves Warning: // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called @@ -134,7 +142,4 @@ tasks.withType().all { // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module // WARNING: Restricted methods will be blocked in a future release unless native access is enabled jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") - environment("PATH", libPath) // For Windows - environment("LD_LIBRARY_PATH", libPath) // For Linux - environment("DYLD_LIBRARY_PATH", libPath) // For macOS } \ No newline at end of file diff --git a/api/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java b/api/src/main/java/org/xxdc/oss/example/PlayerIds.java similarity index 94% rename from api/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java rename to api/src/main/java/org/xxdc/oss/example/PlayerIds.java index 98d939c..826504c 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/PlayerIds.java +++ b/api/src/main/java/org/xxdc/oss/example/PlayerIds.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example.interop; +package org.xxdc.oss.example; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; diff --git a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java index 4ac688f..5883f30 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java +++ b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import org.xxdc.oss.example.GameBoard; +import org.xxdc.oss.example.PlayerIds; class TicTacToeGameBoard implements GameBoard { diff --git a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java index f4be3a4..0cec6af 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java +++ b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java @@ -41,14 +41,10 @@ public GameBoard newGameBoard(int dimension) { return new TicTacToeGameBoard(dimension, libTicTacToe, cleaner); } - private String platformLibraryName() { - return System.mapLibraryName(LIBRARY_NAME); - } - private void initLibrary() { - libTicTacToe = SymbolLookup.libraryLookup(platformLibraryName(), arena); - initLibraryMethods(); try { + libTicTacToe = NativeLoader.loadLibrary(LIBRARY_NAME, arena); + initLibraryMethods(); logVersion(version); logVersionString(versionString); } catch (Throwable e) { diff --git a/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java b/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java index cccfa1a..2b37606 100644 --- a/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java +++ b/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java @@ -4,6 +4,8 @@ import org.testng.annotations.Test; +import org.xxdc.oss.example.PlayerIds; + public class PlayerIdsTest { @Test diff --git a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java b/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java index 53d11a3..4182fea 100644 --- a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java +++ b/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java @@ -7,7 +7,7 @@ import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.xxdc.oss.example.interop.PlayerIds; +import org.xxdc.oss.example.PlayerIds; @SuppressWarnings("unused") public class PlayerIdsBenchmark { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9b54a95..10accfa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -117,7 +117,7 @@ publishing { from(components["java"]) pom { name.set("tictactoe") - description.set("An Over-Engineered Tic Tac Toe game") + description.set("An Over-Engineered Tic Tac Toe Game and Game Server") url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") licenses { license { @@ -140,4 +140,27 @@ publishing { } } } -} \ No newline at end of file +} +// Install a pre-commit hook to run the Gradle task "spotlessApply" before committing changes. +tasks.register("installGitHook") { + doLast { + val hooksDir = file("${rootDir}/.git/hooks") + val preCommitFile = File(hooksDir, "pre-commit") + + if (!preCommitFile.exists()) { + preCommitFile.writeText( + """ + #!/bin/sh + ./gradlew spotlessApply + """.trimIndent() + ) + preCommitFile.setExecutable(true) + println("Pre-commit hook installed.") + } else { + println("Pre-commit hook already exists.") + } + } +} +tasks.named("build") { + dependsOn("installGitHook") +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/native/src/main/rust/build.gradle.kts b/native/build.gradle.kts similarity index 81% rename from native/src/main/rust/build.gradle.kts rename to native/build.gradle.kts index 1fa0dcf..f49dd4e 100644 --- a/native/src/main/rust/build.gradle.kts +++ b/native/build.gradle.kts @@ -3,6 +3,7 @@ import java.io.File plugins { // Apply the application plugin to add support for building a CLI application in Java. id("buildlogic.java-library-conventions") + id("maven-publish") } repositories { @@ -11,12 +12,23 @@ repositories { gradlePluginPortal() } +testing { + suites { + // Configure the built-in test suite + val test by getting(JvmTestSuite::class) { + // Use TestNG test framework + useTestNG("7.5.1") + } + } +} + val jdkVersion = "22" // JDK22: Foreign Function Interface (FFI) // Support building native Rust library using Cargo: // https://doc.rust-lang.org/cargo/getting-started/installation.html val osName = System.getProperty("os.name").lowercase() +val osArch = System.getProperty("os.arch").lowercase() val cargoBuildDir = file("${layout.buildDirectory.get()}/cargo") @@ -26,7 +38,13 @@ val libPath = when { osName.contains("nux") -> "${cargoBuildDir}/debug" else -> throw GradleException("Unsupported OS") } -val libName = System.mapLibraryName("tictactoe") +val libSuffix = when { + osName.contains("win") -> "windows-$osArch" + osName.contains("mac") -> "macos-$osArch" + osName.contains("nux") -> "linux-$osArch" + else -> throw GradleException("Unsupported OS") +} +val libName = System.mapLibraryName("xxdc_oss_tictactoe") // // Rust Build Start @@ -78,8 +96,6 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(jdkVersion) } - withJavadocJar() - withSourcesJar() } // https://docs.gradle.org/current/userguide/publishing_maven.html @@ -99,12 +115,12 @@ publishing { publications { create("maven") { groupId = "org.xxdc.oss.example" - artifactId = "native-tictactoe" - version = "1.0.0-jdk$jdkVersion" + artifactId = "tictactoe-native-$libSuffix" + version = "1.0.0" from(components["java"]) pom { name.set("tictactoe") - description.set("An Over-Engineered Tic Tac Toe game") + description.set("An Over-Engineered Tic Tac Toe Native Library") url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") licenses { license { @@ -128,27 +144,3 @@ publishing { } } } - -/// Install a pre-commit hook to run the Gradle task "spotlessApply" before committing changes. -tasks.register("installGitHook") { - doLast { - val hooksDir = file("${rootDir}/.git/hooks") - val preCommitFile = File(hooksDir, "pre-commit") - - if (!preCommitFile.exists()) { - preCommitFile.writeText( - """ - #!/bin/sh - ./gradlew spotlessApply - """.trimIndent() - ) - preCommitFile.setExecutable(true) - println("Pre-commit hook installed.") - } else { - println("Pre-commit hook already exists.") - } - } -} -tasks.named("build") { - dependsOn("installGitHook") -} diff --git a/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java b/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java new file mode 100644 index 0000000..805ed1d --- /dev/null +++ b/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java @@ -0,0 +1,37 @@ +package org.xxdc.oss.example.interop; + +import java.lang.foreign.SymbolLookup; + +import java.io.IOException; +import java.io.OutputStream; + +import java.lang.foreign.Arena; + +import java.nio.file.Files; + +public class NativeLoader { + + public static SymbolLookup loadLibrary(String libraryName, Arena arena) { + try { + var platformLibraryName = platformLibraryName(libraryName); + return fromClassPathToTemp(platformLibraryName, arena); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + private static String platformLibraryName(String libraryName) { + return System.mapLibraryName(libraryName); + } + + private static SymbolLookup fromClassPathToTemp(String platformLibraryName, Arena arena) throws IOException { + var resourceLookupString = "/" + platformLibraryName; + var tempResourceFile = Files.createTempFile(platformLibraryName, ".tmp"); + try (var inputStream = NativeLoader.class.getResourceAsStream(resourceLookupString); + OutputStream outputStream = Files.newOutputStream(tempResourceFile)) { + inputStream.transferTo(outputStream); + return SymbolLookup.libraryLookup(tempResourceFile, arena); + } + } + +} From c6243bd6286debd8da661d741a752504447a21b8 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Fri, 30 Aug 2024 11:59:55 -0400 Subject: [PATCH 06/20] refactor: load native library via native library ;-) --- api/build.gradle.kts | 11 ++++++++++- .../xxdc/oss/example/{interop => }/PlayerIdsTest.java | 4 +--- .../{interop => }/benchmark/PlayerIdsBenchmark.java | 2 +- native/build.gradle.kts | 1 - 4 files changed, 12 insertions(+), 6 deletions(-) rename api/src/test/java/org/xxdc/oss/example/{interop => }/PlayerIdsTest.java (93%) rename api/src/test/java/org/xxdc/oss/example/{interop => }/benchmark/PlayerIdsBenchmark.java (98%) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 94da50f..23497ec 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -7,6 +7,15 @@ plugins { } val libPath = "native/src/main/rust/target/debug" +val osName = System.getProperty("os.name").lowercase() +val osArch = System.getProperty("os.arch").lowercase() + +val libSuffix = when { + osName.contains("win") -> "windows-$osArch" + osName.contains("mac") -> "macos-$osArch" + osName.contains("nux") -> "linux-$osArch" + else -> throw GradleException("Unsupported OS") +} repositories { // Use Maven Central for resolving dependencies. @@ -37,7 +46,7 @@ dependencies { // Native Library (Rust) runtimeOnly(project(":native")) - runtimeOnly("org.xxdc.oss.example:tictactoe-native-macos-aarch64:1.0.0") + runtimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.0.0") // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback diff --git a/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java b/api/src/test/java/org/xxdc/oss/example/PlayerIdsTest.java similarity index 93% rename from api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java rename to api/src/test/java/org/xxdc/oss/example/PlayerIdsTest.java index 2b37606..77c8b0d 100644 --- a/api/src/test/java/org/xxdc/oss/example/interop/PlayerIdsTest.java +++ b/api/src/test/java/org/xxdc/oss/example/PlayerIdsTest.java @@ -1,11 +1,9 @@ -package org.xxdc.oss.example.interop; +package org.xxdc.oss.example; import static org.testng.Assert.assertEquals; import org.testng.annotations.Test; -import org.xxdc.oss.example.PlayerIds; - public class PlayerIdsTest { @Test diff --git a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java b/api/src/test/java/org/xxdc/oss/example/benchmark/PlayerIdsBenchmark.java similarity index 98% rename from api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java rename to api/src/test/java/org/xxdc/oss/example/benchmark/PlayerIdsBenchmark.java index 4182fea..c545467 100644 --- a/api/src/test/java/org/xxdc/oss/example/interop/benchmark/PlayerIdsBenchmark.java +++ b/api/src/test/java/org/xxdc/oss/example/benchmark/PlayerIdsBenchmark.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example.interop.benchmark; +package org.xxdc.oss.example.benchmark; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; diff --git a/native/build.gradle.kts b/native/build.gradle.kts index f49dd4e..e563225 100644 --- a/native/build.gradle.kts +++ b/native/build.gradle.kts @@ -21,7 +21,6 @@ testing { } } } - val jdkVersion = "22" // JDK22: Foreign Function Interface (FFI) From 48271c3e130ef42e59a08ea2a840068438b4e481 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Fri, 30 Aug 2024 13:46:47 -0400 Subject: [PATCH 07/20] feat: Fallback to the default gameboard implementation if the native implementation is not available. --- .../java/org/xxdc/oss/example/GameBoard.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoard.java b/api/src/main/java/org/xxdc/oss/example/GameBoard.java index d898381..4060266 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoard.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoard.java @@ -1,7 +1,11 @@ package org.xxdc.oss.example; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * Represents a game board for a game. The game board has a square dimension and contains a grid of @@ -9,6 +13,10 @@ */ public interface GameBoard extends JsonSerializable { + static final Logger log = System.getLogger(GameBoard.class.getName()); + + static final AtomicBoolean useNative = new AtomicBoolean(true); + /** * Checks if the given location on the game board is a valid move (i.e. an available location). * @@ -103,6 +111,19 @@ default boolean isEmpty() { */ static GameBoard with(int dimension) { // Prefer the native implementation of the game board for performance. - return new GameBoardNativeImpl(dimension); + GameBoard gameBoard; + try { + if (useNative.get()) { + gameBoard = new GameBoardNativeImpl(dimension); + } else { + gameBoard = new GameBoardDefaultImpl(dimension); + } + } catch (Exception e) { + // Fallback to the Java implementation. + log.log(Level.WARNING, "Unable to use native logger, falling back to default logger: {0}", e.getMessage()); + useNative.set(false); + gameBoard = new GameBoardDefaultImpl(dimension); + } + return gameBoard; } } From dd30d283a5d459a374862839c7ab1546c8869963 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Fri, 30 Aug 2024 13:52:33 -0400 Subject: [PATCH 08/20] feat: Fallback to the default gameboard implementation if the native implementation is not available. --- api/src/main/java/org/xxdc/oss/example/GameBoard.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoard.java b/api/src/main/java/org/xxdc/oss/example/GameBoard.java index 4060266..945291d 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoard.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoard.java @@ -2,7 +2,6 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -120,7 +119,10 @@ static GameBoard with(int dimension) { } } catch (Exception e) { // Fallback to the Java implementation. - log.log(Level.WARNING, "Unable to use native logger, falling back to default logger: {0}", e.getMessage()); + log.log( + Level.WARNING, + "Unable to use native logger, falling back to default logger: {0}", + e.getMessage()); useNative.set(false); gameBoard = new GameBoardDefaultImpl(dimension); } From 6099c45a6ba33867ce2bade4f804ced2a1e3059b Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 12:58:56 -0400 Subject: [PATCH 09/20] feat: Fallback to the default gameboard implementation if the native implementation is not available and fix dependency issues --- README.md | 2 +- api/build.gradle.kts | 13 +++---- .../main/java/org/xxdc/oss/example/Game.java | 3 +- .../java/org/xxdc/oss/example/GameBoard.java | 10 ++--- ...faultImpl.java => GameBoardLocalImpl.java} | 12 +++--- .../oss/example/interop/TicTacToeLibrary.java | 1 + .../example/transport/tcp/TcpProtocol.java | 2 +- .../oss/example/GameBoardNativeImplTest.java | 12 ++++++ .../org/xxdc/oss/example/GameBoardTest.java | 18 ++------- .../java/org/xxdc/oss/example/TestData.java | 2 +- app/build.gradle.kts | 12 ++---- ...ldlogic.java-common-conventions.gradle.kts | 13 +------ native/build.gradle.kts | 6 +-- .../oss/example/interop/NativeLoader.java | 37 ------------------- .../example/interop/loader/NativeLoader.java | 35 ++++++++++++++++++ 15 files changed, 78 insertions(+), 100 deletions(-) rename api/src/main/java/org/xxdc/oss/example/{GameBoardDefaultImpl.java => GameBoardLocalImpl.java} (91%) delete mode 100644 native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java create mode 100644 native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java diff --git a/README.md b/README.md index 8363e52..01244e0 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ https://openjdk.org/projects/jdk/17/ ### Algorithms -The following algorithms are used by the AI BOT in this project (For a detailed discussion see [Road to JDK 25 - An Algorithmic Interlude](https://briancorbinxyz.medium.com/over-engineering-tic-tac-toe-an-algorithmic-interlude-8af3aa13173a): +The following algorithms are used by the AI BOT in this project - for a detailed discussion see [Road to JDK 25 - An Algorithmic Interlude](https://briancorbinxyz.medium.com/over-engineering-tic-tac-toe-an-algorithmic-interlude-8af3aa13173a): - [Random](https://en.wikipedia.org/wiki/Randomness) See: [Random.java](app/src/main/java/org/example/bot/Random.java) - [Minimax](https://en.wikipedia.org/wiki/Minimax) See: [Minimax.java](app/src/main/java/org/example/bot/Minimax.java) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 23497ec..e65d41a 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -19,6 +19,7 @@ val libSuffix = when { repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() gradlePluginPortal() maven { @@ -31,8 +32,6 @@ repositories { } } -val jdkVersion = "22" - // Automatic code formatting before compile tasks.named("compileJava") { dependsOn("spotlessApply") @@ -45,8 +44,9 @@ dependencies { implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") // Native Library (Rust) - runtimeOnly(project(":native")) - runtimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.0.0") + implementation(project(":native")) + testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.0.0") + // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback @@ -81,9 +81,6 @@ testing { // Apply a specific Java toolchain to ease working on different environments. java { - toolchain { - languageVersion = JavaLanguageVersion.of(jdkVersion) - } withJavadocJar() withSourcesJar() } @@ -114,7 +111,7 @@ publishing { create("maven") { groupId = "org.xxdc.oss.example" artifactId = "tictactoe-api" - version = "1.0.0-jdk$jdkVersion" + version = "1.1.0-jdk${java.toolchain.languageVersion}" from(components["java"]) pom { name.set("tictactoe") diff --git a/api/src/main/java/org/xxdc/oss/example/Game.java b/api/src/main/java/org/xxdc/oss/example/Game.java index b80101d..07fbcb9 100644 --- a/api/src/main/java/org/xxdc/oss/example/Game.java +++ b/api/src/main/java/org/xxdc/oss/example/Game.java @@ -45,7 +45,8 @@ public Game(int size, boolean persistenceEnabled, PlayerNode... players) { this.gameId = UUID.randomUUID(); this.moveNumber = 0; this.gameState = new ArrayDeque<>(); - this.gameState.add(new GameState(GameBoard.with(size), this.playerNodes.playerMarkerList(), 0)); + this.gameState.add( + new GameState(GameBoard.withDimension(size), this.playerNodes.playerMarkerList(), 0)); this.persistenceEnabled = persistenceEnabled; } diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoard.java b/api/src/main/java/org/xxdc/oss/example/GameBoard.java index 945291d..9041814 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoard.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoard.java @@ -108,23 +108,23 @@ default boolean isEmpty() { * @param dimension the dimension of the game board, which is the number of rows or columns * @return a new {@link GameBoard} instance with the specified dimension */ - static GameBoard with(int dimension) { + static GameBoard withDimension(int dimension) { // Prefer the native implementation of the game board for performance. GameBoard gameBoard; try { if (useNative.get()) { gameBoard = new GameBoardNativeImpl(dimension); } else { - gameBoard = new GameBoardDefaultImpl(dimension); + gameBoard = new GameBoardLocalImpl(dimension); } - } catch (Exception e) { + } catch (ExceptionInInitializerError e) { // Fallback to the Java implementation. log.log( Level.WARNING, - "Unable to use native logger, falling back to default logger: {0}", + "Unable to use native logger, falling back to local logger: {0}", e.getMessage()); useNative.set(false); - gameBoard = new GameBoardDefaultImpl(dimension); + gameBoard = new GameBoardLocalImpl(dimension); } return gameBoard; } diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java b/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java similarity index 91% rename from api/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java rename to api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java index 0cec326..874447e 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoardDefaultImpl.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java @@ -9,16 +9,16 @@ * stores the current state of the game in a 1D array. Provides methods to check the validity of * moves, place player markers, check for a winner, and get a string representation of the board. */ -public record GameBoardDefaultImpl(int dimension, String[] content) +public record GameBoardLocalImpl(int dimension, String[] content) implements Serializable, GameBoard { private static final long serialVersionUID = 1L; - public GameBoardDefaultImpl(int dimension) { + public GameBoardLocalImpl(int dimension) { this(dimension, new String[dimension * dimension]); } - public GameBoardDefaultImpl { + public GameBoardLocalImpl { if (content.length != dimension * dimension) { throw new IllegalArgumentException("Content must be of length " + dimension * dimension); } @@ -108,13 +108,13 @@ private String contentElseBlank(String unit) { } @Override - public GameBoardDefaultImpl withMove(String playerMarker, int location) { + public GameBoardLocalImpl withMove(String playerMarker, int location) { if (!isValidMove(location)) { throw new InvalidMoveException("Invalid move: " + playerMarker + "@" + location); } String[] boardCopy = getBoardCopy(); boardCopy[location] = playerMarker; - return new GameBoardDefaultImpl(dimension, boardCopy); + return new GameBoardLocalImpl(dimension, boardCopy); } @Override @@ -132,7 +132,7 @@ public String asJsonString() { } public GameBoard clone() { - return new GameBoardDefaultImpl(dimension, getBoardCopy()); + return new GameBoardLocalImpl(dimension, getBoardCopy()); } private String[] getBoardCopy() { diff --git a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java index 0cec6af..e93a2e9 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java +++ b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java @@ -14,6 +14,7 @@ import java.lang.invoke.MethodType; import java.lang.ref.Cleaner; import org.xxdc.oss.example.GameBoard; +import org.xxdc.oss.example.interop.loader.NativeLoader; public final class TicTacToeLibrary { diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java index e9e6135..10d6123 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java @@ -40,7 +40,7 @@ public static Optional fromNextMoveState(String serverMessage) { String[] playerMarkers = matcher.group(3).replaceAll("\"", "").split(","); int currentPlayerIndex = Integer.valueOf(matcher.group(4)); int dimension = Integer.valueOf(matcher.group(5)); - var board = GameBoard.with(dimension); + var board = GameBoard.withDimension(dimension); String[] rawContent = matcher.group(6).split(","); for (int i = 0; i < rawContent.length; i++) { if (rawContent[i] != null && !rawContent[i].equals("null")) { diff --git a/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java b/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java index 4d324d4..bdb3853 100644 --- a/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java +++ b/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java @@ -15,6 +15,18 @@ public class GameBoardNativeImplTest { private static final Logger log = System.getLogger(MethodHandles.lookup().lookupClass().getName()); + @Test + public void test_default_game_board_size_is_3x3() { + GameBoard gameBoard = new GameBoardNativeImpl(); + Assert.assertEquals(gameBoard.dimension(), 3); + } + + @Test + public void test_empty_default_game_board_has_all_moves_available() { + GameBoard gameBoard = new GameBoardNativeImpl(3); + Assert.assertEquals(gameBoard.availableMoves().size(), 9); + } + @Test public void should_load_library() { printEnvironmentVariables(); diff --git a/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java b/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java index fcb2c1d..36ab4f7 100644 --- a/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java +++ b/api/src/test/java/org/xxdc/oss/example/GameBoardTest.java @@ -11,25 +11,13 @@ public class GameBoardTest { @Test public void testDefaultGameBoardSizeIs3x3() { - GameBoard gameBoard = new GameBoardDefaultImpl(3); + var gameBoard = GameBoard.withDimension(3); assertEquals(gameBoard.dimension(), 3); } @Test public void testNewGameBoardHasAllMovesAvailable() { - GameBoard gameBoard = new GameBoardDefaultImpl(3); - assertEquals(gameBoard.availableMoves().size(), 9); - } - - @Test - public void testDefaultGameBoardSizeIs3x3WithNative() { - GameBoard gameBoard = new GameBoardNativeImpl(3); - assertEquals(gameBoard.dimension(), 3); - } - - @Test - public void testNewGameBoardHasAllMovesAvailableWithNative() { - GameBoard gameBoard = new GameBoardNativeImpl(3); + GameBoard gameBoard = GameBoard.withDimension(3); assertEquals(gameBoard.availableMoves().size(), 9); } @@ -47,7 +35,7 @@ public void testPopulatedGameBoardHasCorrectNumberOfAvailableMoves() { } @Test - public void testFullGameeBoardHasNoAvailableMoves() { + public void testFullGameBoardHasNoAvailableMoves() { var gameBoard = createBoardWith( new String[][] { diff --git a/api/src/test/java/org/xxdc/oss/example/TestData.java b/api/src/test/java/org/xxdc/oss/example/TestData.java index 588188e..1ff0868 100644 --- a/api/src/test/java/org/xxdc/oss/example/TestData.java +++ b/api/src/test/java/org/xxdc/oss/example/TestData.java @@ -2,7 +2,7 @@ public class TestData { public static GameBoard createBoardWith(String[][] content) { - GameBoard board = new GameBoardNativeImpl(content.length); + GameBoard board = GameBoard.withDimension(content.length); for (int row = 0; row < content.length; row++) { for (int col = 0; col < content[row].length; col++) { if (content[row][col] != null && !content[row][col].equals("_")) { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 10accfa..b844c53 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,12 +10,11 @@ plugins { repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() gradlePluginPortal() } -val jdkVersion = "22" - // Automatic code formatting before compile tasks.named("compileJava") { dependsOn("spotlessApply") @@ -47,11 +46,6 @@ testing { // Apply a specific Java toolchain to ease working on different environments. java { - sourceCompatibility = JavaVersion.VERSION_22 - targetCompatibility = JavaVersion.VERSION_22 - toolchain { - languageVersion = JavaLanguageVersion.of(jdkVersion) - } withJavadocJar() withSourcesJar() } @@ -71,7 +65,7 @@ graalvmNative { javaLauncher = javaToolchains.launcherFor { // NB: On MacOS ARM ARCH the native-image implementation is not available // for the versions of GRAAL_VM Community edition - selecting Oracle - languageVersion = JavaLanguageVersion.of(jdkVersion) + languageVersion = java.toolchain.languageVersion vendor = JvmVendorSpec.matching("Oracle") } } @@ -113,7 +107,7 @@ publishing { create("maven") { groupId = "org.xxdc.oss.example" artifactId = "tictactoe-app" - version = "1.0.0-jdk$jdkVersion" + version = "1.1.0-jdk${java.toolchain.languageVersion}" from(components["java"]) pom { name.set("tictactoe") diff --git a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts index 4551f9f..44ee93d 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts @@ -17,21 +17,12 @@ dependencies { // Define dependency versions as constraints implementation("org.apache.commons:commons-text:1.11.0") } - - // Use JUnit Jupiter for testing. - testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") - - testRuntimeOnly("org.junit.platform:junit-platform-launcher") } +public val jdkVersion = 22 // Apply a specific Java toolchain to ease working on different environments. java { toolchain { - languageVersion = JavaLanguageVersion.of(22) + languageVersion = JavaLanguageVersion.of(jdkVersion) } } - -tasks.named("test") { - // Use JUnit Platform for unit tests. - useJUnitPlatform() -} diff --git a/native/build.gradle.kts b/native/build.gradle.kts index e563225..2e9cc30 100644 --- a/native/build.gradle.kts +++ b/native/build.gradle.kts @@ -8,6 +8,7 @@ plugins { repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() gradlePluginPortal() } @@ -21,7 +22,6 @@ testing { } } } -val jdkVersion = "22" // JDK22: Foreign Function Interface (FFI) // Support building native Rust library using Cargo: @@ -90,11 +90,7 @@ tasks.named("jar") { // // Rust Build End // -// Apply a specific Java toolchain to ease working on different environments. java { - toolchain { - languageVersion = JavaLanguageVersion.of(jdkVersion) - } } // https://docs.gradle.org/current/userguide/publishing_maven.html diff --git a/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java b/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java deleted file mode 100644 index 805ed1d..0000000 --- a/native/src/main/java/org/xxdc/oss/example/interop/NativeLoader.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.xxdc.oss.example.interop; - -import java.lang.foreign.SymbolLookup; - -import java.io.IOException; -import java.io.OutputStream; - -import java.lang.foreign.Arena; - -import java.nio.file.Files; - -public class NativeLoader { - - public static SymbolLookup loadLibrary(String libraryName, Arena arena) { - try { - var platformLibraryName = platformLibraryName(libraryName); - return fromClassPathToTemp(platformLibraryName, arena); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - private static String platformLibraryName(String libraryName) { - return System.mapLibraryName(libraryName); - } - - private static SymbolLookup fromClassPathToTemp(String platformLibraryName, Arena arena) throws IOException { - var resourceLookupString = "/" + platformLibraryName; - var tempResourceFile = Files.createTempFile(platformLibraryName, ".tmp"); - try (var inputStream = NativeLoader.class.getResourceAsStream(resourceLookupString); - OutputStream outputStream = Files.newOutputStream(tempResourceFile)) { - inputStream.transferTo(outputStream); - return SymbolLookup.libraryLookup(tempResourceFile, arena); - } - } - -} diff --git a/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java b/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java new file mode 100644 index 0000000..1d5c0a2 --- /dev/null +++ b/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java @@ -0,0 +1,35 @@ +package org.xxdc.oss.example.interop.loader; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.SymbolLookup; +import java.nio.file.Files; + +public class NativeLoader { + + public static SymbolLookup loadLibrary(String libraryName, Arena arena) { + try { + var platformLibraryName = platformLibraryName(libraryName); + return fromClassPathToTemp(platformLibraryName, arena); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + private static String platformLibraryName(String libraryName) { + return System.mapLibraryName(libraryName); + } + + private static SymbolLookup fromClassPathToTemp(String platformLibraryName, Arena arena) + throws IOException { + var resourceLookupString = "/" + platformLibraryName; + var tempResourceFile = Files.createTempFile(platformLibraryName, ".tmp"); + try (var inputStream = + NativeLoader.class.getResourceAsStream(resourceLookupString); + OutputStream outputStream = Files.newOutputStream(tempResourceFile)) { + inputStream.transferTo(outputStream); + return SymbolLookup.libraryLookup(tempResourceFile, arena); + } + } +} From d57e7e89d0841aaea0991f8fe28331ea2badc3d1 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 18:23:49 -0400 Subject: [PATCH 10/20] doc: cleanups for javadoc publishing --- .../java/org/xxdc/oss/example/SecureConnectionTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java b/api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java index dcae5f0..07e19a2 100644 --- a/api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java +++ b/api/src/test/java/org/xxdc/oss/example/SecureConnectionTest.java @@ -33,6 +33,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.pqc.jcajce.spec.KyberParameterSpec; +import org.testng.Assert; import org.testng.annotations.Test; import org.xxdc.oss.example.security.KyberKEMProvider; @@ -96,11 +97,9 @@ public void run() { log.log(Level.INFO, "Client accepting connections on {0}...", clientSocket); var clientSession = service.submit(new Client(clientSocket)); clientSession.get(); - } catch (IOException e) { - log.log(Level.ERROR, e.getMessage(), e); - } catch (InterruptedException e) { + } catch (Exception e) { + Assert.fail("Exception during secure connection check.", e); log.log(Level.ERROR, e.getMessage(), e); - } catch (ExecutionException e) { throw new RuntimeException(e); } } @@ -115,7 +114,7 @@ public void run() { service.shutdown(); service.awaitTermination(10, TimeUnit.MINUTES); } catch (Exception e) { - e.printStackTrace(); + Assert.fail("Exception during secure connection check.", e); } } From 2184da1ce7f2bd9b7703eda39e6107500f7468b6 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 18:24:43 -0400 Subject: [PATCH 11/20] doc: cleanups for javadoc publishing --- api/build.gradle.kts | 1 - .../java/org/xxdc/oss/example/BotPlayer.java | 3 + ...Handler.java => DuplexMessageHandler.java} | 29 ++++- .../main/java/org/xxdc/oss/example/Game.java | 41 ++++++- .../xxdc/oss/example/GameBoardLocalImpl.java | 9 ++ .../xxdc/oss/example/GameBoardNativeImpl.java | 6 ++ .../org/xxdc/oss/example/GamePersistence.java | 27 +++++ .../oss/example/GameServiceException.java | 13 +++ .../java/org/xxdc/oss/example/GameState.java | 79 ++++++++++++++ .../org/xxdc/oss/example/HumanPlayer.java | 28 ++++- .../oss/example/InvalidMoveException.java | 7 ++ .../java/org/xxdc/oss/example/PlayerIds.java | 21 ++++ .../java/org/xxdc/oss/example/PlayerNode.java | 41 +++++++ .../org/xxdc/oss/example/PlayerNodes.java | 49 +++++++++ .../org/xxdc/oss/example/PlayerPrinter.java | 9 ++ ...r.java => SecureDuplexMessageHandler.java} | 20 ++-- .../org/xxdc/oss/example/bot/AlphaBeta.java | 26 ++++- .../org/xxdc/oss/example/bot/BotStrategy.java | 90 +++++++++++++++- .../oss/example/bot/BotStrategyConfig.java | 100 ++++++++++++++++++ .../java/org/xxdc/oss/example/bot/MaxN.java | 21 +++- .../org/xxdc/oss/example/bot/Minimax.java | 25 ++++- .../oss/example/bot/MonteCarloTreeSearch.java | 30 +++++- .../org/xxdc/oss/example/bot/Paranoid.java | 23 +++- .../java/org/xxdc/oss/example/bot/Random.java | 8 ++ .../example/interop/TicTacToeGameBoard.java | 25 +++++ .../oss/example/interop/TicTacToeLibrary.java | 12 +++ .../example/security/KyberKEMProvider.java | 4 + .../oss/example/security/KyberKEMSpi.java | 18 ++++ .../oss/example/security/KyberParams.java | 35 ++++++ .../transport/TransportConfiguration.java | 5 + .../example/transport/TransportException.java | 26 +++++ .../oss/example/transport/Transports.java | 20 +++- .../example/transport/tcp/TcpProtocol.java | 59 +++++++++++ .../transport/tcp/TcpTransportClient.java | 22 ++++ .../transport/tcp/TcpTransportServer.java | 35 +++++- .../META-INF/services/javax.crypto.KEMSpi | 2 +- .../oss/example/GameBoardNativeImplTest.java | 14 +-- .../xxdc/oss/example/TcpTransportTest.java | 32 ++++-- .../oss/example/security/KyberKEMSpiTest.java | 30 ++++++ api/src/test/resources/logback-test.xml | 2 +- app/build.gradle.kts | 1 - .../main/java/org/xxdc/oss/example/App.java | 96 +++-------------- .../java/org/xxdc/oss/example/GameClient.java | 30 ++++++ .../java/org/xxdc/oss/example/GameServer.java | 24 +++++ .../META-INF/services/javax.crypto.KEMSpi | 1 - app/src/test/resources/logback-test.xml | 2 +- ...ldlogic.java-common-conventions.gradle.kts | 3 + native/build.gradle.kts | 1 - 48 files changed, 1058 insertions(+), 147 deletions(-) rename api/src/main/java/org/xxdc/oss/example/{RemoteMessageHandler.java => DuplexMessageHandler.java} (56%) rename api/src/main/java/org/xxdc/oss/example/{SecureMessageHandler.java => SecureDuplexMessageHandler.java} (96%) create mode 100644 api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java delete mode 100644 app/src/main/resources/META-INF/services/javax.crypto.KEMSpi diff --git a/api/build.gradle.kts b/api/build.gradle.kts index e65d41a..4c5a2d5 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -111,7 +111,6 @@ publishing { create("maven") { groupId = "org.xxdc.oss.example" artifactId = "tictactoe-api" - version = "1.1.0-jdk${java.toolchain.languageVersion}" from(components["java"]) pom { name.set("tictactoe") diff --git a/api/src/main/java/org/xxdc/oss/example/BotPlayer.java b/api/src/main/java/org/xxdc/oss/example/BotPlayer.java index c16ea2a..8173be2 100644 --- a/api/src/main/java/org/xxdc/oss/example/BotPlayer.java +++ b/api/src/main/java/org/xxdc/oss/example/BotPlayer.java @@ -7,11 +7,14 @@ /** * Represents a bot player in the game. The bot player uses a random number generator to make moves * on the game board. + * + * @param strategyFunction the function that determines the bot's next move */ public record BotPlayer(ToIntFunction strategyFunction) implements Player, Serializable { private static final long serialVersionUID = 1L; + /** Constructs a new BotPlayer instance using the default BotStrategy. */ public BotPlayer() { this(BotStrategy.DEFAULT); } diff --git a/api/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java similarity index 56% rename from api/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java index 60ae6cf..123e712 100644 --- a/api/src/main/java/org/xxdc/oss/example/RemoteMessageHandler.java +++ b/api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java @@ -4,7 +4,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -public class RemoteMessageHandler implements MessageHandler { +/** + * Handles duplex (two-way) communication between a client and a server using Java's built-in object + * serialization. Provides methods to send and receive messages as strings, as well as to send and + * receive arbitrary Java objects. The handler must be initialized before use. + */ +public class DuplexMessageHandler implements MessageHandler { private final ObjectOutputStream out; @@ -12,17 +17,37 @@ public class RemoteMessageHandler implements MessageHandler { private volatile boolean initialized = false; - public RemoteMessageHandler(ObjectOutputStream out, ObjectInputStream in) { + /** + * Constructs a new DuplexMessageHandler instance with the provided ObjectOutputStream and + * ObjectInputStream. + * + * @param out the ObjectOutputStream to use for sending messages + * @param in the ObjectInputStream to use for receiving messages + */ + public DuplexMessageHandler(ObjectOutputStream out, ObjectInputStream in) { this.out = out; this.in = in; } + /** + * Sends the given message as a byte array over the underlying communication channel. + * + * @param message the message to send + * @throws IOException if an I/O error occurs while sending the message + */ @Override public void sendMessage(String message) throws IOException { checkInitialized(); sendBytes(message.getBytes()); } + /** + * Receives a message from the underlying communication channel as a byte array and returns it as + * a string. + * + * @return the received message as a string + * @throws IOException if an I/O error occurs while receiving the message + */ @Override public String receiveMessage() throws IOException { checkInitialized(); diff --git a/api/src/main/java/org/xxdc/oss/example/Game.java b/api/src/main/java/org/xxdc/oss/example/Game.java index 07fbcb9..a243b1d 100644 --- a/api/src/main/java/org/xxdc/oss/example/Game.java +++ b/api/src/main/java/org/xxdc/oss/example/Game.java @@ -32,6 +32,10 @@ public class Game implements Serializable, AutoCloseable { private int moveNumber; + /** + * Constructs a new {@link Game} instance with a 3x3 game board, persistence enabled, and a human + * player as player 'X' and a bot player as player 'O'. + */ public Game() { this( 3, @@ -40,6 +44,15 @@ public Game() { new PlayerNode.Local<>("O", new BotPlayer())); } + /** + * Constructs a new {@link Game} instance with the specified game board size, persistence enabled + * state, and player nodes. + * + * @param size The size of the game board, e.g. 3 for a 3x3 board. + * @param persistenceEnabled Whether game state should be persisted to a file. + * @param players The player nodes for the game, which include the player marker and player + * implementation. + */ public Game(int size, boolean persistenceEnabled, PlayerNode... players) { this.playerNodes = PlayerNodes.of(players); this.gameId = UUID.randomUUID(); @@ -50,11 +63,24 @@ public Game(int size, boolean persistenceEnabled, PlayerNode... players) { this.persistenceEnabled = persistenceEnabled; } + /** + * Loads a {@link Game} instance from the specified file. + * + * @param gameFile The file containing the serialized {@link Game} instance. + * @return The loaded {@link Game} instance. + * @throws IOException If an I/O error occurs while reading the file. + * @throws ClassNotFoundException If the serialized class cannot be found. + */ public static Game from(File gameFile) throws IOException, ClassNotFoundException { GamePersistence persistence = new GamePersistence(); return persistence.loadFrom(gameFile); } + /** + * Plays the game, rendering the board, applying player moves, and persisting the game state if + * enabled. The game continues until a winning player is found or there are no more moves + * available, at which point the winner or tie is logged. + */ public void play() { try { GamePersistence persistence = new GamePersistence(); @@ -109,10 +135,20 @@ private GameState pushGameState(GameState state) { return state; } + /** + * Returns the unique identifier for this game instance. + * + * @return the game ID + */ public UUID getGameId() { return gameId; } + @Override + public void close() throws Exception { + playerNodes.close(); + } + private void renderBoard() { log.log(Level.INFO, "\n" + currentGameState().board()); } @@ -120,9 +156,4 @@ private void renderBoard() { private GameState currentGameState() { return gameState.peekLast(); } - - @Override - public void close() throws Exception { - playerNodes.close(); - } } diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java b/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java index 874447e..5035482 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoardLocalImpl.java @@ -8,12 +8,21 @@ * Represents a game board for a game, such as tic-tac-toe. The board has a specified dimension, and * stores the current state of the game in a 1D array. Provides methods to check the validity of * moves, place player markers, check for a winner, and get a string representation of the board. + * + * @param dimension the dimension of the board + * @param content the current state of the board, represented as a 1D array of player marker strings */ public record GameBoardLocalImpl(int dimension, String[] content) implements Serializable, GameBoard { private static final long serialVersionUID = 1L; + /** + * Constructs a new {@code GameBoardLocalImpl} instance with the specified dimension and + * initializes the content array with null values. + * + * @param dimension the dimension of the game board + */ public GameBoardLocalImpl(int dimension) { this(dimension, new String[dimension * dimension]); } diff --git a/api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java b/api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java index 1416662..6f178d9 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java +++ b/api/src/main/java/org/xxdc/oss/example/GameBoardNativeImpl.java @@ -19,10 +19,16 @@ public class GameBoardNativeImpl implements GameBoard { private final GameBoard board; + /** Constructs a new {@code GameBoardNativeImpl} instance with a default dimension of 3. */ public GameBoardNativeImpl() { this(3); } + /** + * Constructs a new {@code GameBoardNativeImpl} instance with the specified dimension. + * + * @param dimension the dimension of the game board, must be a positive integer + */ public GameBoardNativeImpl(int dimension) { this.library = LibraryHolder.TTT.instance(); this.board = library.newGameBoard(dimension); diff --git a/api/src/main/java/org/xxdc/oss/example/GamePersistence.java b/api/src/main/java/org/xxdc/oss/example/GamePersistence.java index 6d87634..28d9489 100644 --- a/api/src/main/java/org/xxdc/oss/example/GamePersistence.java +++ b/api/src/main/java/org/xxdc/oss/example/GamePersistence.java @@ -25,6 +25,13 @@ public class GamePersistence { private static final Logger log = System.getLogger(GamePersistence.class.getName()); + /** + * Saves the provided {@link Game} object to the specified file. + * + * @param gameFile the file to save the game state to + * @param game the {@link Game} object to save + * @throws IOException if an I/O error occurs while writing the game state to the file + */ public void saveTo(File gameFile, Game game) throws IOException { try (FileOutputStream os = new FileOutputStream(gameFile); ObjectOutputStream o = new ObjectOutputStream(os)) { @@ -33,6 +40,14 @@ public void saveTo(File gameFile, Game game) throws IOException { log.log(Level.DEBUG, "Saved to game state to: {0}", gameFile); } + /** + * Reads a {@link Game} object from the specified file and returns it. + * + * @param gameFile the file to load the game state from + * @return the {@link Game} object read from the file + * @throws IOException if an I/O error occurs while reading the game state from the file + * @throws ClassNotFoundException if the {@link Game} class cannot be found + */ public Game loadFrom(File gameFile) throws IOException, ClassNotFoundException { try (FileInputStream is = new FileInputStream(gameFile); ObjectInputStream o = new ObjectInputStream(is)) { @@ -41,10 +56,22 @@ public Game loadFrom(File gameFile) throws IOException, ClassNotFoundException { } } + /** + * A filter that rejects any loaded classes with more than 1000 object references to prevent + * deserialization attacks. + */ private static class GamePersistenceFilter implements ObjectInputFilter { // OVER-ENGINEER Reject any loaded classes games > 1000 object references private static final long MAX_REFERENCES = 1000; + /** + * Checks the input and returns a status indicating whether the input should be allowed or + * rejected. + * + * @param filterInfo the input filter information + * @return the status of the input, either {@link Status#ALLOWED}, {@link Status#REJECTED}, or + * {@link Status#UNDECIDED} + */ public Status checkInput(FilterInfo filterInfo) { return switch (filterInfo) { case FilterInfo fi when fi.references() > MAX_REFERENCES -> Status.REJECTED; diff --git a/api/src/main/java/org/xxdc/oss/example/GameServiceException.java b/api/src/main/java/org/xxdc/oss/example/GameServiceException.java index 6dfbc56..070b3b3 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameServiceException.java +++ b/api/src/main/java/org/xxdc/oss/example/GameServiceException.java @@ -1,12 +1,25 @@ package org.xxdc.oss.example; +/** Represents an exception that can occur when using the game service. */ public class GameServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + /** + * Constructs a new {@link GameServiceException} with the specified error message. + * + * @param message the error message describing the exception + */ public GameServiceException(String message) { super(message); } + /** + * Constructs a new {@link GameServiceException} with the specified error message and cause. + * + * @param message the error message describing the exception + * @param cause the underlying cause of the exception + */ public GameServiceException(String message, Throwable cause) { super(message, cause); } diff --git a/api/src/main/java/org/xxdc/oss/example/GameState.java b/api/src/main/java/org/xxdc/oss/example/GameState.java index f8ed7a7..037a5cc 100644 --- a/api/src/main/java/org/xxdc/oss/example/GameState.java +++ b/api/src/main/java/org/xxdc/oss/example/GameState.java @@ -4,14 +4,43 @@ import java.util.ArrayList; import java.util.List; +/** + * Represents the current state of a game, including the game board, player markers, and the index + * of the current player. Provides methods to interact with the game state, such as checking for + * available moves, checking for chains, and advancing the game to the next player's turn. + * Implements the JsonSerializable and Serializable interfaces, allowing the game state to be + * serialized and deserialized. + * + * @param board The game board. + * @param playerMarkers The list of player markers. + * @param currentPlayerIndex The index of the current player in the {@code playerMarkers} list. + * @param lastMove The index of the last move made on the game board. + */ public record GameState( GameBoard board, List playerMarkers, int currentPlayerIndex, int lastMove) implements JsonSerializable, Serializable { + /** + * Constructs a new {@link GameState} instance with the provided game board, player markers, and + * the index of the current player. The last move index is set to -1 to indicate that no move has + * been made yet. + * + * @param board The game board. + * @param playerMarkers The list of player markers. + * @param currentPlayerIndex The index of the current player in the {@code playerMarkers} list. + */ public GameState(GameBoard board, List playerMarkers, int currentPlayerIndex) { this(board, playerMarkers, currentPlayerIndex, -1); } + /** + * Constructs a new {@link GameState} instance by copying the state from the provided {@link + * GameState} object. This constructor creates a deep copy of the game board, player markers, and + * current player index, allowing the new {@link GameState} instance to be modified independently + * from the original. + * + * @param state The {@link GameState} object to copy. + */ public GameState(GameState state) { this( state.board, @@ -20,22 +49,49 @@ public GameState(GameState state) { state.lastMove); } + /** + * Returns the current player's marker. + * + * @return The current player's marker. + */ public String currentPlayer() { return playerMarkers.get(currentPlayerIndex); } + /** + * Checks if there are any available moves on the game board. + * + * @return {@code true} if there are available moves, {@code false} otherwise. + */ public boolean hasMovesAvailable() { return board.hasMovesAvailable(); } + /** + * Checks if the specified player has a chain on the game board. + * + * @param player The player marker to check for a chain. + * @return {@code true} if the player has a chain, {@code false} otherwise. + */ public boolean hasChain(String player) { return board.hasChain(player); } + /** + * Returns a list of available moves on the game board. + * + * @return A list of available moves on the game board. + */ public List availableMoves() { return board.availableMoves(); } + /** + * Checks if the game is in a terminal state, where either there are no more available moves or a + * player has a chain. + * + * @return {@code true} if the game is in a terminal state, {@code false} otherwise. + */ public boolean isTerminal() { if (!board.hasMovesAvailable()) { return true; @@ -65,16 +121,34 @@ public String asJsonString() { return json.toString(); } + /** + * Creates a new {@link GameState} instance with the current player's move applied to the game + * board. + * + * @param move The move to apply to the game board. + * @return A new {@link GameState} instance with the updated game board and current player index. + */ public GameState afterPlayerMoves(int move) { GameBoard newBoard = board.withMove(currentPlayer(), move); int newCurrentPlayerIndex = (currentPlayerIndex + 1) % playerMarkers.size(); return new GameState(newBoard, playerMarkers, newCurrentPlayerIndex, move); } + /** + * Checks if the last player to move has a chain on the game board. + * + * @return {@code true} if the last player has a chain, {@code false} otherwise. + */ public boolean lastPlayerHasChain() { return board.hasChain(lastPlayer()); } + /** + * Returns the last player's marker from the list of player markers. + * + * @return the last player's marker + * @throws GameServiceException if there is no last move or the board is empty + */ private String lastPlayer() { if (lastMove < 0 || board.isEmpty()) { throw new GameServiceException("null last player"); @@ -82,6 +156,11 @@ private String lastPlayer() { return playerMarkers.get(lastPlayerIndex()); } + /** + * Returns the index of the last player to move in the list of player markers. + * + * @return the index of the last player to move in the list of player markers + */ public int lastPlayerIndex() { return (currentPlayerIndex + playerMarkers.size() - 1) % playerMarkers.size(); } diff --git a/api/src/main/java/org/xxdc/oss/example/HumanPlayer.java b/api/src/main/java/org/xxdc/oss/example/HumanPlayer.java index bd873fa..46ea22c 100644 --- a/api/src/main/java/org/xxdc/oss/example/HumanPlayer.java +++ b/api/src/main/java/org/xxdc/oss/example/HumanPlayer.java @@ -39,10 +39,20 @@ public int nextMove(GameState state) { return move; } - static sealed interface Input { + /** Represents an input source that can be used to read a line of text. */ + public static sealed interface Input { + /** + * Reads a line of text from the input source. + * + * @return the line of text read from the input source. + */ String readLine(); } + /** + * An implementation of the {@link Input} interface that reads a line of text from the system + * console. + */ static final class ConsoleInput implements Input { @Override public String readLine() { @@ -50,6 +60,12 @@ public String readLine() { } } + /** + * An implementation of the {@link Input} interface that reads a line of text from the system + * input stream, using a {@link Scanner} that closes the underlying input stream when the JVM + * thread exits. This is useful for ensuring that the input stream is properly closed, even if an + * exception is thrown or the method is interrupted. + */ static final class ScannerInput implements Input { @Override public String readLine() { @@ -59,10 +75,20 @@ public String readLine() { } } + /** + * An {@link InputStream} implementation that closes the underlying input stream when the JVM + * thread exits. This is useful for ensuring that the input stream is properly closed, even if an + * exception is thrown or the method is interrupted. + */ static class CloseOnExitInputStream extends InputStream { private final InputStream in; + /** + * An {@link InputStream} implementation that closes the underlying input stream when the JVM + * thread exits. This is useful for ensuring that the input stream is properly closed, even if + * an exception is thrown or the method is interrupted. + */ public CloseOnExitInputStream(InputStream in) { this.in = in; } diff --git a/api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java b/api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java index bc77ed1..e95ceed 100644 --- a/api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java +++ b/api/src/main/java/org/xxdc/oss/example/InvalidMoveException.java @@ -4,10 +4,17 @@ public class InvalidMoveException extends RuntimeException { private static final long serialVersionUID = 1L; + /** Constructs a new {@link InvalidMoveException} with no detail message. */ public InvalidMoveException() { super(); } + /** + * Constructs a new {@link InvalidMoveException} with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link + * #getMessage()} method) + */ public InvalidMoveException(String message) { super(message); } diff --git a/api/src/main/java/org/xxdc/oss/example/PlayerIds.java b/api/src/main/java/org/xxdc/oss/example/PlayerIds.java index 826504c..a8849a5 100644 --- a/api/src/main/java/org/xxdc/oss/example/PlayerIds.java +++ b/api/src/main/java/org/xxdc/oss/example/PlayerIds.java @@ -3,10 +3,16 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +/** + * Manages player IDs for a game system. This class provides thread-safe operations for generating + * and retrieving player IDs. + */ public class PlayerIds { + /** The next available player ID. */ private volatile int nextId; + /** VarHandle for atomic operations on nextId. */ private static final VarHandle NEXT_ID_VH; static { @@ -17,14 +23,29 @@ public class PlayerIds { } } + /** + * Constructs a new PlayerIds instance with the specified initial value. + * + * @param initialValue The initial value for the next player ID. + */ public PlayerIds(int initialValue) { this.nextId = initialValue; } + /** + * Retrieves the current value of the next player ID without incrementing it. + * + * @return The current value of the next player ID. + */ public int getNextId() { return nextId; } + /** + * Atomically retrieves the current value of the next player ID and increments it. + * + * @return The current value of the next player ID before incrementing. + */ public int getNextIdAndIncrement() { return (int) NEXT_ID_VH.getAndAdd(this, 1); } diff --git a/api/src/main/java/org/xxdc/oss/example/PlayerNode.java b/api/src/main/java/org/xxdc/oss/example/PlayerNode.java index 9c6db7b..1320b4a 100644 --- a/api/src/main/java/org/xxdc/oss/example/PlayerNode.java +++ b/api/src/main/java/org/xxdc/oss/example/PlayerNode.java @@ -7,6 +7,17 @@ import org.xxdc.oss.example.transport.TransportConfiguration; import org.xxdc.oss.example.transport.TransportServer; +/** + * The `PlayerNode` interface represents a player in a game. It provides methods to get the player's + * marker and apply the player's next move to the game state. + * + *

The `Local` implementation of `PlayerNode` represents a local player, where the player's logic + * is implemented directly in the application. + * + *

The `Remote` implementation of `PlayerNode` represents a remote player, where the player's + * logic is implemented in a separate process and communicated with the application through a + * transport mechanism. + */ public sealed interface PlayerNode extends ToIntFunction { /** @@ -25,6 +36,14 @@ public sealed interface PlayerNode extends ToIntFunction { */ public int applyAsInt(GameState state); + /** + * Represents a local player node that implements the player's logic directly in the application. + * The local player node is responsible for providing the player's next move to apply to the game + * board. The local player node is serializable, allowing it to be persisted and transferred + * between processes. + * + * @param

the type of the player implementation + */ public final class Local

implements PlayerNode, Serializable { private static final long serialVersionUID = 1L; @@ -33,6 +52,13 @@ public final class Local

implements PlayerNode, Serializable { private final String playerMarker; + /** + * Constructs a new Local PlayerNode instance with the given player marker and player + * implementation. + * + * @param playerMarker the marker to identify this player + * @param player the player implementation to use for this node + */ public Local(String playerMarker, P player) { this.playerMarker = playerMarker; this.player = player; @@ -64,6 +90,13 @@ public String toString() { } } + /** + * Represents a remote player node that communicates with a client over a transport server. The + * remote player node sends the current game state to the client and receives the player's next + * move. The remote player node is responsible for validating the received move and applying it to + * the game board. The remote player node must be closed when the game is finished to release the + * transport server resources. + */ public final class Remote implements PlayerNode, AutoCloseable { private static final Logger log = System.getLogger(Remote.class.getName()); @@ -72,6 +105,14 @@ public final class Remote implements PlayerNode, AutoCloseable { private final TransportServer transport; + /** + * Constructs a new Remote PlayerNode instance that communicates with a client over the provided + * TransportServer. + * + * @param playerMarker the marker to identify this player + * @param transport the TransportServer to use for communication with the client + * @throws IllegalArgumentException if the provided TransportServer is null + */ public Remote(String playerMarker, TransportServer transport) { this.playerMarker = playerMarker; this.transport = transport; diff --git a/api/src/main/java/org/xxdc/oss/example/PlayerNodes.java b/api/src/main/java/org/xxdc/oss/example/PlayerNodes.java index 6a11a16..c61f215 100644 --- a/api/src/main/java/org/xxdc/oss/example/PlayerNodes.java +++ b/api/src/main/java/org/xxdc/oss/example/PlayerNodes.java @@ -8,6 +8,17 @@ import java.util.List; import java.util.SequencedMap; +/** + * Represents a collection of {@link PlayerNode} instances, providing methods to manage and render + * the players. + * + *

The {@code PlayerNodes} class maintains a {@link SequencedMap} of {@link PlayerNode} + * instances, indexed by their unique player marker. It also maintains a list of the player markers + * in the order they were added. + * + *

The class provides methods to retrieve players by index, get the next player index, render the + * player information, and close any {@link AutoCloseable} players. + */ public class PlayerNodes implements Serializable { private static final Logger log = System.getLogger(PlayerNodes.class.getName()); @@ -18,11 +29,22 @@ public class PlayerNodes implements Serializable { private final List playerMarkers; + /** + * Constructs a new {@code PlayerNodes} instance with an initial capacity of 2 for both the player + * map and the player marker list. + */ public PlayerNodes() { this.players = new LinkedHashMap(2); this.playerMarkers = new ArrayList(2); } + /** + * Creates a new {@code PlayerNodes} instance and adds the provided {@link PlayerNode} instances + * to it. + * + * @param players the {@link PlayerNode} instances to add to the new {@code PlayerNodes} instance + * @return a new {@code PlayerNodes} instance containing the provided {@link PlayerNode} instances + */ public static PlayerNodes of(PlayerNode... players) { var playerList = new PlayerNodes(); for (PlayerNode p : players) { @@ -31,14 +53,31 @@ public static PlayerNodes of(PlayerNode... players) { return playerList; } + /** + * Returns the {@link PlayerNode} instance at the specified index in the ordered list of players. + * + * @param index the index of the player to retrieve + * @return the {@link PlayerNode} instance at the specified index + */ public PlayerNode byIndex(int index) { return players.get(playerMarkers.get(index)); } + /** + * Returns the index of the next player in the ordered list of players. If the current index is + * the last player, it returns 0 to wrap around to the first player. + * + * @param index the current index of the player + * @return the index of the next player in the ordered list + */ public int nextPlayerIndex(int index) { return index + 1 < players.size() ? index + 1 : 0; } + /** + * Renders a list of players, logging their player identifiers. This method is used to display + * information about the players in the PlayerNodes instance. + */ public void render() { PlayerPrinter printer = new PlayerPrinter(); log.log(Level.INFO, "Players: {0} ({1})", playerMarkers(), players.values()); @@ -47,10 +86,20 @@ public void render() { } } + /** + * Returns a comma-separated string of all the player markers in the PlayerNodes instance. + * + * @return a string containing all the player markers + */ public String playerMarkers() { return String.join(", ", players.sequencedKeySet()); } + /** + * Returns a list of all the player markers in the PlayerNodes instance. + * + * @return a list of player markers + */ public List playerMarkerList() { return new ArrayList<>(playerMarkers); } diff --git a/api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java b/api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java index 604dfae..158ba1c 100644 --- a/api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java +++ b/api/src/main/java/org/xxdc/oss/example/PlayerPrinter.java @@ -3,8 +3,17 @@ import java.net.InetAddress; import java.net.UnknownHostException; +/** Provides a utility to generate a player identifier string for a given `PlayerNode`. */ public class PlayerPrinter { + /** + * Generates a player identifier string for the given `PlayerNode` instance. The identifier + * includes information about the player type (local or remote), the player marker, the Java + * version, the operating system, and the host IP address and name. + * + * @param player the `PlayerNode` instance to generate the identifier for + * @return a string representing the player identifier + */ public String getPlayerIdentifier(PlayerNode player) { String javaVersion = System.getProperty("java.version"); String osName = System.getProperty("os.name"); diff --git a/api/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java similarity index 96% rename from api/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java index 317ec60..f94d067 100644 --- a/api/src/main/java/org/xxdc/oss/example/SecureMessageHandler.java +++ b/api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java @@ -27,17 +27,17 @@ import org.bouncycastle.pqc.jcajce.spec.KyberParameterSpec; import org.xxdc.oss.example.security.KyberKEMProvider; -public abstract sealed class SecureMessageHandler implements MessageHandler { +public abstract sealed class SecureDuplexMessageHandler implements MessageHandler { - private static final Logger log = System.getLogger(SecureMessageHandler.class.getName()); + private static final Logger log = System.getLogger(SecureDuplexMessageHandler.class.getName()); - protected final RemoteMessageHandler handler; + protected final DuplexMessageHandler handler; protected SecretKey sharedKey; protected boolean initialized = false; - public SecureMessageHandler(RemoteMessageHandler handler) { + public SecureDuplexMessageHandler(DuplexMessageHandler handler) { this.handler = handler; registerSecurityProviders(); } @@ -181,14 +181,14 @@ private void checkInitialized() { * channel, exchanging the shared secret key with the client, and providing methods for sending * and receiving encrypted messages. */ - public static final class Server extends SecureMessageHandler { + public static final class Server extends SecureDuplexMessageHandler { /** * Constructs a new `SecureServerMessageHandler` instance with the given `RemoteMessageHandler`. * * @param remoteMessageHandler the `RemoteMessageHandler` to use for sending and receiving * messages */ - public Server(RemoteMessageHandler remoteMessageHandler) { + public Server(DuplexMessageHandler remoteMessageHandler) { super(remoteMessageHandler); } @@ -204,7 +204,7 @@ public void init() throws IOException { handler.init(); log.log( Level.DEBUG, - "Initializing secure channel for {0}. Exchanging shared key.", + "Initializing secure channel for {0}. Exchanging shared key...", getClass().getSimpleName()); sharedKey = exchangeSharedKey(); initialized = true; @@ -288,14 +288,14 @@ public void publishKey(PublicKey pk) throws IOException { * channel, exchanging the shared secret key with the server, and providing methods for sending * and receiving encrypted messages. */ - public static final class Client extends SecureMessageHandler { + public static final class Client extends SecureDuplexMessageHandler { /** * Constructs a new `SecureClientMessageHandler` instance with the given `RemoteMessageHandler`. * * @param handler the `RemoteMessageHandler` to use for the secure communication channel */ - public Client(RemoteMessageHandler handler) { + public Client(DuplexMessageHandler handler) { super(handler); } @@ -312,7 +312,7 @@ public void init() throws IOException { handler.init(); log.log( Level.DEBUG, - "Initializing secure channel for {0}. Exchanging shared key.", + "Initializing secure channel for {0}. Exchanging shared key...", getClass().getSimpleName()); sharedKey = exchangeSharedKey(); initialized = true; diff --git a/api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java b/api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java index a72282c..bfff58f 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/AlphaBeta.java @@ -4,6 +4,13 @@ import java.lang.System.Logger.Level; import org.xxdc.oss.example.GameState; +/** + * Implements the Alpha-Beta pruning algorithm for a two-player game. The algorithm evaluates game + * states and selects the best move for the current player. It uses a depth-limited search to + * explore the game tree, and prunes branches that are guaranteed to be inferior. The algorithm aims + * to maximize the score for the current player (the "maximizer") and minimize the score for the + * opponent. + */ public final class AlphaBeta implements BotStrategy { private static final Logger log = System.getLogger(AlphaBeta.class.getName()); @@ -16,10 +23,24 @@ public final class AlphaBeta implements BotStrategy { private final GameState initialState; private final BotStrategyConfig config; - public AlphaBeta(GameState state) { - this(state, BotStrategyConfig.newBuilder().build()); + /** + * Constructs an instance of the AlphaBeta bot strategy with the given initial game state and + * default configuration. + * + * @param initialState the initial game state for the bot to analyze + */ + public AlphaBeta(GameState initialState) { + this(initialState, BotStrategyConfig.newBuilder().build()); } + /** + * Constructs an instance of the AlphaBeta bot strategy with the given initial game state and + * configuration. + * + * @param initialState the initial game state for the bot to analyze + * @param config the configuration settings for the bot strategy + * @throws IllegalArgumentException if the initial game state does not have exactly two players + */ public AlphaBeta(GameState initialState, BotStrategyConfig config) { this.initialState = initialState; this.maximizer = initialState.currentPlayer(); @@ -29,6 +50,7 @@ public AlphaBeta(GameState initialState, BotStrategyConfig config) { this.config = config; } + @Override public int bestMove() { int bestMove = -1; int maxScore = -Integer.MAX_VALUE; diff --git a/api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java index f9e3bf4..77c44f4 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategy.java @@ -4,19 +4,69 @@ import java.util.function.ToIntFunction; import org.xxdc.oss.example.GameState; +/** + * An interface representing a bot strategy for a game returning a move for the current game state. + */ public sealed interface BotStrategy permits AlphaBeta, Minimax, MaxN, Random, Paranoid, MonteCarloTreeSearch { - public int bestMove(); + /** + * Returns the best move for the current game state. + * + * @return the index of the best move to make + */ + int bestMove(); + + /** + * Returns a function that returns the best move for the current game state using a Random + * strategy. + */ public static final ToIntFunction RANDOM = random(BotStrategyConfig.empty()); + + /** + * Returns a function that returns the best move for the current game state using a Minimax + * strategy. + */ public static final ToIntFunction MINIMAX = minimax(BotStrategyConfig.empty()); + + /** + * Returns a function that returns the best move for the current game state using a Minimax w. + * Alpha-Beta Pruning strategy. + */ public static final ToIntFunction ALPHABETA = alphabeta(BotStrategyConfig.empty()); + + /** + * Returns a function that returns the best move for the current game state using a MaxN strategy. + */ public static final ToIntFunction MAXN = maxn(BotStrategyConfig.empty()); + + /** + * Returns a function that returns the best move for the current game state using a Paranoid + * strategy. + */ public static final ToIntFunction PARANOID = paranoid(BotStrategyConfig.empty()); + + /** + * Returns a function that returns the best move for the current game state using a Monte Carlo + * Search Tree strategy. + */ public static final ToIntFunction MCTS = mcts(BotStrategyConfig.newBuilder().maxTimeMillis(TimeUnit.SECONDS, 2).build()); + + /** + * Returns a function that returns the best move for the current game state using a default + * strategy (Random). + */ public static final ToIntFunction DEFAULT = RANDOM; + /** + * Returns a function that returns the best move for the current game state using a Random + * strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a Random + * strategy + */ public static ToIntFunction random(BotStrategyConfig config) { return (state) -> { var strategy = new Random(state); @@ -24,6 +74,14 @@ public static ToIntFunction random(BotStrategyConfig config) { }; } + /** + * Returns a function that returns the best move for the current game state using a Minimax + * strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a Minimax + * strategy + */ public static ToIntFunction minimax(BotStrategyConfig config) { return (state) -> { var strategy = new Minimax(state, config); @@ -31,6 +89,14 @@ public static ToIntFunction minimax(BotStrategyConfig config) { }; } + /** + * Returns a function that returns the best move for the current game state using a Minimax w. + * Alpha-Beta Pruning strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a Minimax w. + * Alpha-Beta Pruning strategy + */ public static ToIntFunction alphabeta(BotStrategyConfig config) { return (state) -> { var alphabeta = new AlphaBeta(state, config); @@ -38,6 +104,12 @@ public static ToIntFunction alphabeta(BotStrategyConfig config) { }; } + /** + * Returns a function that returns the best move for the current game state using a MaxN strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a MaxN strategy + */ public static ToIntFunction maxn(BotStrategyConfig config) { return (state) -> { var maxn = new MaxN(state, config); @@ -45,6 +117,14 @@ public static ToIntFunction maxn(BotStrategyConfig config) { }; } + /** + * Returns a function that returns the best move for the current game state using a Paranoid + * strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a Paranoid + * strategy + */ public static ToIntFunction paranoid(BotStrategyConfig config) { return (state) -> { var paranoid = new Paranoid(state, config); @@ -52,6 +132,14 @@ public static ToIntFunction paranoid(BotStrategyConfig config) { }; } + /** + * Returns a function that returns the best move for the current game state using a Monte Carlo + * Tree Search strategy. + * + * @param config the configuration for the bot strategy + * @return a function that returns the best move for the current game state using a Monte Carlo + * Tree Search strategy + */ public static ToIntFunction mcts(BotStrategyConfig config) { return (state) -> { var montecarlo = new MonteCarloTreeSearch(state, config); diff --git a/api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java index 4e61a2c..5f8cdb3 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/BotStrategyConfig.java @@ -2,6 +2,11 @@ import java.util.concurrent.TimeUnit; +/** + * Represents the configuration for a bot strategy, including limits on the number of iterations, + * depth, and maximum execution time. The {@link Builder} class can be used to construct instances + * of this class. + */ public class BotStrategyConfig { private Integer maxIterations; private Integer maxDepth; @@ -15,76 +20,171 @@ private BotStrategyConfig(Integer maxIterations, Integer maxDepth, Long maxTimeM this.maxTimeMillis = maxTimeMillis; } + /** + * Returns the maximum number of iterations allowed for the bot strategy. + * + * @return the maximum number of iterations, or null if not set. + */ public Integer getMaxIterations() { return maxIterations; } + /** + * Returns the maximum depth allowed for the bot strategy. + * + * @return the maximum depth, or null if not set. + */ public Integer getMaxDepth() { return maxDepth; } + /** + * Returns the maximum execution time in milliseconds allowed for the bot strategy. + * + * @return the maximum execution time in milliseconds, or null if not set. + */ public Long getMaxTimeMillis() { return maxTimeMillis; } + /** + * Returns whether the maximum number of iterations has been set for the bot strategy. + * + * @return true if the maximum number of iterations has been set, false otherwise. + */ public boolean hasMaxIterations() { return maxIterations != null; } + /** + * Returns whether the maximum depth has been set for the bot strategy. + * + * @return true if the maximum depth has been set, false otherwise. + */ public boolean hasMaxDepth() { return maxDepth != null; } + /** + * Returns whether the maximum execution time in milliseconds has been set for the bot strategy. + * + * @return true if the maximum execution time in milliseconds has been set, false otherwise. + */ public boolean hasMaxTimeMillis() { return maxTimeMillis != null; } + /** + * Returns whether the number of iterations for the bot strategy exceeds the maximum allowed. + * + * @param iterations the current number of iterations + * @return true if the number of iterations exceeds the maximum, false otherwise + */ public boolean exceedsMaxIterations(int iterations) { return hasMaxIterations() && iterations >= maxIterations; } + /** + * Returns whether the current depth exceeds the maximum allowed depth for the bot strategy. + * + * @param depth the current depth + * @return true if the current depth exceeds the maximum allowed depth, false otherwise + */ public boolean exceedsMaxDepth(int depth) { return hasMaxDepth() && depth >= maxDepth; } + /** + * Returns whether the current execution time in milliseconds exceeds the maximum allowed time for + * the bot strategy. + * + * @param timeMillis the current execution time in milliseconds + * @return true if the current execution time exceeds the maximum allowed time, false otherwise + */ public boolean exceedsMaxTimeMillis(long timeMillis) { return hasMaxTimeMillis() && timeMillis >= maxTimeMillis; } + /** + * A builder for constructing a {@link BotStrategyConfig} instance. + * + *

This builder allows setting the maximum number of iterations, maximum depth, and maximum + * execution time in milliseconds for a bot strategy configuration. + */ public static class Builder { private Integer maxIterations; private Integer maxDepth; private Long maxTimeMillis; + /** + * Sets the maximum number of iterations for the bot strategy. + * + * @param maxIterations the maximum number of iterations + * @return this builder instance + */ public Builder maxIterations(Integer maxIterations) { this.maxIterations = maxIterations; return this; } + /** + * Sets the maximum depth for the bot strategy. + * + * @param maxDepth the maximum depth + * @return this builder instance + */ public Builder maxDepth(Integer maxDepth) { this.maxDepth = maxDepth; return this; } + /** + * Sets the maximum execution time in milliseconds for the bot strategy. + * + * @param maxTimeMillis the maximum execution time in milliseconds + * @return this builder instance + */ public Builder maxTimeMillis(Long maxTimeMillis) { this.maxTimeMillis = maxTimeMillis; return this; } + /** + * Sets the maximum execution time for the bot strategy. + * + * @param timeUnit the time unit of the provided time value + * @param time the maximum execution time + * @return this builder instance + */ public Builder maxTimeMillis(TimeUnit timeUnit, long time) { this.maxTimeMillis = timeUnit.toMillis(time); return this; } + /** + * Builds a {@link BotStrategyConfig} instance with the configured settings. + * + * @return the constructed {@link BotStrategyConfig} instance + */ public BotStrategyConfig build() { return new BotStrategyConfig(maxIterations, maxDepth, maxTimeMillis); } } + /** + * Creates a new {@link BotStrategyConfig.Builder} instance. + * + * @return a new {@link BotStrategyConfig.Builder} instance + */ public static BotStrategyConfig.Builder newBuilder() { return new BotStrategyConfig.Builder(); } + /** + * Returns an empty {@link BotStrategyConfig} instance. + * + * @return an empty {@link BotStrategyConfig} instance + */ public static BotStrategyConfig empty() { return EMPTY; } diff --git a/api/src/main/java/org/xxdc/oss/example/bot/MaxN.java b/api/src/main/java/org/xxdc/oss/example/bot/MaxN.java index 97c116b..b9fd243 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/MaxN.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/MaxN.java @@ -5,6 +5,11 @@ import java.util.Arrays; import org.xxdc.oss.example.GameState; +/** + * Implements the MaxN bot strategy for a game. The MaxN strategy tries to maximize the score for + * the current player, while considering the scores of all other players. It uses a recursive + * algorithm to explore the game tree and find the best move. + */ public final class MaxN implements BotStrategy { private static final Logger log = System.getLogger(MaxN.class.getName()); @@ -15,15 +20,27 @@ public final class MaxN implements BotStrategy { private final GameState initialState; private final BotStrategyConfig config; - public MaxN(GameState state) { - this(state, BotStrategyConfig.empty()); + /** + * Constructs a new MaxN bot strategy with the given initial game state and default configuration. + * + * @param initialState the initial game state to use for the bot strategy + */ + public MaxN(GameState initialState) { + this(initialState, BotStrategyConfig.empty()); } + /** + * Constructs a new MaxN bot strategy with the given initial game state and configuration. + * + * @param initialState the initial game state to use for the bot strategy + * @param config the configuration settings for the bot strategy + */ public MaxN(GameState initialState, BotStrategyConfig config) { this.initialState = initialState; this.config = config; } + @Override public int bestMove() { int bestMove = -1; int[] maxScores = new int[numberOfPlayers()]; diff --git a/api/src/main/java/org/xxdc/oss/example/bot/Minimax.java b/api/src/main/java/org/xxdc/oss/example/bot/Minimax.java index 05952b7..c642624 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/Minimax.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/Minimax.java @@ -4,6 +4,12 @@ import java.lang.System.Logger.Level; import org.xxdc.oss.example.GameState; +/** + * Implements a Minimax algorithm-based bot strategy for a game. The Minimax algorithm is a + * decision-making algorithm used in game theory and artificial intelligence to find the optimal + * move for a player, assuming the opponent plays optimally. This implementation of the Minimax + * algorithm is used to determine the best move for the current player in the game. + */ public final class Minimax implements BotStrategy { private static final Logger log = System.getLogger(Minimax.class.getName()); @@ -16,10 +22,24 @@ public final class Minimax implements BotStrategy { private final BotStrategyConfig config; private final GameState initialState; - public Minimax(GameState state) { - this(state, BotStrategyConfig.empty()); + /** + * Constructs a new Minimax instance with the given initial game state and default bot strategy + * configuration. + * + * @param initialState the initial game state to start the Minimax algorithm from + */ + public Minimax(GameState initialState) { + this(initialState, BotStrategyConfig.empty()); } + /** + * Constructs a new Minimax instance with the given initial game state and bot strategy + * configuration. + * + * @param initialState the initial game state to start the Minimax algorithm from + * @param config the bot strategy configuration to use for this Minimax instance + * @throws IllegalArgumentException if the initial game state does not have exactly two players + */ public Minimax(GameState initialState, BotStrategyConfig config) { this.initialState = initialState; this.maximizer = initialState.currentPlayer(); @@ -29,6 +49,7 @@ public Minimax(GameState initialState, BotStrategyConfig config) { this.config = config; } + @Override public int bestMove() { int bestMove = -1; int maxScore = -Integer.MAX_VALUE; diff --git a/api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java b/api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java index f174cfb..b56f3c5 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/MonteCarloTreeSearch.java @@ -49,6 +49,7 @@ public MonteCarloTreeSearch(GameState state, BotStrategyConfig config) { this.config = config; } + @Override public int bestMove() { return monteCarloTreeSearch(initialState); } @@ -72,6 +73,9 @@ private int monteCarloTreeSearch(GameState state) { return bestChild(root).state.lastMove(); } + /** + * Selects the best child node of the given node using the UCT (Upper Confidence Bound applied to + */ static class MCTSNode { GameState state; MCTSNode parent; @@ -79,10 +83,21 @@ static class MCTSNode { int visits; double[] scores; + /** + * Constructs a new MCTSNode with the given state and no parent. + * + * @param state the state of the game represented by this node + */ public MCTSNode(GameState state) { this(state, null); } + /** + * Constructs a new MCTSNode with the given state and parent node. + * + * @param state the state of the game represented by this node + * @param parent the parent node of this node + */ public MCTSNode(GameState state, MCTSNode parent) { this.state = state; this.parent = parent; @@ -91,6 +106,11 @@ public MCTSNode(GameState state, MCTSNode parent) { this.scores = new double[state.playerMarkers().size()]; } + /** + * Returns the child node with the highest UCT value. + * + * @return the child node with the highest UCT value + */ public MCTSNode select() { MCTSNode selected = null; double bestValue = Double.NEGATIVE_INFINITY; @@ -107,15 +127,21 @@ public MCTSNode select() { return selected; } + /** + * Expands the node by creating a new child node for each available move. + * + * @return + */ public boolean isFullyExpanded() { return children.size() == state.board().availableMoves().size(); } + @Override public String toString() { return toString(0); } - public String toString(int depth) { + String toString(int depth) { var builder = new StringBuilder(); builder.append(" ".repeat(depth * 2)); builder.append( @@ -178,7 +204,7 @@ private double[] defaultPolicy(GameState state) { return defaultReward(tempState); } - public double[] defaultReward(GameState state) { + private double[] defaultReward(GameState state) { var reward = new double[state.playerMarkers().size()]; int winningPlayerIndex = -1; for (int i = 0; i < state.playerMarkers().size(); i++) { diff --git a/api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java b/api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java index 0811437..d131417 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/Paranoid.java @@ -4,6 +4,13 @@ import java.lang.System.Logger.Level; import org.xxdc.oss.example.GameState; +/** + * Implements a "paranoid" bot strategy for a game. The bot tries to maximize its own score while + * minimizing the opponent's score. The strategy uses a recursive minimax algorithm to evaluate the + * best move for the current game state. The bot will choose the move that results in the highest + * score for itself, assuming the opponent will make the move that results in the lowest score for + * the bot. + */ public final class Paranoid implements BotStrategy { private static final Logger log = System.getLogger(Paranoid.class.getName()); @@ -14,16 +21,28 @@ public final class Paranoid implements BotStrategy { private final GameState initialState; private final BotStrategyConfig config; + /** + * Constructs a new Paranoid bot strategy with the given initial game state. + * + * @param initialState the initial game state for the bot to analyze + */ public Paranoid(GameState initialState) { this.initialState = initialState; this.config = BotStrategyConfig.empty(); } - public Paranoid(GameState state, BotStrategyConfig config) { - this.initialState = state; + /** + * Constructs a new Paranoid bot strategy with the given initial game state and configuration. + * + * @param initialState the initial game state for the bot to analyze + * @param config the configuration settings for the bot strategy + */ + public Paranoid(GameState initialState, BotStrategyConfig config) { + this.initialState = initialState; this.config = config; } + @Override public int bestMove() { int bestMove = -1; int maxScore = Integer.MIN_VALUE; diff --git a/api/src/main/java/org/xxdc/oss/example/bot/Random.java b/api/src/main/java/org/xxdc/oss/example/bot/Random.java index c1493ae..d55f165 100644 --- a/api/src/main/java/org/xxdc/oss/example/bot/Random.java +++ b/api/src/main/java/org/xxdc/oss/example/bot/Random.java @@ -4,11 +4,19 @@ import java.util.random.RandomGenerator; import org.xxdc.oss.example.GameState; +/** + * Implements a bot strategy that makes random moves based on the available moves in the game state. + */ public final class Random implements BotStrategy { private final RandomGenerator random; private final GameState state; + /** + * Constructs a new {@code Random} bot strategy instance with the given game state. + * + * @param state the current game state + */ public Random(GameState state) { this.state = state; this.random = new SecureRandom(); diff --git a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java index 5883f30..60d9735 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java +++ b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeGameBoard.java @@ -17,6 +17,12 @@ import org.xxdc.oss.example.GameBoard; import org.xxdc.oss.example.PlayerIds; +/** + * Implements the GameBoard interface for a Tic-Tac-Toe game board. This class manages the state of + * the game board, including tracking player moves, checking for winning conditions, and providing + * methods to interact with the game board. The implementation uses native functions to manage the + * game board state. + */ class TicTacToeGameBoard implements GameBoard { private static final Logger log = @@ -62,6 +68,14 @@ public void run() { private MethodHandle getGameBoardIsFull; private MethodHandle getGameBoardHasChain; + /** + * Constructs a new TicTacToeGameBoard instance with the specified dimension, SymbolLookup, and + * Cleaner. + * + * @param dimension the dimension of the game board (e.g. 3 for a 3x3 board) + * @param libTicTacToe the SymbolLookup instance for looking up native functions + * @param cleaner the Cleaner instance for managing native resources + */ public TicTacToeGameBoard(int dimension, SymbolLookup libTicTacToe, Cleaner cleaner) { this.libTicTacToe = libTicTacToe; this.playerMarkerToId = new HashMap<>(); @@ -73,6 +87,17 @@ public TicTacToeGameBoard(int dimension, SymbolLookup libTicTacToe, Cleaner clea cleaner.register(this, new CleanupTask(board, freeGameBoard)); } + /** + * Constructs a new TicTacToeGameBoard instance with the specified board, player marker to ID + * mapping, ID to player marker mapping, initial player ID value, SymbolLookup, and Cleaner. + * + * @param board the MemorySegment representing the game board + * @param playerMarkerToId a map of player markers to their unique IDs + * @param idToPlayerMarker a map of player IDs to their markers + * @param initialValue the initial value for the next player ID + * @param libTicTacToe the SymbolLookup instance for looking up native functions + * @param cleaner the Cleaner instance for managing native resources + */ TicTacToeGameBoard( MemorySegment board, Map playerMarkerToId, diff --git a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java index e93a2e9..0215eb5 100644 --- a/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java +++ b/api/src/main/java/org/xxdc/oss/example/interop/TicTacToeLibrary.java @@ -16,6 +16,11 @@ import org.xxdc.oss.example.GameBoard; import org.xxdc.oss.example.interop.loader.NativeLoader; +/** + * Provides a Java wrapper around a native TicTacToe library. This class handles loading the native + * library, initializing the required methods, and creating instances of {@link GameBoard} that + * represent the game state. + */ public final class TicTacToeLibrary { private static final Logger log = @@ -34,10 +39,17 @@ public final class TicTacToeLibrary { private MethodHandle version; private MethodHandle versionString; + /** Initializes the native TicTacToe library and prepares it for use. */ public TicTacToeLibrary() { initLibrary(); } + /** + * Creates a new {@link GameBoard} instance with the specified dimension. + * + * @param dimension the dimension of the game board (e.g. 3 for a 3x3 board) + * @return a new {@link GameBoard} instance representing the game board + */ public GameBoard newGameBoard(int dimension) { return new TicTacToeGameBoard(dimension, libTicTacToe, cleaner); } diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java index a8b9b15..bfdae6b 100644 --- a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java +++ b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java @@ -10,6 +10,10 @@ */ public class KyberKEMProvider extends Provider { + /** + * Constructs a new KyberKEMProvider instance that registers the "Kyber" KEM and "Kyber" algorithm + * parameters. + */ public KyberKEMProvider() { super("BCPQC.KEM", "1.0", "Provider for KyberKEM"); put("KEM.Kyber", KyberKEMSpi.class.getName()); diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java index 4476f0b..62aa93d 100644 --- a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java +++ b/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java @@ -31,6 +31,9 @@ */ public class KyberKEMSpi implements KEMSpi { + /** Constructs a new KyberKEMSpi instance. */ + public KyberKEMSpi() {} + @Override public EncapsulatorSpi engineNewEncapsulator( PublicKey publicKey, AlgorithmParameterSpec spec, SecureRandom secureRandom) @@ -72,6 +75,14 @@ private static class KyberEncapsulatorDecapsulatorSpi private SecureRandom random; + /** + * Constructs a new `KyberEncapsulatorDecapsulatorSpi` instance with the given public key, Kyber + * parameter specification, and secure random number generator. + * + * @param publicKey the public key to use for encapsulation + * @param parameterSpec the Kyber parameter specification to use + * @param random the secure random number generator to use + */ public KyberEncapsulatorDecapsulatorSpi( PublicKey publicKey, KyberParameterSpec parameterSpec, SecureRandom random) { this.publicKey = publicKey; @@ -79,6 +90,13 @@ public KyberEncapsulatorDecapsulatorSpi( this.random = random; } + /** + * Constructs a new `KyberEncapsulatorDecapsulatorSpi` instance with the given private key and + * Kyber parameter specification. + * + * @param privateKey the private key to use for decapsulation + * @param parameterSpec the Kyber parameter specification to use + */ public KyberEncapsulatorDecapsulatorSpi( PrivateKey privateKey, KyberParameterSpec parameterSpec) { this.privateKey = privateKey; diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java b/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java index a5341a3..a01a85e 100644 --- a/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java +++ b/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java @@ -12,8 +12,11 @@ * @author Brian Corbin */ public enum KyberParams { + /** Kyber-512 */ Kyber512(KyberParameters.kyber512), + /** Kyber-768 */ Kyber768(KyberParameters.kyber768), + /** Kyber-1024 */ Kyber1024(KyberParameters.kyber1024); private static Map paramsBySpec; @@ -34,10 +37,24 @@ public enum KyberParams { specsBySpecClass.put(KyberParameterSpec.kyber1024.getClass(), KyberParameterSpec.kyber1024); } + /** + * Returns the {@link KyberParams} instance corresponding to the given {@link KyberParameterSpec}. + * + * @param spec the {@link KyberParameterSpec} to get the {@link KyberParams} for + * @return the {@link KyberParams} instance corresponding to the given {@link KyberParameterSpec} + */ public static KyberParams byKyberParameterSpec(KyberParameterSpec spec) { return paramsBySpec.get(spec); } + /** + * Returns a new {@link AlgorithmParameterSpec} instance of the specified class, based on the + * corresponding {@link KyberParameterSpec} in the {@link #specsBySpecClass} map. + * + * @param the type of {@link AlgorithmParameterSpec} to return + * @param spec the class of {@link AlgorithmParameterSpec} to return + * @return a new instance of the specified {@link AlgorithmParameterSpec} class + */ public static T newParameterSpec(Class spec) { return spec.cast(specsBySpecClass.get(spec)); } @@ -46,14 +63,32 @@ public static T newParameterSpec(Class spe this.params = params; } + /** + * Returns the {@link KyberParameters} instance associated with this {@link KyberParams} enum + * value. + * + * @return the {@link KyberParameters} instance associated with this {@link KyberParams} enum + * value + */ public KyberParameters parameters() { return this.params; } + /** + * Returns the {@link KyberParams} instance corresponding to the provided encoded parameter bytes. + * + * @param encodedParams the encoded bytes representing a {@link KyberParams} instance + * @return the {@link KyberParams} instance corresponding to the provided encoded parameter bytes + */ public static KyberParams fromEncoded(byte[] encodedParams) { return valueOf(new String(encodedParams)); } + /** + * Encodes this {@link KyberParams} instance as a byte array. + * + * @return a byte array representation of this {@link KyberParams} instance + */ public byte[] encode() { return name().getBytes(); } diff --git a/api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java b/api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java index a43eb4e..86bd0e3 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/TransportConfiguration.java @@ -1,3 +1,8 @@ package org.xxdc.oss.example.transport; +/** + * Represents the configuration for the transport layer, including the player marker. + * + * @param playerMarker The marker used to identify the player in the transport layer. + */ public record TransportConfiguration(String playerMarker) {} diff --git a/api/src/main/java/org/xxdc/oss/example/transport/TransportException.java b/api/src/main/java/org/xxdc/oss/example/transport/TransportException.java index 8a088df..3d9d274 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/TransportException.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/TransportException.java @@ -1,16 +1,42 @@ package org.xxdc.oss.example.transport; +/** + * Represents an exception that occurred during transport-related operations. This exception is a + * subclass of {@link RuntimeException} and can be used to indicate transport-related errors that + * occur during the execution of the application. + */ public class TransportException extends RuntimeException { + private static final long serialVersionUID = 1L; + /** + * Constructs a new {@link TransportException} with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link + * #getMessage()} method). + */ public TransportException(String message) { super(message); } + /** + * Constructs a new {@link TransportException} with the specified detail message and cause. + * + * @param message the detail message (which is saved for later retrieval by the {@link + * #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + */ public TransportException(String message, Throwable cause) { super(message, cause); } + /** + * Constructs a new {@link TransportException} with the specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). + * (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + */ public TransportException(Throwable cause) { super(cause); } diff --git a/api/src/main/java/org/xxdc/oss/example/transport/Transports.java b/api/src/main/java/org/xxdc/oss/example/transport/Transports.java index 8c7a24e..ca4bb8f 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/Transports.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/Transports.java @@ -4,18 +4,30 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; +import org.xxdc.oss.example.DuplexMessageHandler; import org.xxdc.oss.example.Player; -import org.xxdc.oss.example.RemoteMessageHandler; -import org.xxdc.oss.example.SecureMessageHandler; +import org.xxdc.oss.example.SecureDuplexMessageHandler; import org.xxdc.oss.example.transport.tcp.TcpTransportClient; +/** A utility class for creating transport clients and servers. */ public class Transports { + private Transports() {} + + /** + * Creates a new TCP transport client for the given player and socket. + * + * @param

the type of player + * @param player the player instance + * @param socket the socket to use for the transport + * @return a new TCP transport client + * @throws IOException if an I/O error occurs + */ public static

TcpTransportClient

newTcpTransportClient( P player, Socket socket) throws IOException { return new TcpTransportClient

( - new SecureMessageHandler.Client( - new RemoteMessageHandler( + new SecureDuplexMessageHandler.Client( + new DuplexMessageHandler( new ObjectOutputStream(socket.getOutputStream()), new ObjectInputStream(socket.getInputStream()))), player); diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java index 10d6123..b532d6e 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java @@ -7,13 +7,31 @@ import org.xxdc.oss.example.GameBoard; import org.xxdc.oss.example.GameState; +/** + * Provides utility methods for parsing and formatting JSON messages used in the TCP protocol for + * the game. The protocol defines two main message types: "start" to indicate the game has started, + * and "nextMove" to communicate the current game state. + */ public class TcpProtocol { + /** + * A constant representing an empty JSON object, which can be used to indicate an exit or + * termination condition in the TCP protocol. + */ public static final String EXIT_CODE = "{}"; + /** + * A constant representing the JSON format for a "game started" message, which includes the + * version, message type, and the assigned player marker. + */ public static final String GAME_STARTED_JSON_FORMAT = "{" + "\"version\":1," + "\"message\":\"start\"," + "\"assignedPlayerMarker\":\"%s\"" + "}"; + /** + * A regular expression pattern that matches a JSON string representing a "game started" message. + * The pattern captures the following groups: 1. The version number as an integer 2. The message + * type as a string 3. The assigned player marker as a string + */ public static final Pattern GAME_STARTED_JSON_PATTERN = Pattern.compile( "\\{\\\"version\\\":(\\d+),\\\"message\\\":\\\"([^\\\"]+)\\\",\\\"assignedPlayerMarker\\\":\\\"([^\\\"]+)\\\".*}"); @@ -27,12 +45,37 @@ public class TcpProtocol { public static final String NEXT_MOVE_JSON_FORMAT = "{" + "\"version\":1," + "\"message\":\"nextMove\"," + "\"state\":%s" + "}"; + /** + * A regular expression pattern that matches a JSON string representing a "next move" message. The + * pattern captures the following groups: 1. The version number as an integer 2. The message type + * as a string 3. The player markers as a comma-separated string 4. The index of the current + * player as an integer 5. The dimension of the game board as an integer 6. The content of the + * game board as a comma-separated string + */ public static final Pattern NEXT_MOVE_JSON_PATTERN = Pattern.compile( "\\{\\\"version\\\":(\\d+),\\\"message\\\":\\\"([^\\\"]+)\\\"," + "\\\"state\\\":\\{\\\"playerMarkers\\\":\\[(.*)\\],\\\"currentPlayerIndex\\\":(\\d+)," + "\\\"board\\\":\\{\\\"dimension\\\":(\\d+),\\\"content\\\":\\[(.*)\\]\\}.*\\}}"); + /** + * Parses a JSON string representing a "next move" message and returns a {@link GameState} object + * containing the parsed information. + * + *

The JSON string is expected to have the following format (with no whitespace): + * + *

{ "version": 1, "message": "nextMove", "state": { "playerMarkers": ["X", "O"], + * "currentPlayerIndex": 1, "board": { "dimension": 3, "content": ["X", "O", null, null, "X", + * null, null, null, null] } } } + * + *

The method extracts the player markers, the index of the current player, the dimension of + * the game board, and the content of the game board from the JSON string. It then creates a + * {@link GameState} object with this information and returns it. + * + * @param serverMessage the JSON string representing the "next move" message + * @return an {@link Optional} containing the parsed {@link GameState} object, or {@link + * Optional#empty()} if the input string does not match the expected format + */ public static Optional fromNextMoveState(String serverMessage) { Matcher matcher = TcpProtocol.NEXT_MOVE_JSON_PATTERN.matcher(serverMessage); GameState state = null; @@ -52,6 +95,22 @@ public static Optional fromNextMoveState(String serverMessage) { return Optional.ofNullable(state); } + /** + * Parses a JSON string representing a "game started" message and returns the player marker + * assigned to the client. + * + *

The JSON string is expected to have the following format (with no whitespace): + * + *

{ "version": 1, "message": "gameStarted", "playerMarker": "X" } + * + *

The method extracts the player marker from the JSON string and returns it as an {@link + * Optional}. If the input string does not match the expected format, the method returns {@link + * Optional#empty()}. + * + * @param serverMessage the JSON string representing the "game started" message + * @return an {@link Optional} containing the player marker, or {@link Optional#empty()} if the + * input string does not match the expected format + */ public static Optional fromGameStartedState(String serverMessage) { Matcher matcher = TcpProtocol.GAME_STARTED_JSON_PATTERN.matcher(serverMessage); String playerMarker = null; diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java index 9ce912b..2a6a1b2 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java @@ -9,12 +9,29 @@ import org.xxdc.oss.example.Player; import org.xxdc.oss.example.transport.TransportException; +/** + * Represents a TCP transport client that handles communication with a server. The client is + * responsible for initializing the connection, sending and receiving messages, and handling the + * game logic based on the received messages. + * + * @param the type of player that the client represents + * @param connection the message handler used to communicate with the server + * @param player the player instance + */ public record TcpTransportClient(MessageHandler connection, T player) implements AutoCloseable { private static final Logger log = System.getLogger(MethodHandles.lookup().lookupClass().getName()); + /** + * Initializes a new instance of the {@link TcpTransportClient} class with the specified {@link + * MessageHandler} connection and {@link Player} instance. + * + * @param connection the message handler used to communicate with the server + * @param player the player instance + * @throws TransportException if an I/O exception occurs during initialization + */ public TcpTransportClient(MessageHandler connection, T player) { try { this.connection = connection; @@ -25,6 +42,11 @@ public TcpTransportClient(MessageHandler connection, T player) { } } + /** + * Runs the TCP transport client, initializing the player marker, receiving messages from the + * server, and handling the game logic based on the received messages. The client will continue to + * receive messages until an exit code is received from the server. + */ public void run() { log.log(Level.DEBUG, "Started TCP transport client"); try { diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java index 5ca4d6e..2577f6b 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java @@ -6,15 +6,20 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.net.Socket; +import org.xxdc.oss.example.DuplexMessageHandler; import org.xxdc.oss.example.GameState; import org.xxdc.oss.example.MessageHandler; -import org.xxdc.oss.example.RemoteMessageHandler; -import org.xxdc.oss.example.SecureMessageHandler; +import org.xxdc.oss.example.SecureDuplexMessageHandler; import org.xxdc.oss.example.transport.TransportConfiguration; import org.xxdc.oss.example.transport.TransportException; import org.xxdc.oss.example.transport.TransportServer; /** A {@link TransportServer} implementation that uses TCP sockets. */ +/** + * A {@link TransportServer} implementation that uses TCP sockets to handle communication between a + * client and server. This class is responsible for initializing the socket connection, sending game + * state updates to the client, and receiving client input through the socket. + */ public class TcpTransportServer implements TransportServer { private static final Logger log = System.getLogger(TcpTransportServer.class.getName()); @@ -23,12 +28,21 @@ public class TcpTransportServer implements TransportServer { private transient MessageHandler handler; + /** + * Constructs a new {@link TcpTransportServer} instance with the provided {@link Socket}. This + * constructor initializes the {@link SecureDuplexMessageHandler.Server} with a {@link + * DuplexMessageHandler} that uses the input and output streams of the provided socket. + * + * @param socket the {@link Socket} to use for the transport server + * @throws TransportException if an {@link IOException} occurs while initializing the message + * handler + */ public TcpTransportServer(Socket socket) { this.socket = socket; try { this.handler = - new SecureMessageHandler.Server( - new RemoteMessageHandler( + new SecureDuplexMessageHandler.Server( + new DuplexMessageHandler( new ObjectOutputStream(socket.getOutputStream()), new ObjectInputStream(socket.getInputStream()))); } catch (IOException e) { @@ -36,6 +50,19 @@ public TcpTransportServer(Socket socket) { } } + /** + * Constructs a new {@link TcpTransportServer} instance with the provided {@link Socket} and + * {@link MessageHandler}. This constructor initializes the {@link TcpTransportServer} with the + * given socket and message handler. + * + * @param socket the {@link Socket} to use for the transport server + * @param handler the {@link MessageHandler} to use for the transport server + */ + public TcpTransportServer(Socket socket, MessageHandler handler) { + this.socket = socket; + this.handler = handler; + } + @Override public void initialize(TransportConfiguration configuration) { log.log( diff --git a/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi b/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi index be5e21c..b7ce2b8 100644 --- a/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi +++ b/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi @@ -1 +1 @@ -org.xxdc.oss.example.KyberKEM \ No newline at end of file +org.xxdc.oss.example.security.KyberKEMSpi \ No newline at end of file diff --git a/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java b/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java index bdb3853..43745e5 100644 --- a/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java +++ b/api/src/test/java/org/xxdc/oss/example/GameBoardNativeImplTest.java @@ -29,8 +29,6 @@ public void test_empty_default_game_board_has_all_moves_available() { @Test public void should_load_library() { - printEnvironmentVariables(); - printSystemProperties(); GameBoard gameBoard = new GameBoardNativeImpl(); assertNotNull(gameBoard); } @@ -99,16 +97,10 @@ public void should_clean_up_native_resources() { gameBoard = null; if (idx++ % 100 == 0) { System.gc(); - log.log(Level.INFO, "GC'd " + idx / 100 + " times at " + idx + " iterations"); + log.log( + Level.INFO, + "GC'd " + idx / 100 + " times at " + idx + " iterations. GameBoard is " + gameBoard); } } } - - private void printSystemProperties() { - System.getProperties().forEach((k, v) -> log.log(Level.INFO, k + " = " + v)); - } - - private void printEnvironmentVariables() { - System.getenv().forEach((k, v) -> log.log(Level.INFO, k + " = " + v)); - } } diff --git a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java index 0b23ad6..31a6fcc 100644 --- a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java +++ b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java @@ -10,6 +10,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; import java.util.function.Supplier; import org.testng.annotations.Ignore; import org.testng.annotations.Test; @@ -35,6 +36,14 @@ public void testCanCreateClientServerHumanBotGame() throws Exception { } private void createClientServerGame(Supplier p1Supplier, Supplier p2Supplier) { + createClientServerGame( + p1Supplier, p2Supplier, (p1Socket, p2Socket) -> createSecureGame(p1Socket, p2Socket)); + } + + private void createClientServerGame( + Supplier p1Supplier, + Supplier p2Supplier, + BiFunction gameSupplier) { var executor = Executors.newVirtualThreadPerTaskExecutor(); var serverSocketFuture = startServerAsync(SERVER_PORT, executor); try { @@ -57,13 +66,11 @@ private void createClientServerGame(Supplier p1Supplier, Supplier acceptClientAsync(s, executor)); var client2SocketFuture = serverSocketFuture.thenCompose(s -> acceptClientAsync(s, executor)); - var connectedGame = - client1SocketFuture.thenCombine( - client2SocketFuture, - (client1Socket, client2Socket) -> createGame(client1Socket, client2Socket)); + var connectedGame = client1SocketFuture.thenCombine(client2SocketFuture, gameSupplier); // Wait for both clients to finish connecting and start the game var game = connectedGame.get(); + log.log(Level.INFO, "[Server] Game started."); game.play(); log.log(Level.INFO, "[Server] Game complete."); @@ -76,8 +83,12 @@ private void createClientServerGame(Supplier p1Supplier, Supplier { try { var socket = new Socket(host, port); - log.log(Level.INFO, "[Client] Client connected to server on port {0,number,#}", port); + log.log( + Level.INFO, + "[Client] Client connected to server on port {0,number,#} using local port" + + " {1,number,#}", + port, + socket.getLocalPort()); var client = Transports.newTcpTransportClient(player.get(), socket); client.run(); } catch (IOException e) { @@ -123,7 +139,7 @@ private static CompletableFuture acceptClientAsync( () -> { try { Socket clientSocket = serverSocket.accept(); - log.log(Level.INFO, "[Server] Client connected to port " + serverSocket.getLocalPort()); + log.log(Level.INFO, "[Server] Client connected to port " + clientSocket.getPort()); return clientSocket; } catch (IOException e) { throw new RuntimeException( diff --git a/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java new file mode 100644 index 0000000..f60f7f6 --- /dev/null +++ b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java @@ -0,0 +1,30 @@ +package org.xxdc.oss.example.security; + +import static org.testng.Assert.assertNotNull; + +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import javax.crypto.KEM; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class KyberKEMSpiTest { + + @Test + public void testCanCreateKyberKEMSpi() { + KyberKEMSpi kyberKEMSpi = new KyberKEMSpi(); + assertNotNull(kyberKEMSpi); + } + + @Test + public void testCanLoadRegisterKyberKEMSpi() { + Security.addProvider(new KyberKEMProvider()); + try { + var kem = KEM.getInstance("Kyber", "BCPQC.KEM"); + assertNotNull(kem); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + Assert.fail("Kyber KEM should be available", e); + } + } +} diff --git a/api/src/test/resources/logback-test.xml b/api/src/test/resources/logback-test.xml index ad6ebf5..e7af176 100644 --- a/api/src/test/resources/logback-test.xml +++ b/api/src/test/resources/logback-test.xml @@ -14,7 +14,7 @@ - + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b844c53..5262681 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -107,7 +107,6 @@ publishing { create("maven") { groupId = "org.xxdc.oss.example" artifactId = "tictactoe-app" - version = "1.1.0-jdk${java.toolchain.languageVersion}" from(components["java"]) pom { name.set("tictactoe") diff --git a/app/src/main/java/org/xxdc/oss/example/App.java b/app/src/main/java/org/xxdc/oss/example/App.java index 94a3884..ca637a7 100644 --- a/app/src/main/java/org/xxdc/oss/example/App.java +++ b/app/src/main/java/org/xxdc/oss/example/App.java @@ -7,7 +7,6 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import org.xxdc.oss.example.bot.BotStrategy; -import org.xxdc.oss.example.bot.BotStrategyConfig; /** A simple java tic-tac-toe game. */ public class App { @@ -20,93 +19,11 @@ public class App { * @throws Exception if there is an error whilst playing the game */ public void run() throws Exception { - var game = newStandardGameMCTS(); + var game = newStandardGame(); game.play(); game.close(); } - private Game newStandardGame() { - return new Game( - 3, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.ALPHABETA))); - } - - private Game newStandardGameMaxN() { - return new Game( - 3, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.MAXN))); - } - - private Game newStandardGameParanoid() { - return new Game( - 3, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.PARANOID))); - } - - private Game newStandardGameMCTS() { - return new Game( - 3, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.MCTS))); - } - - private Game newLargeStandardGame() { - return new Game( - 4, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>( - "O", - new BotPlayer( - BotStrategy.alphabeta(BotStrategyConfig.newBuilder().maxDepth(4).build())))); - } - - private Game newMultiplayerGameMCTS() { - return new Game( - 10, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.MCTS)), - new PlayerNode.Local<>("Y", new BotPlayer(BotStrategy.MCTS))); - } - - private Game newMultiplayerGameMaxN() { - // slow! - return new Game( - 5, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>( - "O", - new BotPlayer(BotStrategy.maxn(BotStrategyConfig.newBuilder().maxDepth(3).build()))), - new PlayerNode.Local<>( - "Y", - new BotPlayer(BotStrategy.maxn(BotStrategyConfig.newBuilder().maxDepth(3).build())))); - } - - private Game newMultiplayerGameParanoid() { - // slow! - return new Game( - 10, - false, - new PlayerNode.Local<>("X", new HumanPlayer()), - new PlayerNode.Local<>( - "O", - new BotPlayer( - BotStrategy.paranoid(BotStrategyConfig.newBuilder().maxDepth(2).build()))), - new PlayerNode.Local<>( - "Y", - new BotPlayer( - BotStrategy.paranoid(BotStrategyConfig.newBuilder().maxDepth(2).build())))); - } - /** * Runs the game from the specified file. * @@ -133,6 +50,9 @@ public String getGreeting() { * *

If command-line arguments are provided, it will load a saved game state from the specified * file. Otherwise, it will start a new game. + * + * @param args the command-line arguments + * @throws Exception if there is an error whilst playing the game or loading the game state */ public static void main(String[] args) throws Exception { App app = new App(); @@ -143,4 +63,12 @@ public static void main(String[] args) throws Exception { app.run(); } } + + private Game newStandardGame() { + return new Game( + 3, + false, + new PlayerNode.Local<>("X", new HumanPlayer()), + new PlayerNode.Local<>("O", new BotPlayer(BotStrategy.ALPHABETA))); + } } diff --git a/app/src/main/java/org/xxdc/oss/example/GameClient.java b/app/src/main/java/org/xxdc/oss/example/GameClient.java index 713bf8a..fc71785 100644 --- a/app/src/main/java/org/xxdc/oss/example/GameClient.java +++ b/app/src/main/java/org/xxdc/oss/example/GameClient.java @@ -12,6 +12,16 @@ import org.xxdc.oss.example.bot.BotStrategy; import org.xxdc.oss.example.transport.Transports; +/** + * The `GameClient` class is responsible for connecting to a game server and managing the execution + * of game clients. It submits game clients to an executor service, tracks the status of the + * clients, and logs relevant information. + * + *

The `main` method is the entry point of the application, which creates a `GameClient` instance + * and connects it to the server. The `connectToServer` method is responsible for submitting game + * clients to the executor service and handling any exceptions that may occur during the connection + * process. + */ public class GameClient { private static final Logger log = System.getLogger(GameClient.class.getName()); @@ -30,12 +40,32 @@ public class GameClient { private final LongAdder startedClients = new LongAdder(); + /** + * Constructs a new `GameClient` instance with the specified maximum number of games, server host, + * and server port. + * + * @param maxGames The maximum number of games to be played. + * @param serverHost The hostname or IP address of the game server. + * @param serverPort The port number of the game server. + */ public GameClient(int maxGames, String serverHost, int serverPort) { this.maxGames = maxGames; this.serverHost = serverHost; this.serverSocket = serverPort; } + /** + * The `main` method is the entry point of the application, which creates a `GameClient` instance + * and connects it to the server. It sets up an `ExecutorService` to manage the execution of game + * clients, logs the start of the client connection, and then calls the `connectToServer` method + * to submit the game clients. After the game clients have completed, it logs the final statistics + * of the client execution, including the number of submitted, started, failed, and completed + * clients. + * + * @param args Command-line arguments, where the first argument is the server host (default is + * "localhost") and the second argument is the server port (default is 9090). + * @throws Exception If any unexpected exceptions occur during the execution of the game clients. + */ public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); log.log(Level.INFO, "Client connecting for Tic-Tac-Toe."); diff --git a/app/src/main/java/org/xxdc/oss/example/GameServer.java b/app/src/main/java/org/xxdc/oss/example/GameServer.java index 3c4c8c9..7d3b87e 100644 --- a/app/src/main/java/org/xxdc/oss/example/GameServer.java +++ b/app/src/main/java/org/xxdc/oss/example/GameServer.java @@ -14,6 +14,11 @@ import java.util.function.Supplier; import org.xxdc.oss.example.transport.tcp.TcpTransportServer; +/** + * The `GameServer` class is responsible for managing the game server that hosts the tic-tac-toe + * game. It listens for incoming connections, creates game sessions, and tracks various game + * statistics. + */ public class GameServer { private static final Logger log = System.getLogger(GameServer.class.getName()); @@ -26,6 +31,25 @@ public class GameServer { private final LongAccumulator totalGames = new LongAccumulator(Long::sum, 0); + /** + * The `main` method is the entry point for the GameServer application. It creates a new + * `GameServer` instance, sets up a `ServerSocket` to listen for incoming connections, and starts + * the `listenForPlayers` method to handle incoming players and start new games. + * + *

The method first creates a `ServerSocket` on the specified port (or 9090 if no port is + * provided) with a backlog of 10,000 connections. It then creates a new `ExecutorService` using + * the `newVirtualThreadExecutor` method to handle the asynchronous game sessions. + * + *

The `listenForPlayers` method is called to start the server and listen for incoming player + * connections. If any exceptions occur during the server's operation, the `handleException` + * method is called to log the error. + * + *

Finally, the method logs the total number of games played and the maximum number of + * concurrent games before the server shuts down. + * + * @param args the command-line arguments passed to the application + * @throws Exception if there is an error starting the server + */ public static void main(String[] args) throws Exception { GameServer server = new GameServer(); try (ServerSocket serverSocket = diff --git a/app/src/main/resources/META-INF/services/javax.crypto.KEMSpi b/app/src/main/resources/META-INF/services/javax.crypto.KEMSpi deleted file mode 100644 index be5e21c..0000000 --- a/app/src/main/resources/META-INF/services/javax.crypto.KEMSpi +++ /dev/null @@ -1 +0,0 @@ -org.xxdc.oss.example.KyberKEM \ No newline at end of file diff --git a/app/src/test/resources/logback-test.xml b/app/src/test/resources/logback-test.xml index ad6ebf5..e7af176 100644 --- a/app/src/test/resources/logback-test.xml +++ b/app/src/test/resources/logback-test.xml @@ -14,7 +14,7 @@ - + \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts index 44ee93d..dcc7e38 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts @@ -19,6 +19,8 @@ dependencies { } } +val projectVersion by extra("1.1.0") + public val jdkVersion = 22 // Apply a specific Java toolchain to ease working on different environments. java { @@ -26,3 +28,4 @@ java { languageVersion = JavaLanguageVersion.of(jdkVersion) } } +version = "$projectVersion-jdk${jdkVersion}" \ No newline at end of file diff --git a/native/build.gradle.kts b/native/build.gradle.kts index 2e9cc30..d233022 100644 --- a/native/build.gradle.kts +++ b/native/build.gradle.kts @@ -111,7 +111,6 @@ publishing { create("maven") { groupId = "org.xxdc.oss.example" artifactId = "tictactoe-native-$libSuffix" - version = "1.0.0" from(components["java"]) pom { name.set("tictactoe") From 54cea95073712fc1a92b18e686eb7e8bfd65a25d Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 18:51:59 -0400 Subject: [PATCH 12/20] fix: under certain jvm vendors the Kyber security may fail due to jar signing. Switching to the version used by GitHub until signing is configured correctly --- api/build.gradle.kts | 1 - .../oss/example/security/KyberKEMSpiTest.java | 15 ++++++++++++++- .../buildlogic.java-common-conventions.gradle.kts | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 4c5a2d5..33cc978 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -79,7 +79,6 @@ testing { } } -// Apply a specific Java toolchain to ease working on different environments. java { withJavadocJar() withSourcesJar() diff --git a/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java index f60f7f6..2ed8e53 100644 --- a/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java +++ b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java @@ -9,6 +9,19 @@ import org.testng.Assert; import org.testng.annotations.Test; +/** + * Tests the functionality of the KyberKEMSpi class, which is an implementation of the + * Kyber Key Encapsulation Mechanism (KEM) algorithm. + * + * The tests cover the following scenarios: + * - Verifying that a new instance of KyberKEMSpi can be created successfully. + * - Verifying that the Kyber KEM algorithm can be loaded and used through the + * Java Security API. + * + * Note: This may fail if the Kyber KEM algorithm is not registered with the Java Security API due + * to java security provider issues and or if the jar file has not been signed with the + * correct key. The Temurin JDK does not worry about this. + */ public class KyberKEMSpiTest { @Test @@ -18,7 +31,7 @@ public void testCanCreateKyberKEMSpi() { } @Test - public void testCanLoadRegisterKyberKEMSpi() { + public void testCanLoadRegisteredKyberKEMSpi() { Security.addProvider(new KyberKEMProvider()); try { var kem = KEM.getInstance("Kyber", "BCPQC.KEM"); diff --git a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts index dcc7e38..962705a 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts @@ -26,6 +26,7 @@ public val jdkVersion = 22 java { toolchain { languageVersion = JavaLanguageVersion.of(jdkVersion) + vendor = JvmVendorSpec.ADOPTIUM } } version = "$projectVersion-jdk${jdkVersion}" \ No newline at end of file From c1d3ed6db7ebb0ca0215ce32b484a34c442c2711 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 18:54:10 -0400 Subject: [PATCH 13/20] fix: under certain jvm vendors the Kyber security may fail due to jar signing. Switching to the version used by GitHub until signing is configured correctly --- .../oss/example/security/KyberKEMSpiTest.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java index 2ed8e53..be80681 100644 --- a/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java +++ b/api/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java @@ -10,17 +10,16 @@ import org.testng.annotations.Test; /** - * Tests the functionality of the KyberKEMSpi class, which is an implementation of the - * Kyber Key Encapsulation Mechanism (KEM) algorithm. + * Tests the functionality of the KyberKEMSpi class, which is an implementation of the Kyber Key + * Encapsulation Mechanism (KEM) algorithm. * - * The tests cover the following scenarios: - * - Verifying that a new instance of KyberKEMSpi can be created successfully. - * - Verifying that the Kyber KEM algorithm can be loaded and used through the - * Java Security API. - * - * Note: This may fail if the Kyber KEM algorithm is not registered with the Java Security API due - * to java security provider issues and or if the jar file has not been signed with the - * correct key. The Temurin JDK does not worry about this. + *

The tests cover the following scenarios: - Verifying that a new instance of KyberKEMSpi can be + * created successfully. - Verifying that the Kyber KEM algorithm can be loaded and used through the + * Java Security API. + * + *

Note: This may fail if the Kyber KEM algorithm is not registered with the Java Security API + * due to java security provider issues and or if the jar file has not been signed with the correct + * key. The Temurin JDK does not worry about this. */ public class KyberKEMSpiTest { From d356dc80d185476c519bf62543d9443df1784cb4 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 20:32:28 -0400 Subject: [PATCH 14/20] refactor: refactor the TCP transport ready to migrate into tcp-gameserver --- .../{ => transport}/DuplexMessageHandler.java | 2 +- .../oss/example/{ => transport}/MessageHandler.java | 2 +- .../{ => transport}/SecureDuplexMessageHandler.java | 2 +- .../example/transport/tcp/TcpTransportClient.java | 2 +- .../example/transport/tcp/TcpTransportServer.java | 7 ++++--- .../{Transports.java => tcp/TcpTransports.java} | 12 ++++++------ .../java/org/xxdc/oss/example/TcpTransportTest.java | 5 +++-- .../main/java/org/xxdc/oss/example/GameClient.java | 4 ++-- 8 files changed, 19 insertions(+), 17 deletions(-) rename api/src/main/java/org/xxdc/oss/example/{ => transport}/DuplexMessageHandler.java (98%) rename api/src/main/java/org/xxdc/oss/example/{ => transport}/MessageHandler.java (96%) rename api/src/main/java/org/xxdc/oss/example/{ => transport}/SecureDuplexMessageHandler.java (99%) rename api/src/main/java/org/xxdc/oss/example/transport/{Transports.java => tcp/TcpTransports.java} (78%) diff --git a/api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java similarity index 98% rename from api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java index 123e712..aad1865 100644 --- a/api/src/main/java/org/xxdc/oss/example/DuplexMessageHandler.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example; +package org.xxdc.oss.example.transport; import java.io.IOException; import java.io.ObjectInputStream; diff --git a/api/src/main/java/org/xxdc/oss/example/MessageHandler.java b/api/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java similarity index 96% rename from api/src/main/java/org/xxdc/oss/example/MessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java index 78247c5..6bec9e6 100644 --- a/api/src/main/java/org/xxdc/oss/example/MessageHandler.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example; +package org.xxdc.oss.example.transport; import java.io.IOException; diff --git a/api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java b/api/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java similarity index 99% rename from api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java rename to api/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java index f94d067..b4b04f0 100644 --- a/api/src/main/java/org/xxdc/oss/example/SecureDuplexMessageHandler.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example; +package org.xxdc.oss.example.transport; import java.io.IOException; import java.lang.System.Logger; diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java index 2a6a1b2..4966352 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java @@ -5,8 +5,8 @@ import java.lang.System.Logger.Level; import java.lang.invoke.MethodHandles; import org.xxdc.oss.example.GameState; -import org.xxdc.oss.example.MessageHandler; import org.xxdc.oss.example.Player; +import org.xxdc.oss.example.transport.MessageHandler; import org.xxdc.oss.example.transport.TransportException; /** diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java index 2577f6b..b81e86b 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java @@ -6,10 +6,11 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.net.Socket; -import org.xxdc.oss.example.DuplexMessageHandler; + import org.xxdc.oss.example.GameState; -import org.xxdc.oss.example.MessageHandler; -import org.xxdc.oss.example.SecureDuplexMessageHandler; +import org.xxdc.oss.example.transport.DuplexMessageHandler; +import org.xxdc.oss.example.transport.MessageHandler; +import org.xxdc.oss.example.transport.SecureDuplexMessageHandler; import org.xxdc.oss.example.transport.TransportConfiguration; import org.xxdc.oss.example.transport.TransportException; import org.xxdc.oss.example.transport.TransportServer; diff --git a/api/src/main/java/org/xxdc/oss/example/transport/Transports.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java similarity index 78% rename from api/src/main/java/org/xxdc/oss/example/transport/Transports.java rename to api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java index ca4bb8f..fad0ec9 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/Transports.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java @@ -1,18 +1,18 @@ -package org.xxdc.oss.example.transport; +package org.xxdc.oss.example.transport.tcp; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; -import org.xxdc.oss.example.DuplexMessageHandler; + import org.xxdc.oss.example.Player; -import org.xxdc.oss.example.SecureDuplexMessageHandler; -import org.xxdc.oss.example.transport.tcp.TcpTransportClient; +import org.xxdc.oss.example.transport.DuplexMessageHandler; +import org.xxdc.oss.example.transport.SecureDuplexMessageHandler; /** A utility class for creating transport clients and servers. */ -public class Transports { +public class TcpTransports { - private Transports() {} + private TcpTransports() {} /** * Creates a new TCP transport client for the given player and socket. diff --git a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java index 31a6fcc..1834e95 100644 --- a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java +++ b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java @@ -14,8 +14,9 @@ import java.util.function.Supplier; import org.testng.annotations.Ignore; import org.testng.annotations.Test; -import org.xxdc.oss.example.transport.Transports; + import org.xxdc.oss.example.transport.tcp.TcpTransportServer; +import org.xxdc.oss.example.transport.tcp.TcpTransports; public class TcpTransportTest { @@ -124,7 +125,7 @@ private static void startClientAsync( + " {1,number,#}", port, socket.getLocalPort()); - var client = Transports.newTcpTransportClient(player.get(), socket); + var client = TcpTransports.newTcpTransportClient(player.get(), socket); client.run(); } catch (IOException e) { throw new RuntimeException("Error connecting to server on port " + port, e); diff --git a/app/src/main/java/org/xxdc/oss/example/GameClient.java b/app/src/main/java/org/xxdc/oss/example/GameClient.java index fc71785..c27d6d3 100644 --- a/app/src/main/java/org/xxdc/oss/example/GameClient.java +++ b/app/src/main/java/org/xxdc/oss/example/GameClient.java @@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import org.xxdc.oss.example.bot.BotStrategy; -import org.xxdc.oss.example.transport.Transports; +import org.xxdc.oss.example.transport.tcp.TcpTransports; /** * The `GameClient` class is responsible for connecting to a game server and managing the execution @@ -121,7 +121,7 @@ private void connectToServer(ExecutorService executor) { // Contention will cause SocketException, down Server ConnectException var socket = new Socket(serverHost, serverSocket); var client = - Transports.newTcpTransportClient( + TcpTransports.newTcpTransportClient( new BotPlayer(BotStrategy.MINIMAX), socket); ) { startedClients.increment(); socket.setKeepAlive(true); From 4ff3e81d2488f3a1572024789062e4e157bdd6c8 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 20:36:30 -0400 Subject: [PATCH 15/20] refactor: refactor the TCP transport ready to migrate into tcp-gameserver --- .../org/xxdc/oss/example/transport/tcp/TcpTransportServer.java | 1 - .../java/org/xxdc/oss/example/transport/tcp/TcpTransports.java | 1 - api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java | 1 - 3 files changed, 3 deletions(-) diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java index b81e86b..4c2d26e 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java @@ -6,7 +6,6 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.net.Socket; - import org.xxdc.oss.example.GameState; import org.xxdc.oss.example.transport.DuplexMessageHandler; import org.xxdc.oss.example.transport.MessageHandler; diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java index fad0ec9..d4d6ce7 100644 --- a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java +++ b/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java @@ -4,7 +4,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; - import org.xxdc.oss.example.Player; import org.xxdc.oss.example.transport.DuplexMessageHandler; import org.xxdc.oss.example.transport.SecureDuplexMessageHandler; diff --git a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java index 1834e95..3ce7c0c 100644 --- a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java +++ b/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java @@ -14,7 +14,6 @@ import java.util.function.Supplier; import org.testng.annotations.Ignore; import org.testng.annotations.Test; - import org.xxdc.oss.example.transport.tcp.TcpTransportServer; import org.xxdc.oss.example.transport.tcp.TcpTransports; From 59cb3e4b45cdb60697d9e688798a0395f480d4de Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 21:41:40 -0400 Subject: [PATCH 16/20] refactor: refactor the TCP transport into tcp-gameserver --- api/build.gradle.kts | 40 ----------- .../xxdc/oss/example/GamePerformanceTest.java | 55 --------------- app/build.gradle.kts | 32 --------- buildSrc/build.gradle.kts | 8 ++- ...ic.java-application-conventions.gradle.kts | 2 + ...ldlogic.java-common-conventions.gradle.kts | 47 ++++++++++++- ...dlogic.java-library-conventions.gradle.kts | 3 + .../example/interop/loader/NativeLoader.java | 3 +- settings.gradle.kts | 1 + tcp-gameserver/build.gradle.kts | 68 ++++++++++++++++++ .../java/org/xxdc/oss/example/GameClient.java | 0 .../java/org/xxdc/oss/example/GameServer.java | 0 .../example/transport/tcp/TcpProtocol.java | 0 .../transport/tcp/TcpTransportClient.java | 0 .../transport/tcp/TcpTransportServer.java | 0 .../example/transport/tcp/TcpTransports.java | 0 .../transport/tcp/GamePerformanceTest.java | 69 +++++++++++++++++++ .../transport/tcp}/TcpProtocolTest.java | 3 +- .../transport/tcp}/TcpTransportTest.java | 9 ++- 19 files changed, 202 insertions(+), 138 deletions(-) create mode 100644 tcp-gameserver/build.gradle.kts rename {app => tcp-gameserver}/src/main/java/org/xxdc/oss/example/GameClient.java (100%) rename {app => tcp-gameserver}/src/main/java/org/xxdc/oss/example/GameServer.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java (100%) create mode 100644 tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/GamePerformanceTest.java rename {api/src/test/java/org/xxdc/oss/example => tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp}/TcpProtocolTest.java (94%) rename {api/src/test/java/org/xxdc/oss/example => tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp}/TcpTransportTest.java (96%) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 33cc978..b0f9322 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -2,8 +2,6 @@ import java.io.File plugins { id("buildlogic.java-library-conventions") - id("com.diffplug.spotless") version "7.0.0.BETA1" - `maven-publish` } val libPath = "native/src/main/rust/target/debug" @@ -17,26 +15,6 @@ val libSuffix = when { else -> throw GradleException("Unsupported OS") } -repositories { - // Use Maven Central for resolving dependencies. - mavenLocal() - mavenCentral() - gradlePluginPortal() - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") - credentials { - username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") - password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") - } - } -} - -// Automatic code formatting before compile -tasks.named("compileJava") { - dependsOn("spotlessApply") -} - dependencies { // JDK21: KEM SPI (Third-Party) // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on @@ -69,29 +47,11 @@ tasks.register("jmh") { args = listOf("org.xxdc.oss.example.interop.benchmark.PlayerIdsBenchmark") } -testing { - suites { - // Configure the built-in test suite - val test by getting(JvmTestSuite::class) { - // Use TestNG test framework - useTestNG("7.5.1") - } - } -} - java { withJavadocJar() withSourcesJar() } -// Code formatting (./gradlew spotlessApply) -spotless { - java { - googleJavaFormat("1.23.0") - .reflowLongStrings() - } -} - // https://docs.gradle.org/current/userguide/publishing_maven.html publishing { repositories { diff --git a/api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java b/api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java index 22abe76..d6bb9f1 100644 --- a/api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java +++ b/api/src/test/java/org/xxdc/oss/example/GamePerformanceTest.java @@ -1,16 +1,11 @@ package org.xxdc.oss.example; -import java.io.IOException; import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.net.ServerSocket; -import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.IntStream; import org.testng.annotations.Ignore; import org.testng.annotations.Test; -import org.xxdc.oss.example.transport.tcp.TcpTransportServer; @Ignore public class GamePerformanceTest { @@ -67,56 +62,6 @@ public void testGameBotPerformanceInParallelWithVirtualThreads() throws Exceptio executor.awaitTermination(10, java.util.concurrent.TimeUnit.MINUTES); } - @Test - public void testGameClientServerBotPerformanceInParallelWithPlatformThreads() throws Exception { - ExecutorService executor = - Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - try (ServerSocket serverSocket = new ServerSocket(9090)) { - // round-robin match making for up to 1000 games - execClientServerGames(executor, serverSocket); - } catch (Exception e) { - System.out.println(e); - throw new RuntimeException(e); - } - executor.shutdown(); - executor.awaitTermination(10, java.util.concurrent.TimeUnit.MINUTES); - } - - @Test - public void testGameClientServerBotPerformanceInParallelWithVirtualThreads() throws Exception { - ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); - try (ServerSocket serverSocket = new ServerSocket(9090)) { - // round-robin match making for up to 1000 games - execClientServerGames(executor, serverSocket); - } catch (Exception e) { - System.out.println(e); - throw new RuntimeException(e); - } - executor.shutdown(); - executor.awaitTermination(10, java.util.concurrent.TimeUnit.MINUTES); - } - - private void execClientServerGames(ExecutorService executor, ServerSocket serverSocket) - throws IOException { - for (int i = 0; i < 40000; i++) { - log.log(Level.INFO, "Accepting connections for game {0}", i); - Socket playerOne = serverSocket.accept(); - Socket playerTwo = serverSocket.accept(); - executor.submit( - () -> { - try (var playerX = new PlayerNode.Remote("X", new TcpTransportServer(playerOne)); - var playerO = new PlayerNode.Remote("O", new TcpTransportServer(playerTwo))) { - Game game = new Game(3, false, playerX, playerO); - game.play(); - game.close(); - } catch (Exception e) { - System.out.println(e); - throw new RuntimeException(e); - } - }); - } - } - private PlayerNode newBotPlayer(String playerMarker) { return new PlayerNode.Local<>(playerMarker, new BotPlayer()); } diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5262681..d527f61 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,20 +4,6 @@ plugins { // Apply the application plugin to add support for building a CLI application in Java. id("buildlogic.java-application-conventions") id("org.graalvm.buildtools.native") version "0.10.2" - id("com.diffplug.spotless") version "7.0.0.BETA1" - `maven-publish` -} - -repositories { - // Use Maven Central for resolving dependencies. - mavenLocal() - mavenCentral() - gradlePluginPortal() -} - -// Automatic code formatting before compile -tasks.named("compileJava") { - dependsOn("spotlessApply") } dependencies { @@ -34,30 +20,12 @@ dependencies { runtimeOnly("org.slf4j:slf4j-jdk-platform-logging:2.0.13") } -testing { - suites { - // Configure the built-in test suite - val test by getting(JvmTestSuite::class) { - // Use TestNG test framework - useTestNG("7.5.1") - } - } -} - // Apply a specific Java toolchain to ease working on different environments. java { withJavadocJar() withSourcesJar() } -// Code formatting (./gradlew spotlessApply) -spotless { - java { - googleJavaFormat("1.23.0") - .reflowLongStrings() - } -} - // Allow GraalVM native AOT compilation graalvmNative { binaries { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 83b59c1..792e674 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,4 +10,10 @@ plugins { repositories { // Use the plugin portal to apply community plugins in convention plugins. gradlePluginPortal() -} \ No newline at end of file + // Also use maven central for dependencies + mavenCentral() +} + +dependencies { + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.23.3") // Use the latest version +} diff --git a/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts index b9b34c9..2b5e0be 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts @@ -8,4 +8,6 @@ plugins { // Apply the application plugin to add support for building a CLI application in Java. application + // Apply the maven-publish plugin for publishing to repositories + `maven-publish` } diff --git a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts index 962705a..58c886a 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts @@ -1,6 +1,5 @@ -/* - * This file was generated by the Gradle 'init' task. - */ +import com.diffplug.gradle.spotless.SpotlessExtension +import org.gradle.api.Project plugins { // Apply the java Plugin to add support for Java. @@ -9,9 +8,41 @@ plugins { repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() + gradlePluginPortal() + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } +} + +// Automatic code formatting before compile +tasks.named("compileJava") { + dependsOn("spotlessApply") } +// Code formatting (./gradlew spotlessApply) +fun Project.configureSpotless() { + pluginManager.apply("com.diffplug.spotless") + + extensions.configure { + java { + googleJavaFormat("1.23.0") + .reflowLongStrings() + removeUnusedImports() + } + kotlin { + ktlint() + } + } +} +configureSpotless() + dependencies { constraints { // Define dependency versions as constraints @@ -19,6 +50,16 @@ dependencies { } } +testing { + suites { + // Configure the built-in test suite + val test by getting(JvmTestSuite::class) { + // Use TestNG test framework + useTestNG("7.5.1") + } + } +} + val projectVersion by extra("1.1.0") public val jdkVersion = 22 diff --git a/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts index ab305d8..71afed7 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts @@ -8,4 +8,7 @@ plugins { // Apply the java-library plugin for API and implementation separation. `java-library` + + // Apply the maven-publish plugin for publishing to repositories + `maven-publish` } diff --git a/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java b/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java index 1d5c0a2..f6f0c58 100644 --- a/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java +++ b/native/src/main/java/org/xxdc/oss/example/interop/loader/NativeLoader.java @@ -25,8 +25,7 @@ private static SymbolLookup fromClassPathToTemp(String platformLibraryName, Aren throws IOException { var resourceLookupString = "/" + platformLibraryName; var tempResourceFile = Files.createTempFile(platformLibraryName, ".tmp"); - try (var inputStream = - NativeLoader.class.getResourceAsStream(resourceLookupString); + try (var inputStream = NativeLoader.class.getResourceAsStream(resourceLookupString); OutputStream outputStream = Files.newOutputStream(tempResourceFile)) { inputStream.transferTo(outputStream); return SymbolLookup.libraryLookup(tempResourceFile, arena); diff --git a/settings.gradle.kts b/settings.gradle.kts index 8956004..5ac6f87 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,3 +15,4 @@ rootProject.name = "overengineering-tictactoe" include("api") include("app") include("native") +include("tcp-gameserver") diff --git a/tcp-gameserver/build.gradle.kts b/tcp-gameserver/build.gradle.kts new file mode 100644 index 0000000..8c6d4e2 --- /dev/null +++ b/tcp-gameserver/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + id("buildlogic.java-library-conventions") +} + +dependencies { + implementation(project(":api")) +} + +java { + withJavadocJar() + withSourcesJar() +} + +// https://docs.gradle.org/current/userguide/publishing_maven.html +publishing { + repositories { + // Publish to GitHub Packages + // https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/briancorbinxyz/overengineering-tictactoe") + credentials { + username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } + } + publications { + create("maven") { + groupId = "org.xxdc.oss.example" + artifactId = "tictactoe-tcp-gameserver" + from(components["java"]) + pom { + name.set("tictactoe") + description.set("An Over-Engineered Tic Tac Toe Game API") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + licenses { + license { + name.set("MIT License") + url.set("https://opensource.org/licenses/MIT") + } + developers { + developer { + id.set("briancorbinxyz") + name.set("Brian Corbin") + email.set("mail@briancorbin.xyz") + } + } + } + scm { + connection.set("scm:git:git://github.com/briancorbinxyz/overengineering-tictactoe.git") + developerConnection.set("scm:git:ssh://github.com/briancorbinxyz/overengineering-tictactoe.git") + url.set("https://github.com/briancorbinxyz/overengineering-tictactoe") + } + } + } + } +} + +tasks.withType().all { + // JDK22: Foreign Function Interface (FFI) + // Resolves Warning: + // WARNING: A restricted method in java.lang.foreign.SymbolLookup has been called + // WARNING: java.lang.foreign.SymbolLookup::libraryLookup has been called by org.xxdc.oss.example.GameBoardNativeImpl in an unnamed module + // WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module + // WARNING: Restricted methods will be blocked in a future release unless native access is enabled + jvmArgs = listOf("--enable-native-access=ALL-UNNAMED", "-XX:+UseZGC") +} \ No newline at end of file diff --git a/app/src/main/java/org/xxdc/oss/example/GameClient.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/GameClient.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameClient.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/GameClient.java diff --git a/app/src/main/java/org/xxdc/oss/example/GameServer.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/GameServer.java similarity index 100% rename from app/src/main/java/org/xxdc/oss/example/GameServer.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/GameServer.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpProtocol.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportClient.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransportServer.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/tcp/TcpTransports.java diff --git a/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/GamePerformanceTest.java b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/GamePerformanceTest.java new file mode 100644 index 0000000..d62628b --- /dev/null +++ b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/GamePerformanceTest.java @@ -0,0 +1,69 @@ +package org.xxdc.oss.example.transport.tcp; + +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.testng.annotations.Ignore; +import org.testng.annotations.Test; +import org.xxdc.oss.example.Game; +import org.xxdc.oss.example.PlayerNode; + +@Ignore +public class GamePerformanceTest { + + private static final Logger log = System.getLogger(GamePerformanceTest.class.getName()); + + @Test + public void testGameClientServerBotPerformanceInParallelWithPlatformThreads() throws Exception { + ExecutorService executor = + Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + try (ServerSocket serverSocket = new ServerSocket(9090)) { + // round-robin match making for up to 1000 games + execClientServerGames(executor, serverSocket); + } catch (Exception e) { + System.out.println(e); + throw new RuntimeException(e); + } + executor.shutdown(); + executor.awaitTermination(10, java.util.concurrent.TimeUnit.MINUTES); + } + + @Test + public void testGameClientServerBotPerformanceInParallelWithVirtualThreads() throws Exception { + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + try (ServerSocket serverSocket = new ServerSocket(9090)) { + // round-robin match making for up to 1000 games + execClientServerGames(executor, serverSocket); + } catch (Exception e) { + System.out.println(e); + throw new RuntimeException(e); + } + executor.shutdown(); + executor.awaitTermination(10, java.util.concurrent.TimeUnit.MINUTES); + } + + private void execClientServerGames(ExecutorService executor, ServerSocket serverSocket) + throws IOException { + for (int i = 0; i < 40000; i++) { + log.log(Level.INFO, "Accepting connections for game {0}", i); + Socket playerOne = serverSocket.accept(); + Socket playerTwo = serverSocket.accept(); + executor.submit( + () -> { + try (var playerX = new PlayerNode.Remote("X", new TcpTransportServer(playerOne)); + var playerO = new PlayerNode.Remote("O", new TcpTransportServer(playerTwo))) { + Game game = new Game(3, false, playerX, playerO); + game.play(); + game.close(); + } catch (Exception e) { + System.out.println(e); + throw new RuntimeException(e); + } + }); + } + } +} diff --git a/api/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpProtocolTest.java similarity index 94% rename from api/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java rename to tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpProtocolTest.java index 7d21901..b9241d6 100644 --- a/api/src/test/java/org/xxdc/oss/example/TcpProtocolTest.java +++ b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpProtocolTest.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example; +package org.xxdc.oss.example.transport.tcp; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -6,7 +6,6 @@ import java.util.List; import java.util.regex.Matcher; import org.testng.annotations.Test; -import org.xxdc.oss.example.transport.tcp.TcpProtocol; public class TcpProtocolTest { diff --git a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpTransportTest.java similarity index 96% rename from api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java rename to tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpTransportTest.java index 3ce7c0c..d5aa348 100644 --- a/api/src/test/java/org/xxdc/oss/example/TcpTransportTest.java +++ b/tcp-gameserver/src/test/java/org/xxdc/oss/example/transport/tcp/TcpTransportTest.java @@ -1,4 +1,4 @@ -package org.xxdc.oss.example; +package org.xxdc.oss.example.transport.tcp; import java.io.IOException; import java.lang.System.Logger; @@ -14,8 +14,11 @@ import java.util.function.Supplier; import org.testng.annotations.Ignore; import org.testng.annotations.Test; -import org.xxdc.oss.example.transport.tcp.TcpTransportServer; -import org.xxdc.oss.example.transport.tcp.TcpTransports; +import org.xxdc.oss.example.BotPlayer; +import org.xxdc.oss.example.Game; +import org.xxdc.oss.example.HumanPlayer; +import org.xxdc.oss.example.Player; +import org.xxdc.oss.example.PlayerNode; public class TcpTransportTest { From 830148eedb7c5573efe2e549df8bea976f91d42e Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 22:07:08 -0400 Subject: [PATCH 17/20] refactor: refactor the security and messaging layer into tcp-gameserver --- .vscode/launch.json | 54 +++++++++++------- api/build.gradle.kts | 5 -- .../META-INF/services/javax.crypto.KEMSpi | 1 - tcp-gameserver/.DS_Store | Bin 0 -> 6148 bytes tcp-gameserver/build.gradle.kts | 11 ++++ tcp-gameserver/src/.DS_Store | Bin 0 -> 6148 bytes .../example/security/KyberKEMProvider.java | 0 .../oss/example/security/KyberKEMSpi.java | 0 .../example/security/KyberParametersSpi.java | 0 .../oss/example/security/KyberParams.java | 0 .../transport/DuplexMessageHandler.java | 0 .../oss/example/transport/MessageHandler.java | 0 .../transport/SecureDuplexMessageHandler.java | 0 tcp-gameserver/src/main/resources/logback.xml | 41 +++++++++++++ tcp-gameserver/src/test/.DS_Store | Bin 0 -> 6148 bytes .../oss/example/security/KyberKEMSpiTest.java | 0 .../security}/SecureConnectionTest.java | 3 +- .../src/test/resources/logback-test.xml | 20 +++++++ 18 files changed, 107 insertions(+), 28 deletions(-) delete mode 100644 api/src/main/resources/META-INF/services/javax.crypto.KEMSpi create mode 100644 tcp-gameserver/.DS_Store create mode 100644 tcp-gameserver/src/.DS_Store rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/security/KyberParams.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java (100%) rename {api => tcp-gameserver}/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java (100%) create mode 100644 tcp-gameserver/src/main/resources/logback.xml create mode 100644 tcp-gameserver/src/test/.DS_Store rename {api => tcp-gameserver}/src/test/java/org/xxdc/oss/example/security/KyberKEMSpiTest.java (100%) rename {api/src/test/java/org/xxdc/oss/example => tcp-gameserver/src/test/java/org/xxdc/oss/example/security}/SecureConnectionTest.java (99%) create mode 100644 tcp-gameserver/src/test/resources/logback-test.xml diff --git a/.vscode/launch.json b/.vscode/launch.json index e01d0d7..6a22738 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,20 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "type": "java", + "name": "GameClient", + "request": "launch", + "mainClass": "org.xxdc.oss.example.GameClient", + "projectName": "tcp-gameserver" + }, + { + "type": "java", + "name": "GameServer", + "request": "launch", + "mainClass": "org.xxdc.oss.example.GameServer", + "projectName": "tcp-gameserver" + }, { "type": "java", "name": "Launch Current File", @@ -86,10 +100,10 @@ "projectName": "app", "env": { "LIB_PATH": "${workspaceFolder}/app/build/cargo/debug", - "PATH": "${workspaceFolder}/app/build/cargo/debug", // For Windows - "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", // For Linux - "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" // For macOS - }, + "PATH": "${workspaceFolder}/app/build/cargo/debug", + "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", + "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" + } }, { "type": "java", @@ -99,10 +113,10 @@ "projectName": "app", "env": { "LIB_PATH": "${workspaceFolder}/app/build/cargo/debug", - "PATH": "${workspaceFolder}/app/build/cargo/debug", // For Windows - "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", // For Linux - "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" // For macOS - }, + "PATH": "${workspaceFolder}/app/build/cargo/debug", + "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", + "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" + } }, { "type": "java", @@ -113,10 +127,10 @@ "projectName": "app", "env": { "LIB_PATH": "${workspaceFolder}/app/build/cargo/debug", - "PATH": "${workspaceFolder}/app/build/cargo/debug", // For Windows - "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", // For Linux - "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" // For macOS - }, + "PATH": "${workspaceFolder}/app/build/cargo/debug", + "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", + "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" + } }, { "type": "java", @@ -126,10 +140,10 @@ "projectName": "app", "env": { "LIB_PATH": "${workspaceFolder}/app/build/cargo/debug", - "PATH": "${workspaceFolder}/app/build/cargo/debug", // For Windows - "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", // For Linux - "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" // For macOS - }, + "PATH": "${workspaceFolder}/app/build/cargo/debug", + "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", + "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" + } }, { "type": "java", @@ -140,10 +154,10 @@ "projectName": "app", "env": { "LIB_PATH": "${workspaceFolder}/app/build/cargo/debug", - "PATH": "${workspaceFolder}/app/build/cargo/debug", // For Windows - "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", // For Linux - "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" // For macOS - }, + "PATH": "${workspaceFolder}/app/build/cargo/debug", + "LD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug", + "DYLD_LIBRARY_PATH": "${workspaceFolder}/app/build/cargo/debug" + } } ] } \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index b0f9322..7aa6ce1 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -16,11 +16,6 @@ val libSuffix = when { } dependencies { - // JDK21: KEM SPI (Third-Party) - // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on - // -> JDK API -> Bouncycastle - implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") - // Native Library (Rust) implementation(project(":native")) testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.0.0") diff --git a/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi b/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi deleted file mode 100644 index b7ce2b8..0000000 --- a/api/src/main/resources/META-INF/services/javax.crypto.KEMSpi +++ /dev/null @@ -1 +0,0 @@ -org.xxdc.oss.example.security.KyberKEMSpi \ No newline at end of file diff --git a/tcp-gameserver/.DS_Store b/tcp-gameserver/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5172429f264de2441865cb4700216d4256da9242 GIT binary patch literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=HsM+*^= literal 0 HcmV?d00001 diff --git a/tcp-gameserver/build.gradle.kts b/tcp-gameserver/build.gradle.kts index 8c6d4e2..bb63f08 100644 --- a/tcp-gameserver/build.gradle.kts +++ b/tcp-gameserver/build.gradle.kts @@ -3,6 +3,17 @@ plugins { } dependencies { + // JDK21: KEM SPI (Third-Party) + // https://central.sonatype.com/artifact/org.bouncycastle/bcprov-jdk18on + // -> JDK API -> Bouncycastle + implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + + // JDK9: Platform Logging (Third-Party) + // -> JDK API -> SLF4J -> Logback + testRuntimeOnly("ch.qos.logback:logback-classic:1.5.6") + testRuntimeOnly("org.slf4j:slf4j-api:2.0.13") + testRuntimeOnly("org.slf4j:slf4j-jdk-platform-logging:2.0.13") + implementation(project(":api")) } diff --git a/tcp-gameserver/src/.DS_Store b/tcp-gameserver/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..24c09aabcbaa83b41ec2b85fc8edd4de88145572 GIT binary patch literal 6148 zcmeHK%}T>S5Z<-XrW7Fug&r5Y7Hmx^h?fxS3mDOZN=-=6V9b^#wTDv3SzpK}@p+ut z-H63{6|pn0`_1oe_JiyXV~qQ=xX+lw7_*@va#Yp`y4QxbOfn+JF`{`fi82|1{bpi+ z9q`*NR&FXMvx8r#bjCf6)Dr{5z$ya`Z8~`VpTRG)_L09@ zLKZPV4E!?&xHSq!J}k

2c1XRUyC2Mq=DDpWu~U%3Q;f%`~D1$A7Y4tbuzN+XVf SepL=g7Xd{Gb;Q6gFz^9O+Ddu= literal 0 HcmV?d00001 diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberKEMProvider.java diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberKEMSpi.java diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberParametersSpi.java diff --git a/api/src/main/java/org/xxdc/oss/example/security/KyberParams.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberParams.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/security/KyberParams.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/security/KyberParams.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/DuplexMessageHandler.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/MessageHandler.java diff --git a/api/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java b/tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java similarity index 100% rename from api/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java rename to tcp-gameserver/src/main/java/org/xxdc/oss/example/transport/SecureDuplexMessageHandler.java diff --git a/tcp-gameserver/src/main/resources/logback.xml b/tcp-gameserver/src/main/resources/logback.xml new file mode 100644 index 0000000..914c8c7 --- /dev/null +++ b/tcp-gameserver/src/main/resources/logback.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + %boldWhite(%date) %yellow([%thread]) %highlight(%-5level) %cyan(%-20logger{20}) - %msg %n + + + ${log.level:-INFO} + + + + + tic-tac-toe_tcp-gameserver.log.json + + tic-tac-toe.log.json.%d{yyyy-MM-dd}.log + 30 + 100MB + + + true + false + false + false + false + + + INFO + + + + + + + + \ No newline at end of file diff --git a/tcp-gameserver/src/test/.DS_Store b/tcp-gameserver/src/test/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ec9ff6d544dfbc1270882e235586365492fd3656 GIT binary patch literal 6148 zcmeHKOHKnZ47H()LEUu8GFRvg!e}@_FHp5bu;?fisQVm(b8rmK#q$R=qh^7Ggb=c& za7pElm8)S`~ zsn8_<^=4nj(27-Yf zGk`l=q+1$B9}ENo!N3;-ay}$9!R%NK_2@vSB>+&K(JIiTmJpxhm>sJjRv>JyKyzg; zG1%NOpWH4xRzq_q_Tqzm=g;DWb$7&1YEGODqYnmxfjI-$HeAX5e~(|LS>(@MViXJn z1OJQxUJRR|$4B|ydhvO3*Cw + + + + + + + + + %boldWhite(%date) %yellow([%thread]) %highlight(%-5level) %cyan(%-20logger{20}) - %msg %n + + + ${log.level:-DEBUG} + + + + + + + \ No newline at end of file From 1ec24926110bd5514c808f8b8169b1a4161c3027 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 22:35:35 -0400 Subject: [PATCH 18/20] refactor: refactor the security and messaging layer into tcp-gameserver --- api/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 7aa6ce1..e0b6247 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -18,7 +18,7 @@ val libSuffix = when { dependencies { // Native Library (Rust) implementation(project(":native")) - testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.0.0") + testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.1.0") // JDK9: Platform Logging (Third-Party) From 562880c7ec2809fb0adb531a924924ee3dbc23c6 Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 22:53:25 -0400 Subject: [PATCH 19/20] build: publish to maven local --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c9121fe..3ecf0d6 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,7 +35,7 @@ jobs: uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Build with Gradle Wrapper - run: ./gradlew build + run: ./gradlew build publishToMavenLocal # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. From 4f3416b3ac1b72c766be74ef63c546373e5ae39e Mon Sep 17 00:00:00 2001 From: Brian Corbin Date: Sat, 31 Aug 2024 23:13:22 -0400 Subject: [PATCH 20/20] build: publish to maven local --- api/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index e0b6247..95698d6 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -18,8 +18,7 @@ val libSuffix = when { dependencies { // Native Library (Rust) implementation(project(":native")) - testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.1.0") - + //testRuntimeOnly("org.xxdc.oss.example:tictactoe-native-$libSuffix:1.1.0") // JDK9: Platform Logging (Third-Party) // -> JDK API -> SLF4J -> Logback