diff --git a/.github/pr_assignment_config.yml b/.github/pr_assignment_config.yml new file mode 100644 index 0000000..3f33fd9 --- /dev/null +++ b/.github/pr_assignment_config.yml @@ -0,0 +1,4 @@ +addReviewers: true +reviewers: + - Aliorpse +numberOfReviewers: 1 diff --git a/.github/workflows/pr_assignment.yml b/.github/workflows/pr_assignment.yml new file mode 100644 index 0000000..680a899 --- /dev/null +++ b/.github/workflows/pr_assignment.yml @@ -0,0 +1,12 @@ +name: PR Assignment +on: + pull_request: + types: [opened, ready_for_review] + +jobs: + add-reviews: + runs-on: ubuntu-latest + steps: + - uses: kentaro-m/auto-assign-action@v2.0.1 + with: + configuration-path: ".github/pr_assignment_config.yml" diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml index a133f8b..981ff83 100644 --- a/.github/workflows/pr_validation.yml +++ b/.github/workflows/pr_validation.yml @@ -1,18 +1,41 @@ -name: PR Validation +name: PR ABI Validation on: - pull_request: - branches: [ main ] + pull_request_review: + types: [submitted] jobs: validate: + if: github.event.review.state == 'approved' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - - name: Grant execute permission to gradlew - run: chmod +x ./gradlew + - name: Check Review Status + uses: actions/github-script@v7 + with: + script: | + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + const latestReviews = {}; + reviews.forEach(r => { + latestReviews[r.user.login] = r.state; + }); + + const states = Object.values(latestReviews); + const allApproved = states.every(state => state === 'APPROVED') && states.length > 0; + + if (!allApproved) { + console.log("Skipping ABI validation because not all reviewers approved."); + process.exit(0); + } - name: Set up JDK uses: actions/setup-java@v4 @@ -21,5 +44,8 @@ jobs: java-version: 21 cache: 'gradle' + - name: Grant execute permission + run: chmod +x ./gradlew + - name: ABI Validation run: ./gradlew checkLegacyAbi -Pfull-build=true --no-configuration-cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0fb94b..24ec430 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,12 +4,10 @@ Welcome to contribute to this project! To ensure an effective collaboration, ple ## Pull Requests -- Open an issue to discuss changes before coding. -- Fork the repository and create a new branch named like "feat/my-feature". -- Write your codes and tests in the new branch. -- Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html). -- add `Pfull-build=true` to abi-validating related tasks. -- run `./gradlew checkLegacyAbi` to check your changes. If including a breaking change, run `./gradlew updateLegacyAbi`. -- Use concise, descriptive PR titles with prefixes like `feat:` or `fix:`. -- Link PRs to issues with `Closes #xx`. -- Create a pull request to the `main` branch. \ No newline at end of file +1. Open an issue to discuss changes. +2. Fork the repository and create a new branch named like `feat/my-feature`, code in it. +3. add `Pfull-build=true` to abi-validating related tasks, for full build is disabled to boost dev build speed. +4. run `./gradlew checkLegacyAbi` to check your changes. If including a breaking change, run `./gradlew updateLegacyAbi`. +5. When creating PR, use a concise, descriptive PR title with prefixes like `feat:` or `fix:`.Please open the PR against the `main` branch. + + diff --git a/README.md b/README.md index 81cfeee..90acb7b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,25 @@ # mcutils [![CodeFactor](https://www.codefactor.io/repository/github/aliorpse/mcutils/badge)](https://www.codefactor.io/repository/github/aliorpse/mcutils) -[![Maven Central](https://maven-badges.sml.io/sonatype-central/tech.aliorpse.mcutils/mcutils-shared/badge.svg)](https://central.sonatype.com/artifact/tech.aliorpse.mcutils/mcutils-shared) +![Maven Central](https://maven-badges.sml.io/sonatype-central/tech.aliorpse.mcutils/mcutils-shared/badge.svg) [![View on DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Aliorpse/mcutils) -A Kotlin multiplatform library provides utility functions for Minecraft-related queries. +mcutils is a lightweight **Kotlin Multiplatform** library for Minecraft Java related queries, such as server +ping/management, fetching player profile, etc. + +## Supported platforms + +* **Kotlin/JVM** +* **Kotlin/JS, WasmJS** on + * nodejs + * browser *(except modules that depend on ktor-network, see [build.gradle.kts](build.gradle.kts) for more details)* +* **Kotlin/Native** on + * linuxX64 (X64/Arm64) + * mingw (X64) + * ios (X64/Arm64) + * iosSimulator (Arm64) + * macos (X64/Arm64) + * androidNative (X64/Arm64) ## Modules @@ -13,4 +28,9 @@ A Kotlin multiplatform library provides utility functions for Minecraft-related - [Remote Console (RCON)](mcutils-rcon/README.md): Execute commands remotely on the server. - [Player Profile](mcutils-player/README.md): Retrieve player UUIDs and profiles. -Check out the project's [dokka](https://aliorpse.github.io/mcutils/) for the full API reference. +Click on the links for module-specific documents, or check out the project's [dokka](https://aliorpse.github.io/mcutils/) for the full API reference. + + +## Contributing + +Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more details. diff --git a/mcutils-msmp/README.md b/mcutils-msmp/README.md index 94cecc2..a7e2afe 100644 --- a/mcutils-msmp/README.md +++ b/mcutils-msmp/README.md @@ -1,6 +1,6 @@ # Minecraft Server Management Protocol (MSMP) -`tech.aliorpse.mcutils:mcutils-msmp:$version` +`tech.aliorpse.mcutils:mcutils-msmp` > [!tip] > This module requires a Ktor client engine (e.g., `ktor-client-cio`). @@ -29,7 +29,8 @@ client.use { client -> val players = client.players.get() println("Online players: ${players.size}") - // Await client to be closed (e.g., server stopping or manual close) + // Wait for the client to close (e.g., server stopping or manual close) + // You could use `coroutineScope { ... }` for the same effect, but you know it will add an indentation level. client.await() } ``` @@ -76,7 +77,7 @@ client.on { val event = client.awaitEvent() ``` -You don't need to re-register listeners after reconnection. eventFlow will be empty when reconnecting. +Note: Event subscriptions persist across reconnections, but no events are buffered during the disconnected period. ### Lifecycle diff --git a/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/MsmpLifecycleManager.kt b/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/MsmpLifecycleManager.kt index 78eea25..2f74734 100644 --- a/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/MsmpLifecycleManager.kt +++ b/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/MsmpLifecycleManager.kt @@ -30,6 +30,7 @@ import tech.aliorpse.mcutils.api.MsmpState import tech.aliorpse.mcutils.entity.ServerStoppingEvent import tech.aliorpse.mcutils.internal.util.DispatchersIO import tech.aliorpse.mcutils.internal.util.WebSocketClientProvider.webSocketClient +import tech.aliorpse.mcutils.internal.util.isBrowser import kotlin.math.min import kotlin.math.pow import kotlin.random.Random @@ -143,7 +144,11 @@ internal class MsmpLifecycleManager( */ private suspend fun connectSession(): Boolean = coroutineScope { val session = webSocketClient.webSocketSession(target) { - header(HttpHeaders.Authorization, "Bearer $token") + if (isBrowser) { + header(HttpHeaders.SecWebSocketProtocol, "minecraft-v1,$token") + } else { + header(HttpHeaders.Authorization, "Bearer $token") + } timeout { connectTimeoutMillis = config.connectTimeout } } diff --git a/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.kt b/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.kt new file mode 100644 index 0000000..75c6079 --- /dev/null +++ b/mcutils-msmp/src/commonMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.kt @@ -0,0 +1,3 @@ +package tech.aliorpse.mcutils.internal.util + +internal expect val isBrowser: Boolean diff --git a/mcutils-msmp/src/jsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.js.kt b/mcutils-msmp/src/jsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.js.kt new file mode 100644 index 0000000..1ab68b1 --- /dev/null +++ b/mcutils-msmp/src/jsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.js.kt @@ -0,0 +1,4 @@ +package tech.aliorpse.mcutils.internal.util + +internal actual val isBrowser: Boolean = + js("typeof window !== 'undefined' && typeof window.document !== 'undefined'") diff --git a/mcutils-msmp/src/jvmMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.jvm.kt b/mcutils-msmp/src/jvmMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.jvm.kt new file mode 100644 index 0000000..db0f9f7 --- /dev/null +++ b/mcutils-msmp/src/jvmMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.jvm.kt @@ -0,0 +1,3 @@ +package tech.aliorpse.mcutils.internal.util + +internal actual val isBrowser: Boolean = false diff --git a/mcutils-msmp/src/nativeMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.native.kt b/mcutils-msmp/src/nativeMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.native.kt new file mode 100644 index 0000000..db0f9f7 --- /dev/null +++ b/mcutils-msmp/src/nativeMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.native.kt @@ -0,0 +1,3 @@ +package tech.aliorpse.mcutils.internal.util + +internal actual val isBrowser: Boolean = false diff --git a/mcutils-msmp/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.wasmJs.kt b/mcutils-msmp/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.wasmJs.kt new file mode 100644 index 0000000..d9b153f --- /dev/null +++ b/mcutils-msmp/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/Platform.wasmJs.kt @@ -0,0 +1,5 @@ +package tech.aliorpse.mcutils.internal.util + +@OptIn(ExperimentalWasmJsInterop::class) +internal actual val isBrowser: Boolean = + js("typeof window !== 'undefined' && typeof window.document !== 'undefined'") diff --git a/mcutils-player/README.md b/mcutils-player/README.md index bde69aa..3210bbc 100644 --- a/mcutils-player/README.md +++ b/mcutils-player/README.md @@ -1,6 +1,6 @@ # Player Profile -`tech.aliorpse.mcutils:mcutils-player:$version` +`tech.aliorpse.mcutils:mcutils-player` > [!tip] > This module requires a Ktor client engine (e.g., `ktor-client-cio`). diff --git a/mcutils-rcon/README.md b/mcutils-rcon/README.md index 3ce7597..7859b0c 100644 --- a/mcutils-rcon/README.md +++ b/mcutils-rcon/README.md @@ -1,6 +1,6 @@ # Remote Console (RCON) -`tech.aliorpse.mcutils:mcutils-rcon:$version` +`tech.aliorpse.mcutils:mcutils-rcon` ## Common Usage diff --git a/mcutils-server-status/README.md b/mcutils-server-status/README.md index 18f691e..6a9e7d7 100644 --- a/mcutils-server-status/README.md +++ b/mcutils-server-status/README.md @@ -1,6 +1,6 @@ # Server Status -`tech.aliorpse.mcutils:mcutils-server-status:$version` +`tech.aliorpse.mcutils:mcutils-server-status` > [!warning] > Native targets do not yet support an SRV record implementation. Setting `enableSrv = true` will not have any effect on these platforms. diff --git a/mcutils-util/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/CoroutinesUtil.wasm.kt b/mcutils-util/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/CoroutinesUtil.wasmJs.kt similarity index 100% rename from mcutils-util/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/CoroutinesUtil.wasm.kt rename to mcutils-util/src/wasmJsMain/kotlin/tech/aliorpse/mcutils/internal/util/CoroutinesUtil.wasmJs.kt