Skip to content

refactor: streamline v3 architecture, unify RCON execution, and remove legacy abstractions - #73

Merged
Drownek merged 13 commits into
v3-devfrom
v3-architecture-cleanup
Sep 8, 2026
Merged

Drownek merged 13 commits into
v3-devfrom
v3-architecture-cleanup

Conversation

@Drownek

@Drownek Drownek commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

This PR finalizes the architectural cleanup for Plugwright v3. It consolidates core components, unifies server command execution under RCON for both local and external environments, and removes obsolete, fragile abstractions (journal, AdminBot, abilities, and the standalone cleanup task). It also fixes a configuration inheritance bug in the Gradle plugin matrix for useExternalPluginsOnly.


Key Changes & Motivation

1. Unified RCON Console (@plugwright/runner)

  • Direct integration into @plugwright/runner: Merged the former @plugwright/console-rcon directly into @plugwright/runner (runner-package/lib/rcon/), eliminating package overhead while keeping TCP socket protocol logic self-contained.
  • Why RCON for both Local and External modes?:
    • Previously, local mode relied on process stdin while external mode used RCON.
    • stdin command execution has no request-response correlation. Output from the server is a shared log stream, meaning generic replies (e.g., "Success", "Money given.") could not be reliably attributed to the command that triggered them.
    • In concurrent test sessions or multi-step flows, stdin risked false positives and race conditions, requiring hacky synchronization markers (like minecraft:say sync_<uuid>).
    • Standardizing both modes on RCON makes command execution deterministic, isolated, and ensures await server.execute(...) returns the exact response for that specific command.
  • Synchronous vs. Asynchronous Command Handling:
    • RCON excels on built-in commands like /op, /deop, or /gamemode because Minecraft produces immediate, synchronous command output that RCON captures directly.
    • Note on custom plugin commands: For custom commands that dispatch asynchronous work (e.g., offloading database lookups or scheduling tasks after the initial command handling finishes), developers should still pair await server.execute(...) with expect(server).toHaveReceived(...) to assert against delayed console or broadcast logs.
  • Robust Connection Lifecycle: Exported RconConnection, handled authentication failures cleanly, and ensured explicit socket teardown on environment shutdown.

2. Removal of Legacy & Fragile Abstractions

  • Removed Player abilities:
    • The abilities set on PlayerWrapper was an in-memory client-side record (mark('op'), etc.) that never validated against real server state.
    • If a test or script ran /deop via the console, player.abilities was immediately desynced. Because it was unmaintainable and barely used in the codebase, it has been eliminated.
  • Removed journal & PlugwrightCleanupTask:
    • The file-based crash journal and the standalone --cleanup task were designed around edge cases like mid-test crashes or power loss on external servers.
    • Cleanup is more reliably handled natively at setup/teardown and the start of test lifecycles, removing the need to replay serialized crash journals.
  • Removed AdminBot Console:
    • AdminBot attempted to emulate a console by joining an OP player and running slash commands in chat.
    • Like stdin, reading responses from player chat was ambiguous, prone to mixing with test player messages, and unnecessary with native RCON available.

3. Gradle Plugin Refinements (@plugwright/gradle-plugin)

  • Fixed useExternalPluginsOnly Matrix Scoping:
    • Previously, PlugwrightCorePlugin checked extension.useExternalPluginsOnly.get() globally when creating the project JAR provider. If set to true at the root, matrix environments attempting to override it with .set(false) were ignored because the JAR provider was already discarded globally.
    • Scoped useExternalPluginsOnly evaluation to LocalMode.kt via lazy property binding (flatMap), allowing individual matrix environments to decide whether to install the project JAR or rely purely on external plugins.
    • Maintained backward compatibility with root-level definitions via applyLegacyDefaults.
  • Dynamic Provisioning: Dynamically configure RCON credentials and port in PaperProvisionTask during server provisioning.

4. Documentation & Examples

  • Updated .md and .mdx docs across docs/ and package READMEs to reflect the unified v3 architecture.
  • Migrated all outdated floating server.execute(...) calls to await server.execute(...) in documentation and test specs.
  • Synchronized lockfiles, package manifests, and release scripts (publish.js, bump-version.js).

5. Test Capabilities (requires) Refactor

  • Object-based requires: Migrated the test filtering syntax from custom string arrays (e.g., requires: ['consoleOutput:full', 'op']) to idiomatic, statically-typed object maps (requires: { consoleOutput: 'full', op: true }).
  • Strict Typing for Better DX: RequiresMap is now strictly typed against Partial<EnvironmentCapabilities>. This provides instant IDE autocomplete and rejects typos at compile time.
  • Dropped Legacy Parsing: Backward compatibility for the old string[] format was removed. This eliminated normalizeRequires() boilerplate and cleaned up the runner's skip logic.
  • Capabilities Cleanup: Removed redundant capabilities (freshState, lifecycle, arbitraryUsernames) from the framework. These practically functioned as synonyms for "local mode". The EnvironmentCapabilities interface is now extremely lean, focusing purely on properties that vary dynamically per connection (console, consoleOutput, op). Tests needing destructive actions should now use { environments: ['local'] } instead.

@monikon22 monikon22 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks great - migrating @plugwright/console-rcon directly into @plugwright/runner is useful, since RCON is becoming the foundation for the framework’s interaction with the server.

I agree with you on the idea of removing abilities. I left it in only for the users’ own needs (the original plan was to assign custom tags to specific accounts so they could later be filtered from the account pool based on certain properties), which was especially useful with reuse, which we removed in #59.

The need for PlugwrightCleanupTask also goes away with the decision to use only disposable identities (#58). I also understand the decision to abandon AdminBot—using a game account as a reliable foundation for maintaining the testing pipeline turned out not to be the best idea in practice.

The journal and --cleanup should also be removed, since cleaning up accounts and environments is the responsibility of the server and plugins, not the framework itself.

As for the other changes mentioned in this PR, I fully support these decisions; the fixes seem extremely useful, as does stabilizing the requires labels by converting them to object-based ones.

My main concern with this PR was the decision to fully migrate to RCON to replace stdin: it’s true that some command responses could be “swallowed” due to asynchronous behavior, but since the proposed solution has already accounted for this, there should be no cause for concern. If any problems arise with this, they can be addressed in new issues or PRs.

LGTM!

@Drownek
Drownek merged commit 16116f2 into v3-dev Sep 8, 2026
3 checks passed
@Drownek
Drownek deleted the v3-architecture-cleanup branch September 8, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants