Skip to content

feat: refuse to enable when Set Homes v1 is installed alongside - #58

Merged
milanmalhotra merged 1 commit into
devfrom
issue-53-refuse-alongside-v1
Aug 17, 2026
Merged

feat: refuse to enable when Set Homes v1 is installed alongside#58
milanmalhotra merged 1 commit into
devfrom
issue-53-refuse-alongside-v1

Conversation

@milanmalhotra

Copy link
Copy Markdown
Collaborator

Closes #53. First of the five sub-issues split out of #41.

What changed

onEnable now checks, as its very first statement, whether a plugin named exactly SetHomes is loaded. If one is, it logs a severe block and disables us.

Nothing of v1's is touched and the server keeps running v1 exactly as it did the day before. The block names the jar to move out of plugins/, says to keep it rather than delete it so a rollback stays possible, and gives /import-homes sethomes as the step to run once it is gone.

Why refuse rather than warn

With both jars in plugins/, Bukkit loads both happily and the command set splits along a line no player can see. Measured twice on Paper 1.21.4 on 2026-08-16:

Command Goes to
/sethome, /home, /delhome, /uhome, /home-of, /setmax v1
/homes, /blacklist v2

SimpleCommandMap.register lets a primary name displace an existing alias but never lets an alias displace anything. v1 holds the primaries and we hold the aliases, so v1 wins whatever the load order. /sethome shop would write to v1's homes.yml while /homes opened our menu without it, and neither plugin logs anything about the clash. A warning that can be ignored costs players their homes; refusing to start costs a restart.

The alternatives are recorded on #41 and were rejected: disabling or unloading v1 from our onEnable, moving v1's jar ourselves, and winning the names with primary declarations plus loadbefore.

Notes for review

  • The refusal message is hardcoded rather than config-driven, because the guard runs before initConfig and there is no config to read yet.
  • The block does not offer /sethomestwo:sethome as a stopgap. That form only reaches us while we are enabled, so under a refusal there is nothing to fall back to.
  • SetHomesV1ClashTest boots MockBukkit by hand instead of extending ServerTestBase, because v1 has to be registered before our onEnable runs and the base loads the plugin in its own @BeforeEach.
  • The README change slightly overlaps Correct the migration steps in the README: swap-then-import, and the rollback path #56. The guard makes the previously documented step order impossible rather than merely unwise, so the jar-swap ordering and the move-don't-delete wording came across with it. Correct the migration steps in the README: swap-then-import, and the rollback path #56 has been updated to record that and now covers the rollback path, the restart-after-config note, the max-homes caveat, and restructuring the numbered list.

Verification

MockBukkit: 371 before, 377 after, Failures: 0, Errors: 0, Skipped: 0.

Two tests were watched failing before the code existed. The other four passed on first run, so the guard was broken four ways to prove each one, then restored byte-identical:

Break Test that caught it
equalsIgnoreCase instead of exact match theNameMatchIsCaseSensitive
startsWith instead of exact match aPluginWhoseNameOnlyStartsWithSetHomesIsNotV1
createDirectories() and initConfig() moved above the guard aRefusedBootCreatesNothingInOurDataFolder
guard made unconditional the three enabling tests

Verified in game on the local Paper 1.21.4 server with the real SetHomes-1.3.0.jar, both directions: with v1 present we refuse and print the block while v1 carries on, and with v1 removed we enable normally. The boot log also confirms Bukkit loads every plugin before enabling any, so the lookup finds v1 even though we enable first.

Both plugins declare /sethome, /home and /delhome. v1 declares them as
primary names while we declare them as aliases, and SimpleCommandMap
never lets an alias displace anything, so v1 takes all six whatever the
load order. Homes created after an upgrade would land in v1's homes.yml
while /homes read our database, and neither plugin logs a word about it.

The guard runs as the first statement of onEnable, before any directory
or config is written, so a refused boot leaves the server exactly as it
was and v1 keeps serving its commands.
@milanmalhotra
milanmalhotra merged commit 02f068c into dev Aug 17, 2026
3 checks passed
@milanmalhotra
milanmalhotra deleted the issue-53-refuse-alongside-v1 branch August 17, 2026 22:23
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.

1 participant