docs: add repo-local agent workflow guidance
This commit is contained in:
1 parent
c228809ac9
commit
7627b24863
11 files changed
+777
-237
No files matched your search
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: compatibility-strategy
|
||||
description: Use for Java 8 backports, Bukkit/Paper version differences, NMS/reflection, PacketEvents compatibility, fake-player implementation choices, and feature-detection design; do not use for release publishing, PR wording, or simple test-running decisions.
|
||||
---
|
||||
|
||||
# Compatibility Strategy
|
||||
|
||||
Use this skill when a change must behave across legacy and modern Bukkit/Paper versions or across different Java runtimes.
|
||||
|
||||
## When to use
|
||||
|
||||
- Java 8 source compatibility or dependency updates that might introduce newer APIs.
|
||||
- NMS, CraftBukkit, Paper, PacketEvents, XSeries, or reflection-sensitive changes.
|
||||
- Fake-player internals, attack delivery, fire ticks, entity ticking, or synthetic network plumbing.
|
||||
- Decisions about feature detection versus hard-coded Minecraft version checks.
|
||||
- Fallback behaviour on Spigot, Paper, legacy 1.12/1.9, or modern 1.20.5+ servers.
|
||||
|
||||
## When not to use
|
||||
|
||||
- Do not use for release asset or publishing checks; use `release-readiness-review`.
|
||||
- Do not use for config/modeset-only changes unless compatibility affects the implementation; use `module-config-change` first.
|
||||
- Do not use for PR prose only; use `pr-draft-summary`.
|
||||
|
||||
## Required stance
|
||||
|
||||
- Prefer direct supported APIs when present.
|
||||
- Use reflection only as a fallback or compatibility bridge, especially off hot paths.
|
||||
- Prefer feature detection by class, method, field, component, or capability over hard-coded version numbers.
|
||||
- Keep Java 8-targeted code free of Java 9+ collection factories, `Stream.toList()`, records, pattern matching, and other newer language/runtime features.
|
||||
- For NMS access, prefer `utilities.reflection.Reflector` helpers where they fit the problem.
|
||||
- Avoid hard-coded versioned NMS class names unless no heuristic or API-based path is viable.
|
||||
|
||||
## Compatibility checklist
|
||||
|
||||
1. Identify the minimum supported Java runtime for the touched source set.
|
||||
2. Identify whether Bukkit/Paper exposes a direct API for the target behaviour.
|
||||
3. If an API may be missing or backported, add runtime feature detection.
|
||||
4. Choose a safe fallback that preserves existing behaviour where feature detection fails.
|
||||
5. Keep diagnostics actionable when every candidate path fails.
|
||||
6. Select validation versions that cover the compatibility boundary, usually a legacy version plus a modern Paper version.
|
||||
|
||||
## Java 8 reminders
|
||||
|
||||
- Use `collect(Collectors.toList())` instead of `Stream.toList()`.
|
||||
- Use mutable collections wrapped with `Collections.unmodifiable*` where immutable Java 9 factories would otherwise be tempting.
|
||||
- Avoid records, sealed classes, switch expressions, pattern matching, and `var` in Java source.
|
||||
- Main build config targets Java 8 via `options.release.set(8)` and Kotlin `jvmTarget = 1.8`.
|
||||
|
||||
## Fake-player and NMS reminders
|
||||
|
||||
- The primary fake-player path is modern-NMS-oriented and relies on reflection remapping.
|
||||
- Legacy 1.12 uses versioned NMS and currently needs dedicated or version-aware handling for realistic fake-player tests.
|
||||
- Fake players are simulated login/network entities, not real network clients.
|
||||
- PacketEvents may need fake users or explicit client-version seeding in synthetic scenarios.
|
||||
|
||||
## References
|
||||
|
||||
- Broader test-harness and historical notes: `../integration-test-verification/references/relocated-agents-notes.md`.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: integration-test-verification
|
||||
description: Use when running, selecting, authoring, or triaging integration tests, Kotest specs, Gradle matrix tasks, FakePlayer-backed test cases, or compact test-result files; do not use for unrelated production-only edits, release publishing, or general PR prose.
|
||||
---
|
||||
|
||||
# Integration Test Verification
|
||||
|
||||
Use this skill for the repository's Paper-backed integration-test harness and for deciding how to validate a change without opening large server logs by default.
|
||||
|
||||
## When to use
|
||||
|
||||
- Selecting `./gradlew integrationTest` versus a version-limited matrix run.
|
||||
- Adding or changing Kotlin integration tests under `src/integrationTest/kotlin`.
|
||||
- Registering a new spec in `KotestRunner`.
|
||||
- Triaging `checkTestResults<version>` output or `plugins/OldCombatMechanicsTest/test-failures.txt`.
|
||||
- Reasoning about tests that use `FakePlayer`, synthetic Bukkit events, PacketEvents wrappers, or direct module handler calls.
|
||||
|
||||
## When not to use
|
||||
|
||||
- Do not use for release asset, Hangar, CurseForge, or licence checks; use `release-readiness-review`.
|
||||
- Do not use for module assignment or config migration design unless the task is specifically about test coverage; use `module-config-change`.
|
||||
- Do not use for broad Java 8/NMS design unless the immediate question is test-harness behaviour; use `compatibility-strategy`.
|
||||
|
||||
## Non-negotiable rules
|
||||
|
||||
- Do not read `build/integration-test-logs/*.log` unless the user explicitly asks for log inspection.
|
||||
- Prefer compact console output and `plugins/OldCombatMechanicsTest/test-failures.txt` for failure triage.
|
||||
- New integration specs must be added to `KotestRunner.withClasses(...)`; autoscan is disabled.
|
||||
- Keep paths relative to the repository root, plugin data folder, or server run directory.
|
||||
- Avoid starting servers for documentation-only or opencode-config-only changes.
|
||||
|
||||
## Harness quick reference
|
||||
|
||||
- Full matrix: `./gradlew integrationTest`.
|
||||
- Selected matrix: `./gradlew integrationTest -PintegrationTestVersions=1.19.2,1.21.11,1.12`.
|
||||
- Wrapper helper: `scripts/run-integration-matrix.sh -v 1.19.2,1.21.11 [-s '*SpecName*'] [-t '*test name*']` keeps Gradle and Kotest filter arguments consistent.
|
||||
- Compact result summary: `scripts/summarise-integration-results.sh` reads only `run/*/plugins/OldCombatMechanicsTest/test-results.txt` and `test-failures.txt`; it does not open full server logs.
|
||||
- Matrix suggestion helper: `scripts/suggest-integration-matrix.sh [changed-path ...]` gives a conservative starting point for versions and filters.
|
||||
- Individual task names follow `integrationTest<version>` with punctuation mapped to underscores, for example `integrationTest1_19_2`.
|
||||
- Test plugin entrypoint: `kernitus.plugin.OldCombatMechanics.OCMTestMain`.
|
||||
- Success handoff: `plugins/OldCombatMechanicsTest/test-results.txt` contains `PASS`.
|
||||
- Failure summary: `plugins/OldCombatMechanicsTest/test-failures.txt` contains a compact list, capped for CI readability.
|
||||
- Java 11+ server targets use Kotest 6 (`KotestRunner`, `KotestProjectConfig`).
|
||||
- Java 8 targets use `LegacyTestRunner`, currently only a smoke path.
|
||||
|
||||
## Validation selection template
|
||||
|
||||
1. Identify the changed feature and affected server bands.
|
||||
2. Prefer the narrowest version set that covers the compatibility boundary.
|
||||
3. If the change touches packet handling, include a modern PacketEvents-covered version.
|
||||
4. If the change touches legacy behaviour or Java 8 APIs, include `1.12` where practical.
|
||||
5. If the change is documentation or opencode-only, run filesystem/schema checks rather than server tests.
|
||||
|
||||
Example recommendation:
|
||||
|
||||
```text
|
||||
Run: ./gradlew integrationTest -PintegrationTestVersions=1.19.2,1.21.11
|
||||
Reason: covers modern Paper plus the PacketEvents path affected by the change.
|
||||
Avoid: opening build/integration-test-logs unless compact failure output is insufficient and the user approves.
|
||||
```
|
||||
|
||||
## Known shortcut patterns
|
||||
|
||||
Some tests intentionally exercise module code without fully realistic gameplay plumbing:
|
||||
|
||||
- `GoldenAppleIntegrationTest`, `OldPotionEffectsIntegrationTest`, `OldArmourDurabilityIntegrationTest`, `PlayerKnockbackIntegrationTest`, `SwordBlockingIntegrationTest`, and `SwordSweepIntegrationTest` use synthetic events in places.
|
||||
- `PlayerKnockbackIntegrationTest` and `SwordSweepIntegrationTest` directly invoke module handlers.
|
||||
- Packet cancellation tests construct and dispatch PacketEvents events directly for deterministic coverage.
|
||||
|
||||
Treat these shortcuts as design choices unless the task asks for end-to-end realism.
|
||||
|
||||
## References
|
||||
|
||||
- Historical integration and regression notes: `references/relocated-agents-notes.md`.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Relocated AGENTS.md Historical Notes
|
||||
|
||||
This reference preserves detailed repository context that previously made the root `AGENTS.md` difficult to scan. Keep always-on routing rules in `AGENTS.md`; keep operational detail here or in a more specific skill.
|
||||
|
||||
## Integration test harness
|
||||
|
||||
- Integration tests live in `src/integrationTest/kotlin` and are packaged into `OldCombatMechanics-<version>-tests.jar`.
|
||||
- Entrypoint plugin class: `kernitus.plugin.OldCombatMechanics.OCMTestMain`.
|
||||
- Tests run inside a real Paper server started by the Gradle `run-paper` plugin.
|
||||
- RunServer output is redirected to `build/integration-test-logs/<version>.log`; `checkTestResults<version>` prints only a compact summary/failures to the console.
|
||||
- `KotestRunner` writes `plugins/OldCombatMechanicsTest/test-failures.txt` so CI can surface failure reasons without opening the full server log.
|
||||
- PacketEvents is shaded into the plugin; integration tests do not inject external packet libraries.
|
||||
- `relocateIntegrationTestClasses` relocates PacketEvents references in test classes only.
|
||||
- `integrationTestJar` embeds relocated test classes plus runtime dependencies, excluding PacketEvents so the test plugin resolves PacketEvents from the main plugin’s shaded copy.
|
||||
- `PacketCancellationIntegrationTest` uses a cancellable `CompletableFuture.await()` and drives PacketEvents via `PacketEventsImplHelper.handleClientBoundPacket` with a synthetic `User`.
|
||||
- `PacketCancellationIntegrationTest` sets the Bukkit player on the synthetic PacketEvents event so module listeners can match `PacketSendEvent#getPlayer`.
|
||||
- Matrix task: `integrationTest` depends on `integrationTestMatrix`.
|
||||
- `OCMTestMain` writes `plugins/OldCombatMechanicsTest/test-results.txt` containing `PASS` or `FAIL`.
|
||||
|
||||
## Version matrix and Java selection
|
||||
|
||||
- Config is in `build.gradle.kts`.
|
||||
- `integrationTestVersions` comes from the property or defaults.
|
||||
- `requiredJavaVersion` selects Java based on version.
|
||||
- Pre-1.13 versions use `integrationTestJavaVersionLegacyPre13` (default 8).
|
||||
- Modern versions use Java 25 if `>=1.20.5`, else Java 17.
|
||||
- Legacy vanilla jar cache: `downloadVanilla<version>` downloads Mojang server jars for `<=1.12` and writes `run/<version>/cache/mojang_<version>.jar`.
|
||||
- Paper 1.12 sometimes fails to download the legacy vanilla jar from old Mojang endpoints; the custom `downloadVanilla` task uses the v2 manifest.
|
||||
|
||||
## Kotlin runner split
|
||||
|
||||
- Kotest 6 (Java 11+) is used for 1.19.2 and 1.21.11.
|
||||
- Kotest runner class: `kernitus.plugin.OldCombatMechanics.KotestRunner`.
|
||||
- Project config: `KotestProjectConfig`.
|
||||
- Java 8 uses `kernitus.plugin.OldCombatMechanics.LegacyTestRunner`.
|
||||
- The Java 8 path is currently a smoke test only, verifying the plugin is enabled and `WeaponDamages` is loaded.
|
||||
|
||||
## Fake player notes
|
||||
|
||||
- Primary implementation: `src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FakePlayer.kt`.
|
||||
- It uses `xyz.jpenilla:reflection-remapper` to map modern NMS names.
|
||||
- On legacy servers, mappings are unavailable and code falls back to `ReflectionRemapper.noop()`.
|
||||
- As-is, `FakePlayer` uses modern NMS class names; Paper 1.12 needs a dedicated fake-player path or version-aware class mapping.
|
||||
- Fake player implementations use simulated login/network plumbing with `EmbeddedChannel` and manual login/join/quit events, not a real networked client.
|
||||
- `FakePlayer` uses a plain `EmbeddedChannel` with dummy `decoder`/`encoder` handlers so PacketEvents treats it as fake and does not disallow login.
|
||||
- Modern fake players schedule a manual NMS tick, preferring `doTick`, then `tick`, then `baseTick`.
|
||||
- The tick shim invokes `baseTick` while burning to ensure fire tick damage events still occur on Paper 1.21+, and prefers normal ticking when in water so extinguish logic can run.
|
||||
- `FakePlayer` prefers `PlayerList.placeNewPlayer` and forces a world add when the Bukkit world does not report the fake player entity after placement.
|
||||
- `FakePlayer` clears invulnerability/instabuild abilities after spawn, plus a legacy fallback.
|
||||
|
||||
## Test harness shortcuts
|
||||
|
||||
- Synthetic event tests: `GoldenAppleIntegrationTest`, `OldPotionEffectsIntegrationTest`, `OldArmourDurabilityIntegrationTest`, `PlayerKnockbackIntegrationTest`, `SwordBlockingIntegrationTest`, and `SwordSweepIntegrationTest`.
|
||||
- Direct module handler tests: `PlayerKnockbackIntegrationTest` and `SwordSweepIntegrationTest`.
|
||||
- `AttributeModifierCompat` synthesises a fallback attack-damage modifier from `NewWeaponDamage` when API attributes are missing.
|
||||
|
||||
## Registration and notable test history
|
||||
|
||||
- `KotestRunner` must include each new integration test class explicitly.
|
||||
- `OldToolDamageMobIntegrationTest`, `WeaponDurabilityIntegrationTest`, `DisableOffhandIntegrationTest`, `ToolDamageTooltipIntegrationTest`, `PacketCancellationIntegrationTest`, `ModesetRulesIntegrationTest`, `ConfigMigrationIntegrationTest`, `ChorusFruitIntegrationTest`, `DisableOffhandReflectionIntegrationTest`, `AttackRangeIntegrationTest`, `PaperSwordBlockingDamageReductionIntegrationTest`, and `ConsumableComponentIntegrationTest` have all been registered historically.
|
||||
- `Kotest` filters (`kotest.filter.specs`, `kotest.filter.tests`) pass through Gradle into the run-paper JVM args.
|
||||
- `WeaponDurabilityIntegrationTest` and `OldToolDamageMobIntegrationTest` resolve debug paths relative to the repo root rather than hard-coded home paths.
|
||||
- `1.9` integration tests are currently on hold per user request.
|
||||
- 1.21.11 servers may log hostname and Unsafe warnings; tests can still pass.
|
||||
|
||||
## PacketEvents and packet cancellation history
|
||||
|
||||
- `ModuleSwordSweepParticles` and `ModuleAttackSounds` use PacketEvents listeners/wrappers instead of ProtocolLib.
|
||||
- `PacketCancellationIntegrationTest` builds a `PacketSendEvent` directly from the transformed buffer, sets packet id/type explicitly, and dispatches it through the PacketEvents event manager.
|
||||
- PacketEvents dependency moved to `2.11.2-SNAPSHOT` from CodeMC snapshots for 1.21.11 support.
|
||||
|
||||
## Sword blocking and consumable component history
|
||||
|
||||
- Paper-only sword blocking uses runtime-gated helper `kernitus.plugin.OldCombatMechanics.paper.PaperSwordBlocking` to apply consumable and blocking components via cached reflection.
|
||||
- 1.8-style reduction is applied via `EntityDamageByEntityListener` using the `BLOCKING` modifier, while legacy/non-Paper keeps the shield swap path.
|
||||
- `ModuleSwordBlocking` gates the Paper consumable animation by PacketEvents client version `>=1.20.5`; older clients fall back to offhand shield behaviour.
|
||||
- Unknown PacketEvents client versions fall back to shield behaviour only when a PacketEvents `User` exists; early-login/synthetic players keep animation support unless resolver failure requires fail-safe fallback.
|
||||
- Inventory-click and drag handlers follow a minimal-mutation policy; lifecycle and transition handlers clean stale consumable state.
|
||||
- Lifecycle cleanup covers modeset change, reload, world change, quit, join, held-slot changes, and swap paths.
|
||||
- `sword-blocking.paper-animation` can hard-disable the Paper animation path and force legacy shield fallback.
|
||||
- Legacy shield-drop reconciliation is safety-first and avoids guessing plain shield drops as temporary shields when marker APIs are unavailable.
|
||||
|
||||
## Fire aspect and fire tick notes
|
||||
|
||||
- `FireAspectOverdamageIntegrationTest` uses a Zombie victim for real fire tick sampling with boosted max health.
|
||||
- Initial tests fire synthetic `EntityDamageEvent` with `FIRE_TICK` for deterministic baseline checks.
|
||||
- Paper 1.12 applies attack-cooldown scaling before Bukkit damage events; tests wait a few ticks before the first attack to stabilise the first hit.
|
||||
- Extra coverage includes fire resistance, fire-immune victims, water extinguish behaviour, fire protection versus protection, and alternating attackers during invulnerability.
|
||||
- Legacy player-specific fire tick sampling differs, so player afterburn-vs-environment comparisons are no-ops on legacy; Zombie variants still run across versions.
|
||||
|
||||
## Miscellaneous historical fixes
|
||||
|
||||
- Removed dead reflection utility `ClassType` and unused `Reflector#getClass(ClassType, String)` overload.
|
||||
- `SpigotFunctionChooser` now only falls back for compatibility-style failures and rethrows ordinary runtime logic failures.
|
||||
- `AttackCompat` requires observable living-target hit signals for Bukkit `Player#attack` success and treats boolean-return NMS methods returning `false` as failed attempts.
|
||||
- Several modules replaced per-player/per-hit scheduled tasks with shared or lazy-pruned maps: sword blocking dedupe, fishing rod velocity, attack cooldown tracking, player knockback, shield damage reduction, old armour durability, player regen, disable enderpearl cooldown, and stored last-damage expiry.
|
||||
- `ModuleLoader` clears the static module list on initialise to prevent duplicate registrations after hot reloads.
|
||||
- `README` includes licence notes: source remains MPL-2.0, while pre-built jars bundling PacketEvents are distributed under GPLv3; builds without PacketEvents can remain MPL-2.0.
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: module-config-change
|
||||
description: Use for config.yml, module enablement, modesets, config migration, configurable module assignment, and per-module option changes; do not use for unrelated integration-test triage, release publishing, or PR summaries.
|
||||
---
|
||||
|
||||
# Module Config Change
|
||||
|
||||
Use this skill when changing module configuration, defaults, migration, or runtime handling driven by `config.yml`.
|
||||
|
||||
## When to use
|
||||
|
||||
- Editing bundled `config.yml` defaults or comments.
|
||||
- Adding or changing module settings such as attack cooldown, attack range, sword blocking, old tool damage, tooltip, potion effects, or durability behaviour.
|
||||
- Changing `always_enabled_modules`, `disabled_modules`, modesets, or config migration logic.
|
||||
- Validating reload behaviour after config changes.
|
||||
- Writing tests around config assignment, migration, or modeset-specific behaviour.
|
||||
|
||||
## When not to use
|
||||
|
||||
- Do not use for ordinary test execution unless the test is config-specific; use `integration-test-verification`.
|
||||
- Do not use for Java/NMS compatibility strategy unless the config option depends on runtime feature detection; use `compatibility-strategy` as well.
|
||||
- Do not use for release workflow edits; use `release-readiness-review`.
|
||||
|
||||
## Mandatory configuration rules
|
||||
|
||||
- Configurable modules must appear in only one top-level assignment category: `always_enabled_modules`, `disabled_modules`, or the aggregate `modesets` category. The same module may appear in more than one individual modeset because modesets are alternative player modes.
|
||||
- Duplicate entries are still invalid within the same exact list or individual modeset.
|
||||
- Internal modules (`modeset-listener`, `attack-cooldown-tracker`, `entity-damage-listener`) are always enabled and must not be listed.
|
||||
- Reload/enable should fail for invalid module assignment.
|
||||
- Do not add per-module `enabled:` toggles; the module lists and modesets are authoritative.
|
||||
- Keep bundled comments clear for server owners and use British English.
|
||||
|
||||
## Change checklist
|
||||
|
||||
1. Identify whether the change is a default, migration, reload, or runtime behaviour change.
|
||||
2. Update bundled config comments and defaults together when needed.
|
||||
3. Preserve existing user config where migration should be non-destructive.
|
||||
4. Run `scripts/validate-module-assignments.sh` for a quick static check that bundled configurable lists do not conflict across `always_enabled_modules`, `disabled_modules`, and the aggregate `modesets` category; do not duplicate entries within the same exact list or modeset; and do not include internal modules.
|
||||
5. Add or update integration coverage where practical.
|
||||
6. If adding an integration spec, register it in `KotestRunner.withClasses(...)`.
|
||||
7. Validate both enabled and disabled/modeset-scoped behaviour for options that change item state or player state.
|
||||
|
||||
## Useful known behaviours
|
||||
|
||||
- `old-tool-damage.tooltip.enabled` is enabled by default so players can see configured damage in-game.
|
||||
- `attack-range` is listed in `disabled_modules` by default and is Paper 1.21.11+ only.
|
||||
- `sword-blocking.paper-animation` defaults to `true`; when false, sword blocking uses the legacy shield fallback and strips stale Paper consumable state.
|
||||
- Attack cooldown supports `disable-attack-cooldown.held-item-attack-speeds.<MATERIAL>` with XMaterial/Material matching and restores vanilla attack speed when disabled.
|
||||
|
||||
## Validation template
|
||||
|
||||
```text
|
||||
Config area:
|
||||
Default changed:
|
||||
Migration needed:
|
||||
Enabled-path validation:
|
||||
Disabled/modeset validation:
|
||||
KotestRunner registration needed:
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Historical integration and module notes: `../integration-test-verification/references/relocated-agents-notes.md`.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: pr-draft-summary
|
||||
description: Use when drafting pull-request titles, descriptions, change summaries, risk notes, validation sections, or reviewer handoff text; do not use for implementation design, release publishing checks, or raw test failure triage.
|
||||
---
|
||||
|
||||
# PR Draft Summary
|
||||
|
||||
Use this skill to turn completed work into reviewer-facing prose. Keep wording concise, accurate, and in British English.
|
||||
|
||||
## When to use
|
||||
|
||||
- Drafting a PR title or body.
|
||||
- Summarising changes after a slice or branch of work.
|
||||
- Writing reviewer notes, risk notes, validation sections, or follow-up items.
|
||||
- Turning technical details into a changelog-style summary.
|
||||
|
||||
## When not to use
|
||||
|
||||
- Do not use before implementation decisions are made; use the relevant implementation skill first.
|
||||
- Do not use for release readiness unless the requested output is a PR description; use `release-readiness-review` for release checks.
|
||||
- Do not use to interpret raw integration test failures; use `integration-test-verification`.
|
||||
|
||||
## Preferred structure
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
-
|
||||
-
|
||||
|
||||
## Validation
|
||||
-
|
||||
|
||||
## Risks / notes
|
||||
-
|
||||
```
|
||||
|
||||
## Conventional commit prompt
|
||||
|
||||
When asked for a commit message, use conventional commits format, for example:
|
||||
|
||||
```text
|
||||
docs: add repo-local agent skill guidance
|
||||
```
|
||||
|
||||
## Reviewer handoff checklist
|
||||
|
||||
- Name the changed area before the mechanism.
|
||||
- Mention validation commands exactly as run.
|
||||
- If validation was not run, say why.
|
||||
- Separate behaviour changes from documentation/configuration changes.
|
||||
- Avoid overstating coverage; note known gaps plainly.
|
||||
|
||||
## Example phrasing
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
- Added repo-local agent skills for integration-test verification, compatibility strategy, module config changes, release readiness, and PR summaries.
|
||||
- Slimmed root `AGENTS.md` to always-on rules plus routing guidance, with historical details relocated into skill references.
|
||||
|
||||
## Validation
|
||||
- Checked skill frontmatter and folder-name matches.
|
||||
- Confirmed no plugin runtime files changed.
|
||||
|
||||
## Risks / notes
|
||||
- opencode must be restarted before new or changed skills are available in a running session.
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: release-readiness-review
|
||||
description: Use for GitHub release, Hangar, CurseForge/BukkitDev upload, licence, asset naming, supported-version, and workflow readiness checks; do not use for day-to-day feature implementation, test authoring, or PR prose only.
|
||||
---
|
||||
|
||||
# Release Readiness Review
|
||||
|
||||
Use this skill before changing or reviewing release workflows, publishing metadata, supported game versions, licence notes, and final release assets.
|
||||
|
||||
## When to use
|
||||
|
||||
- Editing `.github/workflows/*release*` or publishing-related Gradle configuration.
|
||||
- Checking Hangar, CurseForge/BukkitDev, or GitHub release upload behaviour.
|
||||
- Reviewing licence implications of shaded dependencies such as PacketEvents.
|
||||
- Updating supported Minecraft versions, `gradle.properties` release metadata, or asset names.
|
||||
- Preparing a release-readiness checklist for maintainers.
|
||||
|
||||
## When not to use
|
||||
|
||||
- Do not use for ordinary implementation or test selection; use the feature-specific skill.
|
||||
- Do not use for PR description wording unless release notes are the main task; use `pr-draft-summary`.
|
||||
- Do not use for module config behaviour unless it affects packaging or documented release defaults.
|
||||
|
||||
## Current release notes
|
||||
|
||||
- GitHub release asset uses stable filename `OldCombatMechanics.jar` without a version suffix.
|
||||
- CurseForge upload uses the same path.
|
||||
- Bukkit-compatible CurseForge game-version entries use `1:<version>` prefixes so type-1 Bukkit versions are selected.
|
||||
- Hangar publish configuration expects `HANGAR_API_TOKEN`.
|
||||
- README licence note: source remains MPL-2.0; pre-built jars bundling PacketEvents are distributed under GPLv3; builds without PacketEvents can remain MPL-2.0.
|
||||
- `gradle.properties` game versions currently include 1.21.11 down to 1.21.1, plus 1.21, ahead of existing entries.
|
||||
|
||||
## Review checklist
|
||||
|
||||
1. Confirm secrets are referenced by environment variable or GitHub secret name, never hard-coded.
|
||||
2. Confirm the built jar path and uploaded asset path match.
|
||||
3. Confirm supported game versions map to the publishing platform’s expected identifiers.
|
||||
4. Confirm shaded dependency licence implications are reflected in release notes or README where needed.
|
||||
5. Confirm workflow changes do not alter plugin runtime behaviour.
|
||||
6. Prefer dry, static checks unless the user explicitly requests a release run.
|
||||
|
||||
## Release note template
|
||||
|
||||
```markdown
|
||||
### Release readiness
|
||||
- Asset: `OldCombatMechanics.jar`
|
||||
- Platforms checked: GitHub / Hangar / CurseForge
|
||||
- Version metadata checked:
|
||||
- Licence notes checked:
|
||||
- Secrets touched: none / list variable names only
|
||||
- Remaining manual steps:
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Broader historical notes: `../integration-test-verification/references/relocated-agents-notes.md`.
|
||||
@@ -2,257 +2,84 @@
|
||||
|
||||
**Prime directive:** If you are reading this file, it is your responsibility as an agent to keep it up to date with any changes you make in this repository.
|
||||
|
||||
This file captures repo-specific context discovered while working on this branch.
|
||||
This file holds always-on repository guidance and routing hints. Detailed workflows and historical notes live in repo-local agent skills under `.agents/skills/`.
|
||||
|
||||
## Always-on rules
|
||||
|
||||
- Use British English spelling and phraseology at all times.
|
||||
- Do **not** use American English spelling or phraseology.
|
||||
- Do **not** open or read `build/integration-test-logs/*.log` unless the user explicitly asks for log inspection. Prefer compact console summaries and `plugins/OldCombatMechanicsTest/test-failures.txt`.
|
||||
- Never hard-code absolute filesystem paths in tests or production code; resolve locations relative to the repo root, plugin data folder, or server run directory.
|
||||
- Prefer feature detection over hard-coded Minecraft version gates because server implementations may backport APIs.
|
||||
- For NMS/reflection access, prefer the project `utilities.reflection.Reflector` helpers over ad-hoc reflection where practical.
|
||||
- Reflection should be a compatibility fallback, not the first choice on hot paths.
|
||||
- Do not gate plugin behaviour on assumptions from one Paper/Spigot version without checking compatible API presence.
|
||||
|
||||
## Repo overview
|
||||
- Project: OldCombatMechanics (Bukkit/Paper plugin)
|
||||
- Branch context: working from `kotlin-tests` branch
|
||||
- Build tool: Gradle (wrapper currently at 9.2.1)
|
||||
- JDKs used locally: 8, 11, 17, 25
|
||||
|
||||
## Integration test harness (Kotlin)
|
||||
- Integration tests live in `src/integrationTest/kotlin` and are packaged into `OldCombatMechanics-<version>-tests.jar`.
|
||||
- Entrypoint plugin class: `kernitus.plugin.OldCombatMechanics.OCMTestMain`.
|
||||
- Tests run inside a real Paper server started by the Gradle `run-paper` plugin.
|
||||
- RunServer output is redirected to `build/integration-test-logs/<version>.log`; `checkTestResults<version>` prints only a compact summary/failures to the console.
|
||||
- `KotestRunner` writes a compact `plugins/OldCombatMechanicsTest/test-failures.txt` file (up to 25 failures) so CI can surface failure reasons without opening the full server log.
|
||||
- Token hygiene: do **not** open/read `build/integration-test-logs/*.log` unless the user explicitly asks for log inspection; rely on the compact console summary by default and ask for permission before digging into full logs.
|
||||
- PacketEvents is shaded into the plugin; integration tests do not inject external packet libraries.
|
||||
- `relocateIntegrationTestClasses` (ShadowJar) relocates PacketEvents references in test classes only.
|
||||
- `integrationTestJar` is a plain `Jar` task that embeds the relocated test classes plus runtime deps, excluding PacketEvents so the test plugin resolves PacketEvents from the main plugin’s shaded copy.
|
||||
- `PacketCancellationIntegrationTest` now uses a cancellable `CompletableFuture.await()` so `withTimeout` actually aborts when no packet arrives.
|
||||
- `PacketCancellationIntegrationTest` now drives PacketEvents via `PacketEventsImplHelper.handleClientBoundPacket` with a synthetic `User` instead of relying on live channel injection; this avoids timeouts when PacketEvents does not emit send events for fake channels.
|
||||
- `PacketCancellationIntegrationTest` sets the Bukkit player on the synthetic PacketEvents event so module listeners can match `PacketSendEvent#getPlayer`.
|
||||
- Matrix task: `integrationTest` depends on `integrationTestMatrix` which runs per-version tasks like:
|
||||
- `integrationTest1_19_2`, `integrationTest1_21_11`, `integrationTest1_12`, `integrationTest1_9`
|
||||
- Test result handoff:
|
||||
- `OCMTestMain` writes `plugins/OldCombatMechanicsTest/test-results.txt` containing `PASS` or `FAIL`.
|
||||
- `KotestRunner` also writes `plugins/OldCombatMechanicsTest/test-failures.txt` (small failure summary).
|
||||
- Gradle `checkTestResults<version>` fails build if file missing, or content is not `PASS`.
|
||||
- Project: OldCombatMechanics, a Bukkit/Paper plugin.
|
||||
- Branch context: working from `kotlin-tests`.
|
||||
- Build tool: Gradle wrapper, currently 9.2.1.
|
||||
- JDKs used locally: 8, 11, 17, 25.
|
||||
- Main integration tests live in `src/integrationTest/kotlin` and are packaged into `OldCombatMechanics-<version>-tests.jar`.
|
||||
- Entrypoint test plugin class: `kernitus.plugin.OldCombatMechanics.OCMTestMain`.
|
||||
- PacketEvents is shaded into the main plugin; integration tests resolve PacketEvents from that shaded copy rather than injecting a separate external packet library.
|
||||
|
||||
## Version matrix + Java selection
|
||||
- Config is in `build.gradle.kts`:
|
||||
- `integrationTestVersions` from property or defaults.
|
||||
- `requiredJavaVersion` selects Java based on version.
|
||||
- Pre-1.13 versions use `integrationTestJavaVersionLegacyPre13` (default 8).
|
||||
- Modern versions use Java 25 if `>=1.20.5` else Java 17.
|
||||
- Legacy vanilla jar cache:
|
||||
- `downloadVanilla<version>` task downloads Mojang server jars for <=1.12 and writes `run/<version>/cache/mojang_<version>.jar`.
|
||||
## Build and validation quick reference
|
||||
|
||||
## Kotlin test runner split (Java 11+ vs Java 8)
|
||||
- Kotest 6 (Java 11+) is used for 1.19.2 and 1.21.11.
|
||||
- Kotest runner class: `kernitus.plugin.OldCombatMechanics.KotestRunner`
|
||||
- Project config: `KotestProjectConfig`
|
||||
- Java 8 uses a separate runner:
|
||||
- `kernitus.plugin.OldCombatMechanics.LegacyTestRunner`
|
||||
- Currently a **smoke test** only (verifies plugin enabled + `WeaponDamages` loaded).
|
||||
- This is a placeholder and **not a full integration test**.
|
||||
- Full integration matrix: `./gradlew integrationTest`.
|
||||
- Selected versions: `./gradlew integrationTest -PintegrationTestVersions=1.19.2,1.21.11,1.12`.
|
||||
- Matrix task examples: `integrationTest1_19_2`, `integrationTest1_21_11`, `integrationTest1_12`, `integrationTest1_9`.
|
||||
- Java toolchain override example: `ORG_GRADLE_JAVA_INSTALLATIONS_PATHS=/path/to/jdk8:/path/to/jdk17:/path/to/jdk25 ./gradlew integrationTest`.
|
||||
- `checkTestResults<version>` fails the build if `plugins/OldCombatMechanicsTest/test-results.txt` is missing or not `PASS`.
|
||||
- `KotestRunner` writes a compact `plugins/OldCombatMechanicsTest/test-failures.txt` summary for CI-friendly failure triage.
|
||||
|
||||
## Fake player implementation notes
|
||||
- Primary fake player implementation is `src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FakePlayer.kt`.
|
||||
- It relies on `xyz.jpenilla:reflection-remapper` to map modern NMS names.
|
||||
- On legacy servers (1.12), reflection remapper mappings are unavailable; code falls back to `ReflectionRemapper.noop()`.
|
||||
- As-is, `FakePlayer` uses modern NMS class names (`net.minecraft.server.MinecraftServer`, etc.).
|
||||
- This fails on 1.12 which uses versioned NMS (`net.minecraft.server.v1_12_R1.*`).
|
||||
- 1.12 requires a dedicated fake player path or version-aware class mapping.
|
||||
## Skill selection guide
|
||||
|
||||
## Java 8 compatibility work
|
||||
- Java 8 compatibility backports already done in main code (records/pattern matching removed).
|
||||
- Java 8-incompatible APIs replaced:
|
||||
- `Stream.toList()` -> `collect(Collectors.toList())`
|
||||
- `Set.of`/`List.of` -> `Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))` etc.
|
||||
- Added missing import in `PotionTypeCompat` for `PotionData`.
|
||||
- Build config sets `options.release.set(8)` for Java, Kotlin `jvmTarget = 1.8`.
|
||||
- **Mandatory trigger:** Use `integration-test-verification` for integration-test runs, matrix selection, compact failure triage, Kotlin integration test authoring, or `KotestRunner` registration questions. Do not use it for ordinary unit-free source edits with no test-harness impact.
|
||||
- **Mandatory trigger:** Use `compatibility-strategy` for Java 8 compatibility, Bukkit/Paper version differences, NMS/reflection, fake players, PacketEvents compatibility, feature detection, or legacy-server fallbacks. Do not use it for release notes or configuration-only routing.
|
||||
- **Mandatory trigger:** Use `module-config-change` for `config.yml`, module enablement, modesets, migrations, configurable module assignment, or per-module settings such as tool damage, attack cooldown, attack range, or sword blocking options. Do not use it for pure test execution or release publishing.
|
||||
- **Mandatory trigger:** Use `release-readiness-review` for GitHub release, Hangar, CurseForge/BukkitDev, licence, workflow, supported-version, or release asset checks. Do not use it for feature implementation unless the change affects release packaging.
|
||||
- **Mandatory trigger:** Use `pr-draft-summary` when drafting PR descriptions, change summaries, reviewer notes, or risk/test sections. Do not use it while still deciding implementation strategy.
|
||||
|
||||
## Dependency / build updates
|
||||
- Kotlin: 2.3.0
|
||||
- Kotest: 6.0.7
|
||||
- run-paper plugin: 3.0.2
|
||||
- Hangar publish plugin: 0.1.4
|
||||
- Other deps updated (bstats, netty, BSON, XSeries, authlib, reflection-remapper, adventure).
|
||||
- XSeries is now 13.7.0 for MC/Paper 26.x version parsing support (#862).
|
||||
- PacketEvents is now used for packet interception (shaded and relocated in the main jar).
|
||||
- PacketEvents dependency moved to `2.11.2-SNAPSHOT` (CodeMC snapshots) for 1.21.11 support.
|
||||
- JSR-305 added for `javax.annotation.Nullable` (compileOnly).
|
||||
## Current project state
|
||||
|
||||
## Current failing area
|
||||
- Paper 1.12 integration tests **do not run real fake player tests yet**.
|
||||
- Desired fix: add proper 1.12 fake player implementation (e.g., version-specific NMS path like `v1_12_R1`).
|
||||
- Example 1.12 fake player implementation provided by user (NMS, PlayerList manipulation, packet send, etc.).
|
||||
- We should integrate a conditional path in `FakePlayer` for 1.12 using versioned NMS classes or an alternate helper.
|
||||
- Paper 1.12 integration tests do not yet run the full fake-player test suite; legacy fake-player work needs a dedicated or version-aware NMS path.
|
||||
- Java 8 compatibility is required for main code. Avoid APIs such as records, pattern matching, `Stream.toList()`, and `Set.of`/`List.of` in Java 8-targeted code.
|
||||
- Main Java/Kotlin compilation targets Java 8 (`options.release.set(8)`, Kotlin `jvmTarget = 1.8`).
|
||||
- Pre-1.13 integration-test versions use `integrationTestJavaVersionLegacyPre13` (default 8); modern versions use Java 25 for `>=1.20.5` and Java 17 otherwise.
|
||||
- Legacy vanilla jars for `<=1.12` are downloaded by `downloadVanilla<version>` into `run/<version>/cache/mojang_<version>.jar`.
|
||||
|
||||
## Local test commands
|
||||
- Run full matrix:
|
||||
- `./gradlew integrationTest`
|
||||
- Change matrix:
|
||||
- `./gradlew integrationTest -PintegrationTestVersions=1.19.2,1.21.11,1.12`
|
||||
- Set Java toolchain paths (example):
|
||||
- `ORG_GRADLE_JAVA_INSTALLATIONS_PATHS=/path/to/jdk8:/path/to/jdk17:/path/to/jdk25 ./gradlew integrationTest`
|
||||
## Core implementation constraints
|
||||
|
||||
## Notes
|
||||
- Removed the dead reflection utility `ClassType` and the unused `Reflector#getClass(ClassType, String)` overload; `Reflector#getClass(String)` remains the supported class-resolution helper.
|
||||
- `SpigotFunctionChooser` now only falls back for compatibility-style failures (LinkageError family, missing-method/class reflection failures, and explicit compatibility-signalled `UnsupportedOperationException` via `compat`/`compatibility` markers), and rethrows ordinary runtime logic failures instead of silently selecting fallback (for example generic "incompatible" wording does not trigger fallback).
|
||||
- `AttackCompat` now only treats Bukkit `Player#attack` as success when it yields an observable living-target hit (health/lastDamage/noDamageTicks signal); otherwise it falls back to NMS attack candidates.
|
||||
- `AttackCompat` now treats boolean-return NMS attack methods that return `false` as failed attempts and continues trying other candidates, with expanded failure diagnostics including false-result and exception counts.
|
||||
- Entity-click dedupe in `ModuleSwordBlocking` now uses a taskless lazy-prune timestamp map (`System.nanoTime`) instead of a one-tick scheduled set clear, with a short dedupe window and periodic/size-triggered expiry pruning.
|
||||
- `ModuleSwordBlocking` now handles `PlayerInteractEntityEvent` and `PlayerInteractAtEntityEvent` for main-hand sword blocking, with short time-window dedupe to prevent duplicate side effects when both events fire for the same click.
|
||||
- Removed the unused main-source Java tester package (`kernitus.plugin.OldCombatMechanics.tester`), deleted stale commented test-command code from `OCMCommandHandler`, and dropped the now-obsolete Gradle source-set exclusion for that package.
|
||||
- Paper 1.12 sometimes fails to download legacy vanilla jar from old Mojang endpoint. The custom `downloadVanilla` task fixes that by using the v2 manifest.
|
||||
- 1.21.11 servers log hostname warnings and Unsafe warnings; tests still pass.
|
||||
- 1.9 integration tests are currently on hold per user request.
|
||||
- Kotest filters (`kotest.filter.specs`, `kotest.filter.tests`) are now passed through Gradle into the run-paper JVM args for integration tests.
|
||||
- Reflection should be used only as a fallback (performance cost); prefer direct API/code paths when available.
|
||||
- The Hangar publish workflow exports `HANGAR_API_TOKEN` to match the Gradle Hangar publish configuration.
|
||||
- `ModuleFishingRodVelocity` uses a single shared per-tick task (1.14+) to adjust hook gravity for all active hooks, instead of one scheduled task per hook.
|
||||
- `AttackCooldownTracker#getLastCooldown` is safe to call when the tracker is not registered (returns null) and uses a `HashMap` rather than a `WeakHashMap`.
|
||||
- `AttackCooldownTracker` defensively feature-detects `HumanEntity#getAttackCooldown` and avoids scheduling its per-tick sampler when the API exists (modern/backported servers).
|
||||
- `ModulePlayerKnockback` uses a `HashMap` + a single shared 1-tick expiry cleaner for pending velocity overrides, instead of `WeakHashMap` + one scheduled task per hit.
|
||||
- `ModuleShieldDamageReduction` uses a `HashMap` + a single shared 1-tick expiry cleaner for the “fully blocked” armour-durability suppression, instead of `WeakHashMap` + one scheduled task per hit.
|
||||
- `ModuleOldArmourDurability` uses a `HashMap` + a single shared 1-tick expiry cleaner for the “explosion damaged armour” suppression, instead of `WeakHashMap` + one scheduled task per explosion.
|
||||
- `ModulePlayerRegen` uses tick-based interval tracking (1.8-like; TPS aware) with a `HashMap` and a single shared tick counter task that runs only while players are tracked.
|
||||
- `ModuleDisableEnderpearlCooldown` uses a `HashMap` and lazily drops expired cooldown entries during checks (wall-clock cooldown; no recurring task).
|
||||
- `ModuleSwordBlocking` no longer version-gates Paper support; it feature-detects Paper data component APIs and avoids ConcurrentModificationException by iterating legacy tick state over a snapshot.
|
||||
- Do not gate behaviour on hard-coded Minecraft version numbers; use feature detection (class/method presence) because some servers backport APIs.
|
||||
- Weapon/armour unknown-enchantment warnings only fire for non-`minecraft` namespaces; legacy servers fall back to a known vanilla-enchantment list to avoid warning on built-ins.
|
||||
- For NMS access, prefer the project Reflector helpers (`utilities.reflection.Reflector`) over ad-hoc reflection, and avoid hard-coded versioned class names where heuristics (signatures/fields) can locate methods safely.
|
||||
- Added integration tests in `OldPotionEffectsIntegrationTest` for strength addend scaling (Strength II and III), a distinct modifier value check, and strength multiplier scaling.
|
||||
- Added integration test ensuring vanilla strength addend applies when `old-potion-effects` is disabled.
|
||||
- Strength modifier in `OCMEntityDamageByEntityEvent` now stores per-level value (3) and applies level when reconstructing base damage.
|
||||
- Added `OldToolDamageMobIntegrationTest` to assert old-tool-damage config affects vindicator iron-axe hits.
|
||||
- `KotestRunner` class list updated to include `OldToolDamageMobIntegrationTest`.
|
||||
- `ModuleOldToolDamage` now adjusts mob weapon damage by shifting base damage with the configured-vs-vanilla delta for non-player damagers.
|
||||
- `ModuleOldToolDamage` documents that mob custom weapons are not detected; the delta is always applied for mobs and may conflict with other plugins.
|
||||
- Added `WeaponDurabilityIntegrationTest` covering tool durability vs hit counts during invulnerability and after it expires (FakePlayer attacker vs FakePlayer victim); registered in `KotestRunner`.
|
||||
- `WeaponDurabilityIntegrationTest` writes debug summaries to `build/weapon-durability-debug-<version>.txt`.
|
||||
- `WeaponDurabilityIntegrationTest` and `OldToolDamageMobIntegrationTest` now resolve debug output paths relative to the repo root (based on the server run directory), avoiding hard-coded home paths.
|
||||
- `OldToolDamageMobIntegrationTest` uses a Villager victim and waits for a real Vindicator hit by setting a target and retrying with tick delays (plus a best-effort NMS attackCompat call) before asserting the mob tool-damage delta.
|
||||
- Module assignment is strict for configurable modules: every non-internal module must appear in exactly one of `always_enabled_modules`, `disabled_modules`, or a modeset. Internal modules (`modeset-listener`, `attack-cooldown-tracker`, `entity-damage-listener`) are always enabled and must not be listed; reload/enable fails if they are configured.
|
||||
- Use British English spelling and phraseology at all times.
|
||||
- DO NOT use American English spelling or phraseology under any circumstances.
|
||||
- Never hard-code absolute filesystem paths in tests or production code; resolve locations relative to the repo root or server run directory.
|
||||
- Added `DisableOffhandIntegrationTest` to assert the disable-offhand modeset-change handler does not clear the offhand when the module is not enabled for the player.
|
||||
- `KotestRunner` now includes `DisableOffhandIntegrationTest` in its explicit class list.
|
||||
- Module assignment is strict for configurable modules at top-level category scope: every non-internal module must appear in at most one of `always_enabled_modules`, `disabled_modules`, or the aggregate `modesets` category. A module may appear in more than one individual modeset because modesets are alternative player modes.
|
||||
- Internal modules (`modeset-listener`, `attack-cooldown-tracker`, `entity-damage-listener`) are always enabled and must not be listed in configurable module groups.
|
||||
- Reload/enable must fail for invalid module assignment rather than silently choosing a fallback.
|
||||
- Modules are enabled/disabled solely via `always_enabled_modules`, `disabled_modules`, and `modesets`; there is no per-module `enabled:` toggle.
|
||||
- When adding new integration test specs, add them to the explicit `.withClasses(...)` list in `KotestRunner` because autoscan is disabled.
|
||||
- Added `ToolDamageTooltipIntegrationTest` (in `KotestRunner` list) to define behaviour for an opt-in “configured tool damage tooltip” feature (lore line) under `old-tool-damage.tooltip` (`enabled`, `prefix`).
|
||||
- `old-tool-damage.tooltip.enabled` is now enabled by default in the bundled config so players can see the configured damage in-game.
|
||||
- Modules are enabled/disabled solely via `always_enabled_modules`, `disabled_modules`, and `modesets` (no per-module `enabled:` toggle).
|
||||
- `SwordBlockingIntegrationTest` uses synthetic `PlayerInteractEvent` right-clicks; on legacy (offhand-shield) path this cannot reliably assert `isBlocking`/`isHandRaised` (client-driven), so tests treat “blocking applied” as either a shield injection or a raised hand depending on path.
|
||||
- Added `PacketCancellationIntegrationTest` to cover PacketEvents sweep-particle and attack-sound cancellation using PacketEvents wrappers/listeners (registered in `KotestRunner`).
|
||||
- Added `ModesetRulesIntegrationTest` to cover always-enabled, disabled, and modeset-scoped module rules plus reload failures for invalid assignments.
|
||||
- Added `ConfigMigrationIntegrationTest` to cover config upgrade migration into always/disabled module lists and preservation of custom modesets.
|
||||
- Added a `weakness should not store negative last damage values` test in `InvulnerabilityDamageIntegrationTest` that forces `old-potion-effects.weakness.modifier = -10`, consumes a weakness potion (amplifier -1), attacks once, and asserts the stored last damage is non-negative; passes on 1.12/1.19.2 after clamping, but 1.21.11 can still fail with `No stored last damage for victim (events=0)` (attack event not recorded).
|
||||
- `EntityDamageByEntityListener.checkOverdamage` now clamps the stored `lastDamages` value to a minimum of 0 to avoid negative last-damage entries when weakness (or other modifiers) drives pre-clamp damage below zero.
|
||||
- Weakness amplifier clamping changed in 1.20+: attempts to use amplifier `-1` are clamped to `0` (Weakness I). With low-damage weapons this can yield zero vanilla damage, and Paper 1.21 does not fire `EntityDamageByEntityEvent` for zero damage, so tests that rely on an EDBE hit must use a stronger weapon or account for the clamp.
|
||||
- `OldPotionEffectsIntegrationTest` now disables the module by moving `old-potion-effects` into `disabled_modules` (and removing it from modesets/always lists), then uses `Config.reload()`; the `withConfig` helper restores module lists/modesets and saves+reloads config to keep state consistent.
|
||||
- `FireAspectOverdamageIntegrationTest` includes afterburn-vs-environmental fire-tick checks for both player and zombie victims, with and without Protection IV armour (mirrors issue 707 MRE).
|
||||
- Release workflow (`.github/workflows/build-upload-release.yml`) now uploads Bukkit files via `itsmeow/curseforge-upload@v3` against the `minecraft` endpoint, reusing `DBO_UPLOAD_API_TOKEN`, and prefixes `GAME_VERSIONS` as `1:<ver>` so CurseForge selects the Bukkit-compatible type-1 version entries.
|
||||
- New classes should be written in Kotlin by default where practical, matching `.github/CONTRIBUTING.md`.
|
||||
|
||||
## Test harness shortcuts (known non-realistic paths)
|
||||
- Several integration tests manually construct and fire Bukkit events rather than triggering real in-world actions:
|
||||
- `GoldenAppleIntegrationTest` (manual `PlayerItemConsumeEvent` and `PrepareItemCraftEvent`)
|
||||
- `OldPotionEffectsIntegrationTest` (manual `PlayerItemConsumeEvent`, `PlayerInteractEvent`, `BlockDispenseEvent`)
|
||||
- `OldArmourDurabilityIntegrationTest` (manual `PlayerItemDamageEvent`, `EntityDamageEvent`)
|
||||
- `PlayerKnockbackIntegrationTest` (manual `EntityDamageByEntityEvent`, `PlayerVelocityEvent`)
|
||||
- `SwordBlockingIntegrationTest` (manual `PlayerInteractEvent`)
|
||||
- `SwordSweepIntegrationTest` (manual `EntityDamageByEntityEvent`)
|
||||
- Some tests directly invoke module handlers instead of going through the event bus:
|
||||
- `PlayerKnockbackIntegrationTest` (direct `module.onEntityDamageEntity`)
|
||||
- `SwordSweepIntegrationTest` (direct `module.onEntityDamaged`)
|
||||
- `AttributeModifierCompat` synthesises a fallback attack-damage modifier from `NewWeaponDamage` when API attributes are missing.
|
||||
- Fake player implementations use simulated login/network plumbing (EmbeddedChannel + manual login/join/quit events), not a real networked client.
|
||||
- FakePlayer uses a plain `EmbeddedChannel` (not an anonymous subclass) so PacketEvents treats it as fake and does not disallow login; dummy `decoder`/`encoder` handlers are still added to the pipeline.
|
||||
- FakePlayer now schedules a manual NMS tick for non-legacy servers (prefers `doTick`, then `tick`, falls back to `baseTick`) to drive vanilla ticking like fire and passive effects.
|
||||
- FakePlayer tick shim invokes `baseTick` whenever `remainingFireTicks > 0` (burning) to ensure fire tick damage events still occur on Paper 1.21+ (which can short-circuit `doTick`/`tick` for fake players). When the fake player is in water, it prefers `doTick`/`tick` so the server can properly clear fire ticks (extinguish) before any fire damage is applied.
|
||||
- FakePlayer now prefers `PlayerList.placeNewPlayer` over the legacy `load`/manual list insertion path to better mirror vanilla login initialisation (helps player fire-tick damage on modern servers).
|
||||
- FakePlayer does not emulate fire-tick damage; fire ticks should be driven by the NMS tick path.
|
||||
- FakePlayer now forces a world add when the Bukkit world does not report the fake player entity after `placeNewPlayer` to keep PvP interactions reliable.
|
||||
- FakePlayer now clears invulnerability/instabuild abilities after spawn (plus a legacy fallback) to improve PvP interactions between fake players.
|
||||
- EntityDamageByEntityListener now logs extra debug about lastDamage restoration for non-entity damage, and documents the vanilla 1.12 damage flow in checkOverdamage.
|
||||
- EntityDamageByEntityListener no longer overwrites the stored last-damage baseline when cancelling “fake overdamage” (e.g. cancelled fire tick during invulnerability), preventing subsequent hits from incorrectly bypassing immunity.
|
||||
- Stored last-damage baselines now use a single lightweight expiry sweeper (tick-based TTL) instead of scheduling one Bukkit task per damage event; this keeps the hot path allocation-free. Expiry is monotonic (only extended, never shortened) and has a small minimum TTL to tolerate `maximumNoDamageTicks = 0`.
|
||||
- `WeaponDurabilityIntegrationTest` now uses a Zombie victim (fake-player attacker) and prefers the Bukkit `Player#attack` API before falling back to reflective NMS attack resolution, to make hit delivery reliable on modern servers.
|
||||
- `ModuleSwordSweepParticles` and `ModuleAttackSounds` now use PacketEvents listeners/wrappers instead of ProtocolLib.
|
||||
- `PacketCancellationIntegrationTest` now builds a `PacketSendEvent` directly (reflection) from the transformed buffer, sets the packet id/type explicitly, and dispatches it via the PacketEvents `eventManager` so module listeners can cancel reliably.
|
||||
- README now includes a licence note: source remains MPL‑2.0, but pre-built jars bundling PacketEvents are distributed under GPLv3; builds without PacketEvents can remain MPL‑2.0.
|
||||
- Legacy fake player (1.9) now uses a plain `EmbeddedChannel` with dummy `decoder`/`encoder` handlers, mirroring the modern fake player setup so PacketEvents treats it as fake.
|
||||
- `ModuleChorusFruit` now reimplements the chorus teleport search (16 attempts, world-border aware, passable feet/head, solid ground) for custom teleport distances; falls back to vanilla target if no safe spot found.
|
||||
- Added `ChorusFruitIntegrationTest` (in KotestRunner list) to assert custom chorus teleport distance lands on a safe block within the configured radius.
|
||||
- Chorus fruit safety test now handles legacy 1.12 by using solid/non-solid checks when `Block#isPassable` is absent; passes on 1.12, 1.19.2, and 1.21.11.
|
||||
- `ModuleOldToolDamage` now supports configurable TRIDENT (melee), TRIDENT_THROWN, and MACE damage; mace preserves its vanilla fall bonus while overriding base damage. New defaults added to `old-tool-damage.damages`.
|
||||
- Added `ModuleAttackRange` (Paper 1.21.11+ only) to apply a configurable attack_range data component (default 1.8-like: 0–3 range, creative 0–4, margin 0.1, mob factor 1.0); auto-disables on Spigot/older versions. `attack-range` module listed in `disabled_modules` by default.
|
||||
- `ModuleSwordBlocking` now only strips the Paper `CONSUMABLE` component from sword items, preventing food and other consumables from inheriting a `!minecraft:consumable` patch when inventory events fire on 1.20.5+.
|
||||
- Added `DisableOffhandReflectionIntegrationTest` (in `KotestRunner` list) to ensure reflective access to `InventoryView#getBottomInventory`/`getTopInventory` works on non-public CraftBukkit view implementations.
|
||||
- `Reflector.getMethod` overloads now include declared methods and call `setAccessible(true)` to avoid `IllegalAccessException` when CraftBukkit uses non-public view classes (e.g. `CraftContainer$1` on 1.20.1).
|
||||
- Added `AttackRangeIntegrationTest` (1.21.11+) to assert vanilla hits at ~3.6 blocks and 1.8-style attack_range reduces reach so the same hit misses; registered in `KotestRunner`.
|
||||
- Removed the cancelled S3-only `AttackRangeIntegrationTest` case `swap hand keeps attack-range off offhand sword (Paper 1.21.11+)` so ongoing S6 validation is not blocked by a parked slice artefact.
|
||||
- InvulnerabilityDamageIntegrationTest adds a case asserting environmental damage above the baseline applies during invulnerability (manual EntityDamageEvent).
|
||||
- `gradle.properties` gameVersions list now includes 1.21.11 down to 1.21.1 (plus 1.21) ahead of existing entries.
|
||||
- GitHub release asset now keeps a stable filename `OldCombatMechanics.jar` (no version suffix); the CurseForge upload uses the same path.
|
||||
- Expanded `SwordBlockingIntegrationTest` to cover right-click blocking, non-sword handling, offhand restoration (hotbar change and drop cancel), permission gating, and preserving an existing real shield.
|
||||
- Added `PaperSwordBlockingDamageReductionIntegrationTest` (in KotestRunner list) to regression-test that Paper sword blocking is recognised server-side (via `ModuleSwordBlocking.isPaperSwordBlocking`) and produces a non-zero 1.8-style reduction from `applyPaperBlockingReduction`. This uses a synthetic damage event to avoid flakiness from mob AI / PvP settings.
|
||||
- Paper-only sword blocking uses a runtime-gated helper (`kernitus.plugin.OldCombatMechanics.paper.PaperSwordBlocking`) that applies consumable + blocking components via cached reflection (lookup once, MethodHandle invoke thereafter). 1.8-style reduction is applied via `EntityDamageByEntityListener` using the `BLOCKING` damage modifier, while legacy/non-Paper keeps the shield swap path.
|
||||
- Fixed legacy (1.9.x) sweep detection flakiness by tracking priming per-attacker UUID (not `Location`, which is unstable due to yaw/pitch) and clearing the set on module reload; stabilises `SwordSweepIntegrationTest` on 1.9.4.
|
||||
- `ModuleSwordSweep` now uses a one-shot next-tick clear (single scheduled task guarded by `pendingClearTask`) instead of a repeating every-tick task, to avoid doing any work on ticks where no sword hits occurred.
|
||||
- `ModuleSwordBlocking` legacy (offhand shield) path no longer schedules per-player repeating tasks; it now uses a single shared tick runner with per-player tick deadlines (10-tick warmup + 2-tick poll + restoreDelay), which reduces task churn under heavy right-click spam.
|
||||
- `ModuleLoader` now clears the static module list on initialise to prevent duplicate registrations after hot reloads.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage to assert the Paper sword-blocking consumable cleaner leaves swords untouched when the module is disabled (modeset/world) and does not mutate swords when no component change is required.
|
||||
- `ConsumableComponentIntegrationTest` now seeds and asserts the CONSUMABLE component via NMS reflection (not Paper API) and uses standalone CraftItemStacks for cursor/current items to avoid classloader mismatches and fake-player cursor side effects.
|
||||
- `ModuleSwordBlocking#onModesetChange` now strips the Paper CONSUMABLE component from the player’s main hand/offhand and stored swords when sword-blocking is disabled for that player, preventing component “taint” lingering after mode changes.
|
||||
- `ModuleSwordBlocking#reload` now strips Paper sword-blocking consumable components from online players when the module is disabled globally (disabled_modules) to avoid lingering taint after config reloads.
|
||||
- `ModuleSwordBlocking` now gates the Paper consumable animation by PacketEvents client version (>=1.20.5); older clients fall back to the offhand shield and unknown versions default to the animation path.
|
||||
- `config.yml` now documents that Paper 1.20.5+ uses the consumable-based sword-blocking animation, with older/Paperless servers falling back to an offhand shield.
|
||||
- `config.yml` now notes that ViaVersion clients older than 1.20.5 also fall back to the shield behaviour.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage for disabling sword-blocking via `disabled_modules` and asserting the consumable component is cleared after config reload.
|
||||
- Extended `ConsumableComponentIntegrationTest` to cover disabled-module right-click suppression, reload toggling, stored-inventory cleanup, offhand stability, and modeset-change behaviour after a disabled reload.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage for forcing an older client version and asserting sword-blocking falls back to an offhand shield without applying consumable components.
|
||||
- `ConsumableComponentIntegrationTest` now uses PacketEvents reflection to seed a User/client version for fake players when PacketEvents has not registered one yet.
|
||||
- `ModuleSwordBlocking#restore` no longer skips offhand restoration just because Paper support is present; older clients using the shield fallback now restore their original offhand item correctly.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage asserting the older-client shield fallback restores the offhand item on hotbar change.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage for inventory-glitch regressions: unknown PacketEvents client versions should use shield fallback, middle-clicking custom GUIs should not mutate held sword components, and custom-GUI drag events should not rewrite top-inventory swords.
|
||||
- Those inventory-glitch regressions now pass on 1.21.11 after the unknown-client fallback + click/drag scope fixes.
|
||||
- `ModuleSwordBlocking#supportsPaperAnimation` now treats unknown PacketEvents client versions as shield-fallback only when a PacketEvents `User` exists; if no user is registered yet (early login/synthetic test player), it keeps animation support to avoid regressing normal modern-client behaviour.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryClickPost` now re-applies the main-hand consumable component only for click paths that can actually affect the selected hotbar slot (selected NUMBER_KEY swaps or direct selected-slot player-inventory clicks), and ignores middle-clicks.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryDrag` now ignores top-inventory raw slots and skips main-hand re-application when the drag only touched top inventory slots, preventing custom-GUI slot rewrites.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage for legacy fallback shield-guard scope: custom GUI shield-icon clicks and unrelated shield drops should not be cancelled while fallback is active; temporary offhand shield swap blocking should remain enforced.
|
||||
- `ModuleSwordBlocking#onInventoryClick` legacy shield-guard scope now only cancels direct offhand temporary-shield interactions (player inventory slot 40), avoiding cancellation of unrelated custom-GUI shield clicks.
|
||||
- `ModuleSwordBlocking#onInventoryClick` also blocks `ClickType.SWAP_OFFHAND` while temporary legacy shield state is active, preventing offhand shield extraction via inventory swap-clicks.
|
||||
- `ModuleSwordBlocking#onItemDrop` no longer cancels shield drops while legacy fallback state is active; it now force-restores the temporary shield state immediately to avoid trapping unrelated shield drops.
|
||||
- `ModuleSwordBlocking#isPlayerBlocking` now requires an actual offhand shield before treating `isBlocking`/`isHandRaised` as legacy shield-blocking, preventing stale hand-use state from suppressing fallback shield injection.
|
||||
- `ModuleSwordBlocking#supportsPaperAnimation` now falls back to `User#getClientVersion` when `PlayerManager#getClientVersion` is null, improving old-client fallback stability in synthetic/integration scenarios.
|
||||
- `ModuleSwordBlocking#supportsPaperAnimation` now fails safe to legacy shield fallback when PacketEvents client-version resolution is unavailable (resolver initialisation missing, resolver methods/objects null, or reflection errors), while preserving existing behaviour for normal early-login/synthetic-player cases.
|
||||
- `ModuleSwordBlocking#onPlayerSwapHandItems` now treats stale legacy stored-shield state as non-authoritative for Paper-animation players: it clears stale legacy entries instead of cancelling swaps, so synthetic swap listeners still run.
|
||||
- `ModuleSwordBlocking#onPlayerSwapHandItems` now restores any stale legacy stored offhand item (via `restore(..., true)`) before clearing legacy state on the Paper-animation path, so stale entries are not silently discarded.
|
||||
- `ConsumableCleaner#onSwap` now snapshots the held hotbar slot and only reapplies the Paper consumable component when the slot is unchanged on the deferred tick; offhand stripping remains deferred as before.
|
||||
- The new legacy-scope regressions now pass on 1.19.2 and 1.21.11.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryClickPost` now snapshots click context (held slot and open inventory top/bottom) and skips next-tick reapply when that context is stale, preventing deferred reapply from tainting a newly selected main-hand sword.
|
||||
- Added `ConsumableComponentIntegrationTest` coverage for stale `PlayerSwapHandItemsEvent` deferred reapply: if held slot changes before next tick, the newly selected main-hand sword must not gain a consumable component, while swapped-offhand sword cleanup still occurs.
|
||||
- Added `ConsumableComponentIntegrationTest` regression coverage for stale `PlayerSwapHandItemsEvent` deferred reapply when the open inventory view changes before next tick: the new main-hand context must not be tainted, while swapped-offhand consumable cleanup must still run.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onSwap` now snapshots open-inventory top/bottom identity at swap time and requires a view match only for deferred main-hand reapply; deferred offhand consumable cleanup still runs even when the view changed.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner` click/drag handlers now follow a minimal-mutation policy (no proactive consumable strip/apply in `InventoryClickEvent` or `InventoryDragEvent` paths); cleanup is handled opportunistically via lifecycle/transition handlers (`onModesetChange`/`reload`/world-change, held-slot, and swap paths) using a shared per-player consumable sweep helper.
|
||||
- `ModuleSwordBlocking#onModesetChange` now force-restores stale legacy fallback shield state (`restore(..., true)`) when sword-blocking becomes disabled for a player, before sweeping Paper consumable components.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onWorldChange` now strips consumable components from main hand, offhand, and stored swords without re-applying, so world changes clear stale consumable state consistently.
|
||||
- `ModuleSwordBlocking.ConsumableCleaner#onQuit` now uses the same strip-only full sweep as world-change cleanup (main hand, offhand, and stored swords), ensuring logout clears stale consumable state from storage as well.
|
||||
- `ModuleSwordBlocking` now handles `PlayerJoinEvent` with a force legacy restore (`restore(..., true)`) followed by strip-only consumable cleanup (main hand, offhand, and stored swords), so join clears stale state without re-applying consumable components.
|
||||
- `ModuleSwordBlocking` inner listener `ConsumableCleaner` was renamed to `ConsumableLifecycleHandler` (registration updated; behaviour unchanged).
|
||||
- Added `sword-blocking.paper-animation` config (default `true`) to hard-disable the Paper consumable animation path; when false, sword-blocking always uses the legacy shield fallback and reload/lifecycle cleanup strips stale consumable state without re-applying it.
|
||||
- Legacy sword-blocking now marks injected temporary offhand shields when marker APIs are available so death handling can identify the temporary drop path reliably.
|
||||
- `ModuleSwordBlocking#onPlayerDeath` now pops stored offhand exactly once, clears legacy state deterministically, respects `keepInventory` without rewriting drops, rewrites at most one temporary shield drop, and adds the stored offhand drop when no temporary shield drop is found.
|
||||
- Legacy shield-drop reconciliation now uses a strict safety-first fallback when marker APIs are unavailable: it does not guess temporary shields from plain shield shape, avoiding accidental replacement of legitimate shields (stored offhand is appended instead).
|
||||
- For synthetic/manual death events where drop metadata may not preserve the temporary-shield marker, `ModuleSwordBlocking#onPlayerDeath` allows a single shield-drop rewrite only when the player offhand was verified as marker-tagged at death time.
|
||||
- `ModuleAttackCooldown` now supports `disable-attack-cooldown.held-item-attack-speeds.<MATERIAL>` overrides with XMaterial/Material matching, falls back to `generic-attack-speed`, reconciles on join/world/modeset/hotbar/swap, restores vanilla base ATTACK_SPEED 4.0 when disabled, and no longer calls `player.saveData()` on attack-speed updates. The bundled config defaults keep most items on the no-cooldown fallback, with `TRIDENT`, `MACE`, and spear variants listed as the built-in slower exceptions; keys for newer-version materials are ignored safely on older servers.
|
||||
- `ModuleAttackCooldown` hotbar/swap handling is immediate best-effort only: at `EventPriority.HIGHEST`, it re-reads the live inventory only when the event is already cancelled, otherwise it trusts the event payload and applies straight away; later plugin mutations may win until the next ordinary reconcile trigger.
|
||||
- `.github/CONTRIBUTING.md` now states that contributions should include automated coverage within the existing test framework where practical, and that new classes should be written in Kotlin by default.
|
||||
## Integration test essentials
|
||||
|
||||
## Fire aspect / fire tick test notes
|
||||
- `FireAspectOverdamageIntegrationTest` now uses a Zombie victim for real fire tick sampling, with max health boosted (via MAX_HEALTH attribute) to survive rapid clicking.
|
||||
- The first two tests fire a synthetic `EntityDamageEvent` with `FIRE_TICK` to control timing and make the baseline check deterministic.
|
||||
- Paper 1.12 applies attack-cooldown scaling before the Bukkit damage event fires; fake players can start with a low initial cooldown, producing a weak first hit and allowing a stronger second hit as legitimate “overdamage”. `FireAspectOverdamageIntegrationTest` now waits a few ticks before the first attack to make the first hit stable on 1.12.
|
||||
- Added extra fire edge-case coverage in `FireAspectOverdamageIntegrationTest`: fire resistance + fire-immune victim rapid-click parity, water extinguish behaviour (no fire tick damage while submerged), fire protection vs protection comparisons (uses a Zombie victim for consistency), and alternating attackers during invulnerability.
|
||||
- Legacy (1.12) fake player behaviour differs for player-specific fire tick sampling, so the player afterburn-vs-environmental comparisons are no-ops on legacy; the Zombie variants still run across all versions.
|
||||
- Tests run inside a real Paper server started by the Gradle `run-paper` plugin.
|
||||
- RunServer output is redirected to `build/integration-test-logs/<version>.log`; avoid reading those logs without explicit user approval.
|
||||
- Kotlin tests use Kotest 6 for Java 11+ server targets (`KotestRunner`, `KotestProjectConfig`).
|
||||
- Java 8 targets use `LegacyTestRunner`, currently a smoke-test path rather than the full Kotest suite.
|
||||
- Several integration tests intentionally use synthetic Bukkit events or direct module handler calls. Consult `integration-test-verification` before assuming a test represents a real in-world action.
|
||||
|
||||
## Historical notes index
|
||||
|
||||
- Detailed relocated history from the former long `AGENTS.md` lives in `.agents/skills/integration-test-verification/references/relocated-agents-notes.md`.
|
||||
- Integration harness, fake-player, PacketEvents, and test-shortcut details: `integration-test-verification` and its references.
|
||||
- Compatibility, Java 8, reflection, NMS, and version strategy details: `compatibility-strategy`.
|
||||
- Module configuration, modeset, migration, and option-change details: `module-config-change`.
|
||||
- Publishing, release workflow, licence, and asset notes: `release-readiness-review`.
|
||||
- PR summary templates and reviewer-facing change grouping: `pr-draft-summary`.
|
||||
|
||||
## TDAID reminders (this repo)
|
||||
|
||||
- Plan → Red → Green → Refactor → Validate.
|
||||
- Red phase: only touch tests. Do not modify production code.
|
||||
- Green phase: only touch production code. Do not modify tests.
|
||||
- Refactor phase: cleanups only; keep behavior unchanged and tests green.
|
||||
- Validate phase: rerun tests and do a human sanity check before declaring done.
|
||||
- Red phase: only touch tests.
|
||||
- Green phase: only touch production code.
|
||||
- Refactor phase: clean-ups only; keep behaviour unchanged and tests green.
|
||||
- Validate phase: rerun the narrowest relevant tests and do a human sanity check before declaring done.
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/run-integration-matrix.sh [-v versions] [-s spec-filter] [-t test-filter] [--] [extra Gradle args...]
|
||||
|
||||
Wraps ./gradlew integrationTest using repository-relative paths.
|
||||
|
||||
Options:
|
||||
-v, --versions LIST Comma-separated Minecraft versions, for example 1.19.2,1.21.11
|
||||
-s, --spec-filter TEXT Kotest spec filter passed as -Dkotest.filter.specs=TEXT
|
||||
-t, --test-filter TEXT Kotest test filter passed as -Dkotest.filter.tests=TEXT
|
||||
-h, --help Show this help text
|
||||
|
||||
This command may start Paper servers. For a non-invasive check, use
|
||||
scripts/validate-module-assignments.sh or scripts/summarise-integration-results.sh.
|
||||
USAGE
|
||||
}
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
versions=
|
||||
spec_filter=
|
||||
test_filter=
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-v|--versions)
|
||||
[ "$#" -ge 2 ] || { printf '%s\n' "Missing value for $1" >&2; usage >&2; exit 2; }
|
||||
versions=$2
|
||||
shift 2
|
||||
;;
|
||||
-s|--spec-filter)
|
||||
[ "$#" -ge 2 ] || { printf '%s\n' "Missing value for $1" >&2; usage >&2; exit 2; }
|
||||
spec_filter=$2
|
||||
shift 2
|
||||
;;
|
||||
-t|--test-filter)
|
||||
[ "$#" -ge 2 ] || { printf '%s\n' "Missing value for $1" >&2; usage >&2; exit 2; }
|
||||
test_filter=$2
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
printf '%s\n' "Unknown option: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- integrationTest "$@"
|
||||
[ -z "$versions" ] || set -- "$@" "-PintegrationTestVersions=$versions"
|
||||
[ -z "$spec_filter" ] || set -- "$@" "-Dkotest.filter.specs=$spec_filter"
|
||||
[ -z "$test_filter" ] || set -- "$@" "-Dkotest.filter.tests=$test_filter"
|
||||
|
||||
printf '%s\n' "Repository: $repo_root"
|
||||
printf '%s\n' "Running: ./gradlew $*"
|
||||
cd "$repo_root"
|
||||
exec ./gradlew "$@"
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/suggest-integration-matrix.sh [-h] [changed-path ...]
|
||||
|
||||
Suggests a small integration-test matrix from changed paths. With no paths, it
|
||||
tries git diff --name-only. Suggestions are conservative hints, not a quality
|
||||
gate.
|
||||
USAGE
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
if command -v git >/dev/null 2>&1 && git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
set -- $(git -C "$repo_root" diff --name-only -- src src/integrationTest build.gradle.kts gradle.properties || true)
|
||||
fi
|
||||
fi
|
||||
|
||||
versions="1.19.2,1.21.11"
|
||||
reason="default modern coverage for uncertain changes"
|
||||
filters=""
|
||||
|
||||
for path in "$@"; do
|
||||
case "$path" in
|
||||
src/main/resources/config.yml|*config*|*modeset*|*Module*)
|
||||
versions="1.12,1.19.2,1.21.11"
|
||||
reason="config or module assignment changes benefit from legacy and modern coverage"
|
||||
filters="-Dkotest.filter.specs=*Module*,*Config*,*Modeset*"
|
||||
;;
|
||||
*Packet*|*packet*|*FakePlayer*|*fake-player*)
|
||||
versions="1.19.2,1.21.11"
|
||||
reason="packet or fake-player areas need modern Paper/PacketEvents coverage"
|
||||
;;
|
||||
*reflection*|*nms*|*legacy*|*1_12*)
|
||||
versions="1.12,1.19.2"
|
||||
reason="legacy or reflective code should include the Java 8-era path"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
printf 'Recommended versions: %s\n' "$versions"
|
||||
printf 'Reason: %s\n' "$reason"
|
||||
[ -z "$filters" ] || printf 'Suggested filter: %s\n' "$filters"
|
||||
printf 'Run with: scripts/run-integration-matrix.sh -v %s' "$versions"
|
||||
[ -z "$filters" ] || printf ' -- %s' "$filters"
|
||||
printf '\n'
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/summarise-integration-results.sh [-h]
|
||||
|
||||
Reports compact integration-test outcomes from run/* plugin result files.
|
||||
It deliberately does not open build/integration-test-logs/*.log. If compact
|
||||
results are insufficient, ask explicitly before inspecting full server logs.
|
||||
USAGE
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
'') ;;
|
||||
*)
|
||||
printf '%s\n' "Unknown option: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
found=0
|
||||
failed=0
|
||||
|
||||
for run_dir in "$repo_root"/run/*; do
|
||||
[ -d "$run_dir" ] || continue
|
||||
version=${run_dir##*/}
|
||||
result_file=$run_dir/plugins/OldCombatMechanicsTest/test-results.txt
|
||||
failures_file=$run_dir/plugins/OldCombatMechanicsTest/test-failures.txt
|
||||
|
||||
if [ ! -f "$result_file" ] && [ ! -f "$failures_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
found=1
|
||||
result="MISSING"
|
||||
[ ! -f "$result_file" ] || result=$(tr -d '\r' < "$result_file" | sed '/^[[:space:]]*$/d' | sed -n '1p')
|
||||
|
||||
printf '%s: %s\n' "$version" "$result"
|
||||
if [ "$result" != "PASS" ]; then
|
||||
failed=1
|
||||
fi
|
||||
|
||||
if [ -s "$failures_file" ]; then
|
||||
sed 's/^/ /' "$failures_file"
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$found" -eq 0 ]; then
|
||||
printf '%s\n' 'No compact integration-test result files found under run/*/plugins/OldCombatMechanicsTest/.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit "$failed"
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/validate-module-assignments.sh [-h] [config-file]
|
||||
|
||||
Best-effort static validation for bundled module assignment lists.
|
||||
Checks that every listed module is assigned to only one configurable top-level
|
||||
category: always_enabled_modules, disabled_modules, or the aggregate modesets
|
||||
category. It also checks duplicate entries within the same exact list or
|
||||
modeset, and that internal modules are not listed in those configurable
|
||||
sections. Reusing a module in more than one modeset is allowed because modesets
|
||||
are alternative player modes.
|
||||
|
||||
The default config file is src/main/resources/config.yml, resolved relative to
|
||||
the repository root. This script does not start servers.
|
||||
USAGE
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
config_file=${1:-"$repo_root/src/main/resources/config.yml"}
|
||||
|
||||
python3 - "$config_file" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
config = Path(sys.argv[1])
|
||||
if not config.is_file():
|
||||
print(f"Config file not found: {config}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
internal = {"modeset-listener", "attack-cooldown-tracker", "entity-damage-listener"}
|
||||
assignments = defaultdict(list)
|
||||
section = None
|
||||
modeset = None
|
||||
|
||||
top_level = re.compile(r"^([A-Za-z0-9_.-]+):\s*(?:#.*)?$")
|
||||
mode_name = re.compile(r"^ ([A-Za-z0-9_.-]+):\s*(?:#.*)?$")
|
||||
list_item = re.compile(r"^\s*-\s*['\"]?([^'\"#\s]+)['\"]?\s*(?:#.*)?$")
|
||||
|
||||
for number, raw in enumerate(config.read_text(encoding="utf-8").splitlines(), 1):
|
||||
top = top_level.match(raw)
|
||||
if top:
|
||||
section = top.group(1)
|
||||
modeset = None
|
||||
continue
|
||||
|
||||
if section == "modesets":
|
||||
mode = mode_name.match(raw)
|
||||
if mode:
|
||||
modeset = mode.group(1)
|
||||
continue
|
||||
|
||||
item = list_item.match(raw)
|
||||
if not item:
|
||||
continue
|
||||
|
||||
module = item.group(1)
|
||||
if section == "always_enabled_modules":
|
||||
category = "always_enabled_modules"
|
||||
bucket = "always_enabled_modules"
|
||||
elif section == "disabled_modules":
|
||||
category = "disabled_modules"
|
||||
bucket = "disabled_modules"
|
||||
elif section == "modesets" and modeset:
|
||||
category = "modesets"
|
||||
bucket = f"modesets.{modeset}"
|
||||
else:
|
||||
continue
|
||||
assignments[module].append((category, bucket, number))
|
||||
|
||||
errors = []
|
||||
for module, locations in sorted(assignments.items()):
|
||||
if module in internal:
|
||||
for _category, bucket, number in locations:
|
||||
errors.append(f"internal module listed: {module} in {bucket} at line {number}")
|
||||
exact_locations = defaultdict(list)
|
||||
category_locations = defaultdict(list)
|
||||
for category, bucket, number in locations:
|
||||
exact_locations[bucket].append(number)
|
||||
category_locations[category].append((bucket, number))
|
||||
if len(category_locations) > 1:
|
||||
where = ", ".join(
|
||||
f"{bucket}:line {number}"
|
||||
for _category, bucket, number in locations
|
||||
)
|
||||
errors.append(f"conflicting module assignment: {module} in {where}")
|
||||
for bucket, numbers in sorted(exact_locations.items()):
|
||||
if len(numbers) > 1:
|
||||
where = ", ".join(f"line {number}" for number in numbers)
|
||||
errors.append(f"duplicate module assignment within {bucket}: {module} at {where}")
|
||||
|
||||
if errors:
|
||||
print("Module assignment validation FAILED:")
|
||||
for error in errors:
|
||||
print(f"- {error}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Module assignment validation passed for {config}")
|
||||
print(f"Checked {len(assignments)} assigned module entr{'y' if len(assignments) == 1 else 'ies'}.")
|
||||
PY
|
||||
Reference in new issue
Block a user