Skip to content

Commit bd38813

Browse files
committed
feat: add safe admin score fast-forward
1 parent c7e7eab commit bd38813

28 files changed

Lines changed: 1132 additions & 110 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
This changelog records source milestones. A listed feature is not production approval; release evidence and the Paper/live-data acceptance result belong in [checklist-walktheplank.md](checklist-walktheplank.md).
44

5+
## [2.8.4-029] — 2026-08-03
6+
7+
Expected artifact: `1MB-WalkThePlank-v2.8.4-029-j25-26.2.jar`
8+
9+
### Non-scoring high-score reproduction
10+
11+
- Added the separately permissioned `/walk admin test fast-forward <target-score>` workflow for reproducing long-run behavior without requiring an operator to make hundreds of manual jumps. Administrative testing is disabled by default and bounded by `adminTesting.maximumTargetScore`.
12+
- Fast-forwarding replays the real platform planner, durability journal, restoration, block-lease, and successor pipeline one transition at a time on the primary thread while file durability remains on its bounded worker. It waits for each predecessor cleanup and the next durable successor instead of faking a score counter.
13+
- The run is irreversibly marked as an administrative test before the first synthetic transition. Test runs cannot update Classic, season, Combo, or Flawless results; cannot prepare or dispatch rewards; and settle as an audited `ABORTED` run without a score.
14+
- Normal movement handling and cosmetic feedback are suppressed only while synthetic transitions are in flight. Public jump/completion events and normal jump metrics remain suppressed for the entire marked test run, preventing unaware integrations from treating its manual tail as genuine gameplay. At the target score the player is placed on the exact current platform with a ready successor and resumes ordinary manual movement, allowing a direct 110-to-111-to-112 reproduction.
15+
- Source review found no explicit score-111 ceiling. The planner changes difficulty at score 100 and otherwise continues; this controlled workflow is intended to distinguish a genuine cumulative durability/world-placement failure from an ordinary fall on the harder post-100 jumps. No database-schema or historical-leaderboard change is included.
16+
517
## [2.8.3-028] — 2026-07-28
618

719
Expected artifact: `1MB-WalkThePlank-v2.8.3-028-j25-26.2.jar`

README.md

Lines changed: 34 additions & 16 deletions
Large diffs are not rendered by default.

checklist-walktheplank.md

Lines changed: 27 additions & 20 deletions
Large diffs are not rendered by default.

feature-improvements-walktheplank.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
# WalkThePlank future improvements and release status
22

3-
This is the authoritative future-development TODO and release-status document. It separates what is implemented through source release **v2.8.3 build 028** from what still needs server acceptance or future design work. “Implemented” means the behavior is present in source; the table or checklist identifies whether automated or Paper/live-data evidence still remains. It does not mean the summer-event candidate is production-approved. The release candidate still has to pass [checklist-walktheplank.md](checklist-walktheplank.md).
3+
This is the authoritative future-development TODO and release-status document. It separates what is implemented through source release **v2.8.4 build 029** from what still needs server acceptance or future design work. “Implemented” means the behavior is present in source; the table or checklist identifies whether automated or Paper/live-data evidence still remains. It does not mean the summer-event candidate is production-approved. The release candidate still has to pass [checklist-walktheplank.md](checklist-walktheplank.md).
44

55
Status labels:
66

7-
- **Implemented** — present in the current build-028 source.
7+
- **Implemented** — present in the current build-029 source.
88
- **Implemented; beta verification pending** — present, but the final JAR still needs the named Paper/live-data test.
99
- **Partial** — a safe foundation exists, but an important workflow or assurance remains.
1010
- **Proposed** — not present and must not be advertised as a current feature.
1111

1212
## Deferred modernization queue
1313

