diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..3576d9a54 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,47 @@ +name: Acecore build verification +on: + push: + branches: [v3.0, 'codex/**'] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-24.04 + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + platform: [core, spigot] + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + with: + persist-credentials: false + - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '21' + - name: Test shared core + if: matrix.platform == 'core' + run: bash gradlew -PdynmapPlatform=core :DynmapCore:test --no-daemon --console=plain + - name: Build and inspect Spigot jar + if: matrix.platform == 'spigot' + run: bash gradlew -PdynmapPlatform=spigot verifySpigotJar --no-daemon --console=plain + - name: Preserve verification reports (no plugin binaries) + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: verification-${{ matrix.platform }}-${{ github.sha }} + retention-days: 14 + if-no-files-found: warn + path: | + DynmapCore/build/reports/tests/ + DynmapCore/build/test-results/ + build/reports/acecore/ diff --git a/.github/workflows/spellcheck.yaml b/.github/workflows/spellcheck.yaml deleted file mode 100644 index d4facf8a6..000000000 --- a/.github/workflows/spellcheck.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: Checking for spelling errors - -on: - push: - -jobs: - spellcheck: - name: rojopolis/spellcheck - runs-on: ubuntu-20.04 - steps: - - name: actions/checkout - uses: actions/checkout@v2 - - name: rojopolis/spellcheck - actually doing something - uses: rojopolis/spellcheck-github-actions@0.29.0 diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java index bd0e12d5b..46dcd1bec 100644 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java @@ -711,9 +711,9 @@ public void playerEvent(DynmapPlayer p) { /* Print version info */ Log.info("version " + plugin_ver + " is enabled - core version " + version ); - Log.info("For support, visit our Discord at https://discord.gg/s3rd5qn"); + Log.info("Unofficial Acecore build; support: https://github.com/acecore-systems/dynmap/issues"); Log.info("For news, visit https://reddit.com/r/Dynmap or follow https://universeodon.com/@dynmap"); - Log.info("To report or track bugs, visit https://github.com/webbukkit/dynmap/issues"); + Log.info("Upstream Dynmap: https://github.com/webbukkit/dynmap (do not report fork-only bugs upstream)"); Log.info("If you'd like to donate, please visit https://www.patreon.com/dynmap or https://ko-fi.com/michaelprimm"); events.trigger("initialized", null); diff --git a/README.md b/README.md index 640ac88b5..77475aec6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +> **Unofficial Acecore fork.** Support: https://github.com/acecore-systems/dynmap/issues +> Build and maintenance: [docs/acecore-maintenance.md](docs/acecore-maintenance.md). +> Not supported or endorsed by the Dynmap team. Original upstream documentation follows. + # Dynmap® - dynamic web maps for Minecraft servers # Jump around the README diff --git a/build.gradle b/build.gradle index 662a53493..e6a54132e 100644 --- a/build.gradle +++ b/build.gradle @@ -28,12 +28,12 @@ eclipse { allprojects { repositories { - mavenLocal() + if (providers.gradleProperty('useMavenLocal').isPresent()) { mavenLocal() } + mavenCentral() + maven { url "https://repo.mikeprimm.com" } maven { url 'https://libraries.minecraft.net/' } maven { url "https://oss.sonatype.org/content/repositories/releases" } maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://repo.mikeprimm.com" } - maven { url "https://repo.maven.apache.org/maven2" } maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } maven { url "https://repo.codemc.org/repository/maven-public/" } } @@ -46,10 +46,19 @@ allprojects { } class Globals { - String buildNumber = System.getenv().BUILD_NUMBER ?: "Dev" + String buildNumber +} +def gitOutput = { List arguments -> + def output = new ByteArrayOutputStream() + exec { commandLine(['git'] + arguments); standardOutput = output } + output.toString('UTF-8').trim() } +def sourceRevision = gitOutput(['rev-parse', 'HEAD']) +def sourceDirty = !gitOutput(['status', '--porcelain', '--untracked-files=normal']).isEmpty() ext { - globals = new Globals() + acecoreRevision = sourceRevision + acecoreDirty = sourceDirty + globals = new Globals(buildNumber: 'acecore-' + sourceRevision.take(12) + (sourceDirty ? '-dirty' : '')) } subprojects { @@ -62,8 +71,18 @@ subprojects { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } + tasks.withType(ProcessResources).configureEach { + inputs.property('acecoreBuildNumber', rootProject.ext.globals.buildNumber) + inputs.property('dynmapVersion', project.version.toString()) + } + tasks.withType(AbstractArchiveTask).configureEach { + preserveFileTimestamps = false + reproducibleFileOrder = true + } } +apply from: 'gradle/acecore-verification.gradle' + clean { delete "target" } diff --git a/docs/acecore-maintenance.md b/docs/acecore-maintenance.md new file mode 100644 index 000000000..befc42a97 --- /dev/null +++ b/docs/acecore-maintenance.md @@ -0,0 +1,74 @@ +# Acecore fork のビルドと検証 + +このforkは非公式で、Dynmapチームによるサポート・推奨を受けていません。 +不具合の窓口は [Acecore Issues](https://github.com/acecore-systems/dynmap/issues) です。 +元コードは [webbukkit/dynmap](https://github.com/webbukkit/dynmap) で、著作権表示、 +Apache-2.0 の LICENSE、上流 README のカスタムビルド・商標・配布方針を維持します。 +現段階は特定サーバーでの検証用ソースとCI整備です。CIからJARを一般配布しません。 +公開配布は上流READMEの条件と対応状況を改めて確認する別の判断です。 + +## ビルド + +Git、JDK 21、Gradle wrapper 8.14 を使います。Windowsでは `bash gradlew` を +`gradlew.bat` に読み替えてください。 + +```sh +bash gradlew -PdynmapPlatform=core :DynmapCore:test --no-daemon +bash gradlew -PdynmapPlatform=spigot verifySpigotJar --no-daemon +``` + +プロファイル省略時は上流と同じ全モジュールを構成します。`core` は共有コア/API、 +`spigot` はそれらと既存の全Bukkit helper/API/Spigotを構成します。 +異なるローダーの初期化を省く仕組みであり、Forge/Fabricの検証成功を意味しません。 +共有コアを上流に提案するときは、上流READMEどおり全対象プラットフォームの検証が必要です。 +Minecraft 26.2 / Java 25 の候補対応は Issue #2 で別に管理します。 + +`mavenLocal()` は明示的な `-PuseMavenLocal` のときだけ有効です。CIは使いません。 +既存の Maven Central / Minecraft / MikePrimm / Spigot / CodeMC / Sonatype / JitPack +への参照を維持し、不要なリポジトリ巡回を減らすためCentralとMikePrimmを先にします。 +既存依存のバージョンは変更しません。SNAPSHOTや動的指定は残っているため、 +同じソースでも将来同じ依存が取得できる保証はありません。 +`build/reports/acecore/build.json` は実際に解決した依存の座標とSHA-256を記録します。 +これは監査記録であり、依存ロックや脆弱性評価の代替ではありません。 + +版番号は `3.9-SNAPSHOT-acecore-[-dirty]` です。 +Git管理ファイルまたは未追跡ソースの変更がある場合はdirtyを付けます。 +`BUILD_NUMBER` による上書きはしません。版番号をリソース処理の入力にして、 +コミット後の再ビルドでも plugin.yml / core.yml が更新されるようにしています。 +アーカイブ内の日時と順序を固定しますが、依存を含む完全な再現性は未保証です。 + +`verifySpigotJar` はコアテスト、JAR生成、Jetty/Servlet/S3/bStats/ブラウザ資材の存在、 +版番号の整合を検査し、SHA256SUMS.txtとbuild.jsonを出力します。 +GitHub Actions は固定SHAのアクション、contents:read、認証情報を残さないcheckoutを使い、 +テスト結果と監査記録だけを14日保存します。秘密値・実サーバー設定を必要としません。 +この構成は自動デプロイ、Release作成、Maven公開を行いません。 + +## 変更と同期 + +default branch `v3.0` を安定基点とし、Issueごとの `codex/*` branch/worktreeで作業します。 +上流の追従候補はコミットSHAを固定して差分・依存・ライセンスをレビューし、 +必要なソースだけを別PRに取り込みます。上流の生成済みJARは取り込みません。 +CI成功後もdraft PRで差分と検証範囲を確認し、マージと本番反映を区別します。 + +| Issue | 独自差分 | 管理方法 | +| --- | --- | --- | +| #1 | R2の境界スナップショットと同一位置マーカーの更新抑制 | 独立PR、回帰テスト・隔離サーバー比較 | +| #2 | Minecraft 26.2候補、helper・モデル・Java/Gradle互換性 | 上流SHAと採否・既知制限を記録する独立PR | +| #3 | ビルド対象選択、CI、版識別、同梱検査、非公式窓口 | この文書とワークフロー | + +## 段階的な稼働検証 + +1. 読み取り専用で稼働版、Java、設定、拡張プラグイン、エラーを確認し検証先を選ぶ。 +2. 同じPaper版の隔離サーバーに新規ワールドを作り、既存地図・R2接続を持ち込まず検証する。 +3. 起動、初期レンダー、実ブロック更新、ズーム、境界・スポーン・連携マーカー、停止を確認する。 +4. 候補のコミット・版・SHA-256と検証証拠を揃え、本番反映の承認を得る。 +5. 現行JAR・設定・連携プラグイン・必要データをバックアップし、復旧可能性を確かめる。 + 自動更新が候補JARを上書きしないことも実環境で確認する。 +6. 選定した1台だけに反映し、予定された安全な停止/起動で有効化する。 + 既存タイル・ワールド・R2オブジェクトを削除しない。 +7. エラー、地図更新、利用者影響、R2 originの変更を観測する。 + キャッシュされたHTTP応答やLast-ModifiedだけからPUT件数・料金を断定しない。 +8. 起動失敗、描画欠落、連携不具合があれば候補を外し、バックアップJAR/設定へ戻す。 + ロールバック時も停止/起動の時機を確認し、地図を一括再生成しない。 + +ホスト名、内部パス、認証情報、実プレイヤー情報は公開PR・CI artifactに記載しません。 diff --git a/gradle/acecore-verification.gradle b/gradle/acecore-verification.gradle new file mode 100644 index 000000000..a31d34cd7 --- /dev/null +++ b/gradle/acecore-verification.gradle @@ -0,0 +1,69 @@ +import groovy.json.JsonOutput +import java.security.MessageDigest +import java.util.zip.ZipFile + +def sha256 = { File file -> + def digest = MessageDigest.getInstance('SHA-256') + file.withInputStream { stream -> + byte[] buffer = new byte[65536] + int count + while ((count = stream.read(buffer)) != -1) { digest.update(buffer, 0, count) } + } + digest.digest().encodeHex().toString() +} + +if (findProject(':spigot') != null) { + tasks.register('verifySpigotJar') { + dependsOn ':spigot:shadowJar', ':DynmapCore:test' + doLast { + File jar = project(':spigot').tasks.named('shadowJar').get().archiveFile.get().asFile + String expectedVersion = "${project.version}-${globals.buildNumber}" + new ZipFile(jar).withCloseable { zip -> + [ + 'org/dynmap/bukkit/DynmapPlugin.class', + 'org/dynmap/DynmapCore.class', + 'org/dynmap/jetty/server/Server.class', + 'org/dynmap/jetty/servlet/ServletContextHandler.class', + 'org/dynmap/javax/servlet/Servlet.class', + 'org/dynmap/bstats/bukkit/Metrics.class', + 'org/dynmap/s3lite/api/client/S3Client.class', + 'extracted/web/js/map.js' + ].each { name -> + if (zip.getEntry(name) == null) { throw new GradleException("Missing runtime entry: ${name}") } + } + ['plugin.yml', 'core.yml'].each { name -> + def entry = zip.getEntry(name) + if (entry == null || !zip.getInputStream(entry).getText('UTF-8').contains('version: "' + expectedVersion + '"')) { + throw new GradleException("Unexpected ${name} version; expected ${expectedVersion}") + } + } + } + def dependencies = [] + subprojects.sort { it.path }.each { p -> + ['compileClasspath', 'runtimeClasspath', 'testRuntimeClasspath'].each { name -> + def config = p.configurations.findByName(name) + if (config != null && config.canBeResolved) { + config.resolvedConfiguration.resolvedArtifacts.each { artifact -> + if (artifact.id.componentIdentifier instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier) { + dependencies << [module: artifact.moduleVersion.id.toString(), file: artifact.file.name, + sha256: sha256(artifact.file)] + } + } + } + } + } + File reportDir = layout.buildDirectory.dir('reports/acecore').get().asFile + reportDir.mkdirs() + def checksum = sha256(jar) + new File(reportDir, 'SHA256SUMS.txt').text = "${checksum} ${jar.name}\n" + new File(reportDir, 'build.json').text = JsonOutput.prettyPrint(JsonOutput.toJson([ + revision: acecoreRevision, dirty: acecoreDirty, version: expectedVersion, + platform: providers.gradleProperty('dynmapPlatform').getOrElse('all'), + gradle: gradle.gradleVersion, java: System.getProperty('java.version'), + jar: jar.name, sha256: checksum, + dependencies: dependencies.unique().sort { a, b -> (a.module + a.file) <=> (b.module + b.file) } + ])) + '\n' + logger.lifecycle("Verified ${jar.name}: ${expectedVersion}; SHA-256 ${checksum}") + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca025c83a..7e2d0c99a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -5,3 +5,4 @@ networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionSha256Sum=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa diff --git a/settings.gradle b/settings.gradle index 3429534f0..2c538ca46 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,103 +8,63 @@ pluginManagement { rootProject.name = 'dynmap-common' -include ':spigot' -include ':bukkit-helper-113-2' -include ':bukkit-helper-114-1' -include ':bukkit-helper-115' -include ':bukkit-helper-116' -include ':bukkit-helper-116-2' -include ':bukkit-helper-116-3' -include ':bukkit-helper-116-4' -include ':bukkit-helper-117' -include ':bukkit-helper-118' -include ':bukkit-helper-118-2' -include ':bukkit-helper-119' -include ':bukkit-helper-119-3' -include ':bukkit-helper-119-4' -include ':bukkit-helper-120' -include ':bukkit-helper-120-2' -include ':bukkit-helper-120-4' -include ':bukkit-helper-120-5' -include ':bukkit-helper-121' -include ':bukkit-helper-121-3' -include ':bukkit-helper-121-4' -include ':bukkit-helper-121-5' -include ':bukkit-helper-121-6' -include ':bukkit-helper-121-10' -include ':bukkit-helper-121-11' -include ':bukkit-helper' -include ':dynmap-api' -include ':DynmapCore' -include ':DynmapCoreAPI' -include ':fabric-1.21.6' -include ':fabric-1.21.9-10' -include ':fabric-1.21.11' -include ':fabric-1.20.6' -include ':fabric-1.19.4' -include ':fabric-1.18.2' -include ':fabric-1.17.1' -include ':fabric-1.16.4' -include ':fabric-1.15.2' -include ':fabric-1.14.4' -include ':forge-1.21.11' -include ':forge-1.21.10' -include ':forge-1.21.6' -include ':forge-1.20.6' -include ':forge-1.19.3' -include ':forge-1.18.2' -include ':forge-1.17.1' -include ':forge-1.16.5' -include ':forge-1.15.2' -include ':forge-1.14.4' - -project(':spigot').projectDir = "$rootDir/spigot" as File -project(':bukkit-helper-113-2').projectDir = "$rootDir/bukkit-helper-113-2" as File -project(':bukkit-helper-114-1').projectDir = "$rootDir/bukkit-helper-114-1" as File -project(':bukkit-helper-115').projectDir = "$rootDir/bukkit-helper-115" as File -project(':bukkit-helper-116').projectDir = "$rootDir/bukkit-helper-116" as File -project(':bukkit-helper-116-2').projectDir = "$rootDir/bukkit-helper-116-2" as File -project(':bukkit-helper-116-3').projectDir = "$rootDir/bukkit-helper-116-3" as File -project(':bukkit-helper-116-4').projectDir = "$rootDir/bukkit-helper-116-4" as File -project(':bukkit-helper-117').projectDir = "$rootDir/bukkit-helper-117" as File -project(':bukkit-helper-118').projectDir = "$rootDir/bukkit-helper-118" as File -project(':bukkit-helper-118-2').projectDir = "$rootDir/bukkit-helper-118-2" as File -project(':bukkit-helper-119').projectDir = "$rootDir/bukkit-helper-119" as File -project(':bukkit-helper-119-3').projectDir = "$rootDir/bukkit-helper-119-3" as File -project(':bukkit-helper-119-4').projectDir = "$rootDir/bukkit-helper-119-4" as File -project(':bukkit-helper-120').projectDir = "$rootDir/bukkit-helper-120" as File -project(':bukkit-helper-120-2').projectDir = "$rootDir/bukkit-helper-120-2" as File -project(':bukkit-helper-120-4').projectDir = "$rootDir/bukkit-helper-120-4" as File -project(':bukkit-helper-120-5').projectDir = "$rootDir/bukkit-helper-120-5" as File -project(':bukkit-helper-121').projectDir = "$rootDir/bukkit-helper-121" as File -project(':bukkit-helper-121-3').projectDir = "$rootDir/bukkit-helper-121-3" as File -project(':bukkit-helper-121-4').projectDir = "$rootDir/bukkit-helper-121-4" as File -project(':bukkit-helper-121-5').projectDir = "$rootDir/bukkit-helper-121-5" as File -project(':bukkit-helper-121-6').projectDir = "$rootDir/bukkit-helper-121-6" as File -project(':bukkit-helper-121-10').projectDir = "$rootDir/bukkit-helper-121-10" as File -project(':bukkit-helper-121-11').projectDir = "$rootDir/bukkit-helper-121-11" as File -project(':bukkit-helper').projectDir = "$rootDir/bukkit-helper" as File -project(':dynmap-api').projectDir = "$rootDir/dynmap-api" as File -project(':DynmapCore').projectDir = "$rootDir/DynmapCore" as File -project(':DynmapCoreAPI').projectDir = "$rootDir/DynmapCoreAPI" as File -project(':fabric-1.21.6').projectDir = "$rootDir/fabric-1.21.6" as File -project(':fabric-1.21.9-10').projectDir = "$rootDir/fabric-1.21.9-10" as File -project(':fabric-1.21.11').projectDir = "$rootDir/fabric-1.21.11" as File -project(':fabric-1.20.6').projectDir = "$rootDir/fabric-1.20.6" as File -project(':fabric-1.19.4').projectDir = "$rootDir/fabric-1.19.4" as File -project(':fabric-1.18.2').projectDir = "$rootDir/fabric-1.18.2" as File -project(':fabric-1.17.1').projectDir = "$rootDir/fabric-1.17.1" as File -project(':fabric-1.16.4').projectDir = "$rootDir/fabric-1.16.4" as File -project(':fabric-1.15.2').projectDir = "$rootDir/fabric-1.15.2" as File -project(':fabric-1.14.4').projectDir = "$rootDir/fabric-1.14.4" as File -project(':forge-1.21.11').projectDir = "$rootDir/forge-1.21.11" as File -project(':forge-1.21.10').projectDir = "$rootDir/forge-1.21.10" as File -project(':forge-1.21.6').projectDir = "$rootDir/forge-1.21.6" as File -project(':forge-1.20.6').projectDir = "$rootDir/forge-1.20.6" as File -project(':forge-1.19.3').projectDir = "$rootDir/forge-1.19.3" as File -project(':forge-1.18.2').projectDir = "$rootDir/forge-1.18.2" as File -project(':forge-1.17.1').projectDir = "$rootDir/forge-1.17.1" as File -project(':forge-1.16.5').projectDir = "$rootDir/forge-1.16.5" as File -project(':forge-1.15.2').projectDir = "$rootDir/forge-1.15.2" as File -project(':forge-1.14.4').projectDir = "$rootDir/forge-1.14.4" as File - +// Focused builds avoid configuring unrelated mod loaders; the default keeps upstream scope. +def platform = providers.gradleProperty('dynmapPlatform').getOrElse('all') +if (!(platform in ['all', 'core', 'spigot'])) { + throw new GradleException("Unknown dynmapPlatform: ${platform}") +} +def modules = [ + 'spigot', + 'bukkit-helper-113-2', + 'bukkit-helper-114-1', + 'bukkit-helper-115', + 'bukkit-helper-116', + 'bukkit-helper-116-2', + 'bukkit-helper-116-3', + 'bukkit-helper-116-4', + 'bukkit-helper-117', + 'bukkit-helper-118', + 'bukkit-helper-118-2', + 'bukkit-helper-119', + 'bukkit-helper-119-3', + 'bukkit-helper-119-4', + 'bukkit-helper-120', + 'bukkit-helper-120-2', + 'bukkit-helper-120-4', + 'bukkit-helper-120-5', + 'bukkit-helper-121', + 'bukkit-helper-121-3', + 'bukkit-helper-121-4', + 'bukkit-helper-121-5', + 'bukkit-helper-121-6', + 'bukkit-helper-121-10', + 'bukkit-helper-121-11', + 'bukkit-helper', + 'dynmap-api', + 'DynmapCore', + 'DynmapCoreAPI', + 'fabric-1.21.6', + 'fabric-1.21.9-10', + 'fabric-1.21.11', + 'fabric-1.20.6', + 'fabric-1.19.4', + 'fabric-1.18.2', + 'fabric-1.17.1', + 'fabric-1.16.4', + 'fabric-1.15.2', + 'fabric-1.14.4', + 'forge-1.21.11', + 'forge-1.21.10', + 'forge-1.21.6', + 'forge-1.20.6', + 'forge-1.19.3', + 'forge-1.18.2', + 'forge-1.17.1', + 'forge-1.16.5', + 'forge-1.15.2', + 'forge-1.14.4', +] +modules.findAll { name -> + platform == 'all' || name in ['DynmapCore', 'DynmapCoreAPI'] || + (platform == 'spigot' && (name in ['spigot', 'dynmap-api'] || name.startsWith('bukkit-helper'))) +}.each { name -> include ':' + name } diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 8efeee429..fa73f7aa2 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: org.dynmap.bukkit.DynmapPlugin version: "${version}-${buildnumber}" api-version: 1.13 authors: [mikeprimm] -website: "https://www.reddit.com/r/Dynmap/" +website: "https://github.com/acecore-systems/dynmap" softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, GroupManager, LuckPerms, Vault, SkinsRestorer ] commands: dynmap: