Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Summary

Describe the problem and the approach taken.

## Validation

- [ ] `./gradlew clean build` passes on Java 21.
- [ ] New or changed behavior has automated tests where practical.
- [ ] Paper API access remains on the server thread.
- [ ] New queues, scans, or I/O paths have explicit bounds.
- [ ] Commands and state-changing actions enforce permissions.
- [ ] Configuration changes are documented and validated.
- [ ] User-facing changes are reflected in the changelog or wiki.

## Manual testing

List the Paper version, commands exercised, and observed result. Include only
sanitized logs or exports; remove server addresses, player data, world names,
and sensitive coordinates.

## Compatibility

State whether this changes the supported Paper, Minecraft, or Java versions.
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ jobs:
fi
./gradlew clean build --no-daemon --warning-mode all

- name: Publish to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
MODRINTH_PROJECT_ID: ${{ vars.MODRINTH_PROJECT_ID }}
run: ./gradlew modrinth --no-daemon --warning-mode all

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/wiki.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Publish Wiki

on:
push:
branches: [main]
paths:
- "wiki/**"
- ".github/workflows/wiki.yml"
workflow_dispatch:

permissions:
contents: write

concurrency:
group: publish-wiki
cancel-in-progress: false

jobs:
publish:
name: Sync wiki pages
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Publish wiki
env:
GITHUB_TOKEN: ${{ github.token }}
WIKI_REPOSITORY: ${{ github.repository }}.wiki
shell: bash
run: |
set -euo pipefail

wiki_url="https://x-access-token:${GITHUB_TOKEN}@github.com/${WIKI_REPOSITORY}.git"
if ! git ls-remote "$wiki_url" >/dev/null 2>&1; then
echo "::error::Initialize the GitHub Wiki by creating its first Home page, then rerun this workflow."
exit 1
fi

wiki_checkout="$RUNNER_TEMP/chunkdoctor-wiki"
git clone --depth 1 "$wiki_url" "$wiki_checkout"
rsync -a --delete --exclude ".git/" wiki/ "$wiki_checkout/"

cd "$wiki_checkout"
wiki_branch="$(git branch --show-current)"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add --all

if git diff --cached --quiet; then
echo "Wiki is already up to date."
exit 0
fi

git commit -m "docs: sync project wiki"
git push origin "HEAD:$wiki_branch"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to ChunkDoctor are documented here. The project follows
- Interactive Adventure and MiniMessage command interface.
- Administrator notifications with cooldown and persistent opt-out.
- Safe asynchronous JSON report exports.
- Anonymous, globally opt-out bStats usage metrics.
- Unit tests and checksum-pinned Gradle builds.

[Unreleased]: https://github.com/denfry/ChunkDoctor/compare/v1.0.0...HEAD
Expand Down
36 changes: 35 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<p align="center">
<a href="https://github.com/denfry/ChunkDoctor/actions/workflows/build.yml"><img alt="Build" src="https://github.com/denfry/ChunkDoctor/actions/workflows/build.yml/badge.svg"></a>
<a href="https://github.com/denfry/ChunkDoctor/releases"><img alt="Release" src="https://img.shields.io/github/v/release/denfry/ChunkDoctor?display_name=tag&sort=semver"></a>
<a href="https://bstats.org/plugin/bukkit/ChunkDoctor/32969"><img alt="Servers using ChunkDoctor on bStats" src="https://img.shields.io/bstats/servers/32969?label=bStats%20servers"></a>
<a href="https://github.com/denfry/ChunkDoctor/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/denfry/ChunkDoctor"></a>
<img alt="Paper 1.21.8" src="https://img.shields.io/badge/Paper-1.21.8-2f3136?logo=paper">
<img alt="Java 21" src="https://img.shields.io/badge/Java-21-e76f00?logo=openjdk&logoColor=white">
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/License-MIT-22c55e.svg"></a>
Expand All @@ -21,6 +23,8 @@
<a href="#installation">Installation</a> ·
<a href="#commands">Commands</a> ·
<a href="#configuration">Configuration</a> ·
<a href="https://github.com/denfry/ChunkDoctor/wiki">Wiki</a> ·
<a href="#gallery">Gallery</a> ·
<a href="docs/README_RU.md">Русская документация</a>
</p>

Expand Down Expand Up @@ -67,6 +71,16 @@ to profile execution time.
- **Permission-gated notifications** with cooldowns and persistent player opt-out.
- **No NMS, reflection, database, or mandatory plugin dependencies.**

## Gallery

| Explainable analysis | Highest-risk chunks | Bounded deep scan |
|---|---|---|
| [![ChunkDoctor explains an 84 out of 100 CRITICAL chunk risk score](docs/assets/gallery/chunkdoctor-risk-analysis.png)](docs/assets/gallery/chunkdoctor-risk-analysis.png) | [![ChunkDoctor ranks loaded chunks by risk](docs/assets/gallery/chunkdoctor-risk-ranking.png)](docs/assets/gallery/chunkdoctor-risk-ranking.png) | [![ChunkDoctor deep scan progresses within block and time budgets](docs/assets/gallery/chunkdoctor-deep-scan.png)](docs/assets/gallery/chunkdoctor-deep-scan.png) |