14-
Build 003 remains the historical modernization baseline, build 004 the event-safety release, build 005 the destructive-testing foundation, build 006 the off-main durability release, and build 007 the command/API modernization release. Build 008 added bounded migration-backup retention, milestones/accessibility, separate Combo/Flawless categories, rate-limited movement anomaly evidence, and a verified audit chain. Builds 009–022 culminated in a successful real-player score-9 run and approved summer appearance. Builds 023–026 added optional queue reservation, event themes/sounds, paged GUI status, and atomic in-game event control. Build 027 added UUID-first dot-prefixed Floodgate display metadata and schema v4 without changing Classic ownership. Build 028 refreshes the maintained PaperScript/server profile, exact stable Paper API/runtime floor, Java 25 tooling, embedded release metadata, and drift verification without changing gameplay or persistence. Every remaining unchecked feature stays deferred so its evidence cannot be confused with this candidate.
14+
Build 003 remains the historical modernization baseline, build 004 the event-safety release, build 005 the destructive-testing foundation, build 006 the off-main durability release, and build 007 the command/API modernization release. Build 008 added bounded migration-backup retention, milestones/accessibility, separate Combo/Flawless categories, rate-limited movement anomaly evidence, and a verified audit chain. Builds 009–022 culminated in a successful real-player score-9 run and approved summer appearance. Builds 023–026 added optional queue reservation, event themes/sounds, paged GUI status, and atomic in-game event control. Build 027 added UUID-first dot-prefixed Floodgate display metadata and schema v4 without changing Classic ownership. Build 028 refreshed the maintained PaperScript/server profile, exact stable Paper API/runtime floor, Java 25 tooling, embedded release metadata, and drift verification. Build 029 adds an explicitly enabled, separately permissioned, irreversibly non-scoring administrative fast-forward that replays genuine platform lifecycle transitions for controlled high-score reproduction without changing the database schema or historical leaderboards. Every remaining unchecked feature stays deferred so its evidence cannot be confused with this candidate.
1515

1616
1. [x] **v2.1.1 build 004 — event-safety and operations implemented; beta acceptance still pending.** External-teleport decisions occur at `HIGHEST` with observation-only `MONITOR` and next-tick verification; GUI sessions bind owner UUID/nonce/generation/exact inventory with one pending action; trusted formatting is parsed separately from literal dynamic components with explicit `minimessage:` opt-in and legacy `&` compatibility; `/walk admin doctor` produces a privacy-safe report with an asynchronous SQLite probe.
1717
2. [x] **v2.1.2 build 005 — disposable Paper integration and fault-injection harness implemented; final-candidate and real-client evidence pending.** A separate test plugin, Java 25 Class-File API instrumented-copy builder, 24 named failpoints, two-start/PlaceholderAPI/lifecycle/log runner, real-player queue/teleport/GUI/reconnect/exit probes, reflection event contracts, and positive/negative production-isolation checks are present. Development-artifact runs passed the automated two-start profile and the `config.after_runtime_commit` exit-97/recovery profile; repeat them against the clean committed candidate and complete every applicable real-client/hard-kill checklist row before treating this item as release-qualified.
@@ -46,7 +46,7 @@ Build 008 includes all previous modernization layers documented in [CHANGELOG.md
4646
| Capability | Status | Notes |
4747
| --- | --- | --- |
4848
| Gradle build and wrapper | **Implemented** | Gradle 9.6.1, Shadow 9.5.1, strict Java 25 compilation. |
49-
| Paper target | **Implemented; clean freeze rerun pending** | Exact compile API is `26.2.build.84-stable`; `api-version: 26.2` and an explicit `ServerBuildInfo` gate reject older lines and Paper-compatible runtimes below the separately embedded minimum build 84. The uncommitted build-028 artifact passed full-stack build-84 smoke on Java 25.0.4 and Java 26.0.2; repeat it after the clean freeze. |
49+
| Paper target | **Implemented; clean freeze rerun pending** | Exact compile API is `26.2.build.84-stable`; `api-version: 26.2` and an explicit `ServerBuildInfo` gate reject older lines and Paper-compatible runtimes below the separately embedded minimum build 84. Build 028 passed full-stack build-84 smoke on Java 25.0.4 and Java 26.0.2; repeat it for the clean build-029 candidate. |
5050
| Standalone SQLite JAR | **Implemented** | SQLite JDBC 3.53.2.0 is shaded; MySQL/MariaDB is absent. |
5151
| Archive-composition gate | **Implemented** | Rejects bundled Paper/Bukkit/PlaceholderAPI/live-database classes or files and remote-database drivers. |
5252
| Isolated scenario artifacts | **Implemented; clean-candidate rerun pending** | The harness is an independent Paper plugin and the Java 25 Class-File API transformer writes a distinct `TEST-ONLY-` copy. Neither is packaged beneath `build/libs/` or allowed to replace the production JAR. Build-028 development-artifact isolation and controlled-profile gates passed. |

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
pluginVersion=2.8.3
2-
buildNumber=028
1+
pluginVersion=2.8.4
2+
buildNumber=029
33
javaTarget=25
44
paperTarget=26.2
55
paperApiVersion=26.2.build.84-stable

src/main/java/com/mrfdev/walktheplank/command/CommandSupport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ boolean hasAnyAdminPermission(CommandSender sender) {
151151
|| sender.hasPermission(permissions.adminSeason())
152152
|| sender.hasPermission(permissions.adminExport())
153153
|| sender.hasPermission(permissions.adminReward())
154-
|| sender.hasPermission(permissions.adminInvestigate());
154+
|| sender.hasPermission(permissions.adminInvestigate())
155+
|| sender.hasPermission(permissions.adminTest());
155156
}
156157

157158
void deny(CommandSender sender, String permission) {

src/main/java/com/mrfdev/walktheplank/command/DatabaseCommands.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ private void showCommands(CommandSender sender) {
602602
support.sendLine(
603603
sender,
604604
"&7Staff: &f/walk admin open, reload, stop, recover, validate, "
605-
+ "arena, queue, season, export, reward, run, status, "
605+
+ "arena, queue, season, export, reward, run, test, status, "
606606
+ "debug, doctor");
607607
support.sendLine(
608608
sender,
@@ -638,6 +638,7 @@ private void showPermissions(CommandSender sender) {
638638
Map.entry("admin.season", permissions.adminSeason()),
639639
Map.entry("admin.export", permissions.adminExport()),
640640
Map.entry("admin.reward", permissions.adminReward()),
641+
Map.entry("admin.test", permissions.adminTest()),
641642
Map.entry(
642643
"admin.investigate",
643644
permissions.adminInvestigate())).entrySet()) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.mrfdev.walktheplank.command;
2+
3+
import com.mrfdev.walktheplank.game.GameManager.AdminTestStartResult;
4+
import java.util.Map;
5+
import org.bukkit.command.CommandSender;
6+
import org.bukkit.entity.Player;
7+
8+
/** Explicitly armed, self-only administrative gameplay reproduction controls. */
9+
final class TestCommands {
10+
private final CommandSupport support;
11+
12+
TestCommands(CommandSupport support) {
13+
this.support = support;
14+
}
15+
16+
void help(CommandSender sender) {
17+
if (!support.requireAdministrativePermission(
18+
sender, support.permissions().adminTest())) {
19+
return;
20+
}
21+
var testing = support.settings.get().adminTesting();
22+
support.sendHeader(sender, "Administrative gameplay test");
23+
support.sendField(sender, "Enabled", Boolean.toString(testing.enabled()));
24+
support.sendField(
25+
sender,
26+
"Maximum target",
27+
Integer.toString(testing.maximumTargetScore()));
28+
support.sendCommandHelp(
29+
sender,
30+
"/walk admin test fast-forward <target-score>",
31+
"Fast-forward your score-zero run without scores or rewards");
32+
}
33+
34+
void fastForward(CommandSender sender, int targetScore) {
35+
if (!support.requireAdministrativePermission(
36+
sender, support.permissions().adminTest())) {
37+
return;
38+
}
39+
Player player = support.requirePlayer(sender);
40+
if (player == null) {
41+
return;
42+
}
43+
44+
AdminTestStartResult result =
45+
support.games.startAdminTestFastForward(player, targetScore);
46+
switch (result) {
47+
case STARTED -> support.sendLine(
48+
sender,
49+
"&aStarted a non-scoring fast-forward toward score &f{{score}}&a. "
50+
+ "Wait for its completion message before jumping.",
51+
Map.of("score", targetScore));
52+
case DISABLED -> support.sendLine(
53+
sender,
54+
"&eAdministrative gameplay testing is disabled in config.");
55+
case NO_ACTIVE_RUN -> support.sendLine(
56+
sender,
57+
"&eStart a WalkThePlank run before using fast-forward.");
58+
case SCORE_NOT_ZERO -> support.sendLine(
59+
sender,
60+
"&eFast-forward is accepted only while the active run is still at score 0.");
61+
case ALREADY_RUNNING -> support.sendLine(
62+
sender,
63+
"&eAn administrative fast-forward is already in progress.");
64+
case TARGET_OUT_OF_RANGE -> support.sendLine(
65+
sender,
66+
"&eThe target must be between 1 and the configured maximum of &f{{maximum}}&e.",
67+
Map.of(
68+
"maximum",
69+
support.settings.get()
70+
.adminTesting()
71+
.maximumTargetScore()));
72+
}
73+
}
74+
}

src/main/java/com/mrfdev/walktheplank/command/WalkCommand.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
/**
2424
* Lifecycle-registered Brigadier command coordinator.
2525
*
26-
* <p>Domain behavior lives in the player, queue, arena, season, reward, investigation, and
26+
* <p>Domain behavior lives in the player, queue, arena, season, reward, investigation, test, and
2727
* database modules. This class only coordinates registration, shutdown, reload, and shared help.
2828
*/
2929
public final class WalkCommand {
@@ -35,6 +35,7 @@ public final class WalkCommand {
3535
final SeasonCommands seasons;
3636
final RewardCommands rewards;
3737
final InvestigationCommands investigations;
38+
final TestCommands tests;
3839
final DatabaseCommands database;
3940

4041
private final ReloadHandler reloadHandler;
@@ -75,6 +76,7 @@ public WalkCommand(
7576
this.seasons = new SeasonCommands(support);
7677
this.rewards = new RewardCommands(support);
7778
this.investigations = new InvestigationCommands(support);
79+
this.tests = new TestCommands(support);
7880
this.database = new DatabaseCommands(support);
7981
}
8082

@@ -249,6 +251,11 @@ void adminHelp(CommandSender sender) {
249251
permissions.adminInvestigate(),
250252
"/walk admin run",
251253
"Query redacted retained-run evidence");
254+
support.addAdminHelp(
255+
sender,
256+
permissions.adminTest(),
257+
"/walk admin test fast-forward <target-score>",
258+
"Start a self-only non-scoring reproduction run");
252259
support.addAdminHelp(
253260
sender,
254261
permissions.adminDebug(),

src/main/java/com/mrfdev/walktheplank/command/WalkCommandTree.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ private static LiteralArgumentBuilder<CommandSourceStack> adminNode(
293293
admin.then(exportNode(command));
294294
admin.then(rewardNode(command));
295295
admin.then(investigationNode(command));
296+
admin.then(adminTestNode(command));
296297

297298
LiteralArgumentBuilder<CommandSourceStack> status =
298299
Commands.literal("status")
@@ -319,6 +320,27 @@ private static LiteralArgumentBuilder<CommandSourceStack> adminNode(
319320
return admin;
320321
}
321322

323+
private static LiteralArgumentBuilder<CommandSourceStack> adminTestNode(
324+
WalkCommand command) {
325+
return Commands.literal("test")
326+
.requires(source -> command.adminPermission(
327+
source,
328+
command.support.permissions().adminTest()))
329+
.executes(context -> command.execute(
330+
context.getSource(), command.tests::help))
331+
.then(Commands.literal("fast-forward")
332+
.then(Commands.argument(
333+
"target-score",
334+
IntegerArgumentType.integer(1, 500))
335+
.executes(context -> command.execute(
336+
context.getSource(),
337+
sender -> command.tests.fastForward(
338+
sender,
339+
IntegerArgumentType.getInteger(
340+
context,
341+
"target-score"))))));
342+
}
343+
322344
private static LiteralArgumentBuilder<CommandSourceStack> eventNode(
323345
WalkCommand command) {
324346
LiteralArgumentBuilder<CommandSourceStack> event =

0 commit comments

Comments
 (0)