These are representative gameplay mockups based on the real commands, default
budgets, and output model. They are not captures from a particular production
server.

## Performance model

ChunkDoctor follows one rule: a lag diagnostic must not become the source of lag.
Expand Down Expand Up @@ -180,7 +194,19 @@ disabling the plugin.
The complete scoring weights, world filters, notification settings, message
templates, JSON schema, operational guidance, and troubleshooting are documented
in the [Russian reference](docs/README_RU.md). An English configuration reference
will expand as the public API stabilizes.
is available in the [project wiki](https://github.com/denfry/ChunkDoctor/wiki).

## Documentation

| Guide | Contents |
|---|---|
| [Installation](https://github.com/denfry/ChunkDoctor/wiki/Installation) | Requirements, upgrade procedure, first-run checks |
| [Commands and permissions](https://github.com/denfry/ChunkDoctor/wiki/Commands-and-Permissions) | Complete command and permission reference |
| [Configuration](https://github.com/denfry/ChunkDoctor/wiki/Configuration) | Monitoring, scanning, worlds, notifications, exports |
| [Risk scoring](https://github.com/denfry/ChunkDoctor/wiki/Risk-Scoring) | Score inputs, nonlinear penalties, levels, confidence |
| [Performance and safety](https://github.com/denfry/ChunkDoctor/wiki/Performance-and-Safety) | Thread boundaries, budgets, overload behavior |
| [Troubleshooting](https://github.com/denfry/ChunkDoctor/wiki/Troubleshooting) | Common symptoms and diagnostic steps |
| [Building and releasing](https://github.com/denfry/ChunkDoctor/wiki/Building-and-Releasing) | Java 21 build, tags, Modrinth and GitHub releases |

## Building from source

Expand Down Expand Up @@ -222,6 +248,14 @@ Please do not publish exploitable issues, path escapes, permission bypasses, or
denial-of-service findings in a public issue. Follow [SECURITY.md](SECURITY.md)
for private reporting.

## Anonymous metrics

ChunkDoctor uses [bStats](https://bstats.org/plugin/bukkit/ChunkDoctor/32969) to
collect anonymous, aggregated usage statistics. It does not collect player
names, chat, world data, coordinates, scan results, configuration values, or
report contents. Server owners can disable metrics globally in
`plugins/bStats/config.yml`.

## License

ChunkDoctor is available under the [MIT License](LICENSE).
Expand Down
43 changes: 41 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
plugins {
java
id("com.gradleup.shadow") version "8.3.10"
id("com.modrinth.minotaur") version "2.9.0"
}

group = "dev.chunkdoctor"
version = "1.0.0"
val pluginVersionForResources = version.toString()
val bStatsPluginId = providers.gradleProperty("bstats_plugin_id").orElse("0")

fun releaseNotesFor(version: String): String {
val changelog = rootProject.file("CHANGELOG.md").readText()
val versionHeader = Regex("""(?m)^## \[${Regex.escape(version)}]\s+-\s+.+$""")
val match = versionHeader.find(changelog)
?: throw GradleException("CHANGELOG.md has no release section for version $version")
val nextHeader = Regex("""(?m)^## \[""").find(changelog, match.range.last + 1)
return changelog
.substring(match.range.last + 1, nextHeader?.range?.first ?: changelog.length)
.trim()
.ifEmpty {
throw GradleException("CHANGELOG.md release section for version $version is empty")
}
}

repositories {
mavenCentral()
Expand All @@ -15,6 +31,7 @@ repositories {
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.8-R0.1-SNAPSHOT")
implementation("com.google.code.gson:gson:2.13.1")
implementation("org.bstats:bstats-bukkit:3.2.1")

testImplementation("org.junit.jupiter:junit-jupiter:5.13.4")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand All @@ -26,8 +43,11 @@ java {

tasks {
processResources {
filesMatching("plugin.yml") {
expand("version" to pluginVersionForResources)
filesMatching(listOf("plugin.yml", "bstats.properties")) {
expand(
"version" to pluginVersionForResources,
"bstatsPluginId" to bStatsPluginId.get()
)
}
}

Expand All @@ -38,6 +58,7 @@ tasks {
shadowJar {
archiveClassifier.set("")
relocate("com.google.gson", "dev.chunkdoctor.lib.gson")
relocate("org.bstats", "dev.chunkdoctor.lib.bstats")
}

jar {
Expand All @@ -48,3 +69,21 @@ tasks {
dependsOn(shadowJar)
}
}

modrinth {
token.set(providers.environmentVariable("MODRINTH_TOKEN"))
projectId.set(providers.environmentVariable("MODRINTH_PROJECT_ID"))
versionNumber.set(project.version.toString())
versionName.set("ChunkDoctor ${project.version}")
versionType.set(
if (project.version.toString().contains(Regex("""(?i)(alpha|beta|rc)"""))) {
"beta"
} else {
"release"
}
)
uploadFile.set(tasks.shadowJar)
gameVersions.add("1.21.8")
loaders.add("paper")
changelog.set(providers.provider { releaseNotesFor(project.version.toString()) })
}
Loading