Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
new ImportCommand(this.getAddon(), this);
new GeneratorWhyCommand(this.getAddon(), this);
new GeneratorDatabaseCommand(this.getAddon(), this);
new ResetCommand(this.getAddon(), this);
}


Expand Down Expand Up @@ -93,10 +94,86 @@


// ---------------------------------------------------------------------
// Section: Subcommadns
// Section: Subcommands
// ---------------------------------------------------------------------


/**
* Base class for admin subcommands that take a single player name argument. It validates the argument, resolves the
* target player UUID and provides player name tab-completion, delegating the actual work to
* {@link #executeForTarget(User, String, UUID)}.
*/
private abstract static class PlayerTargetCommand extends ConfirmableCommand
{
protected PlayerTargetCommand(StoneGeneratorAddon addon, CompositeCommand parentCommand, String label)
{
super(addon, parentCommand, label);
}


@Override
public void setup()
{
// Permission and locale references are derived from the subcommand label.
this.setPermission("admin.stone-generator." + this.getLabel());
this.setParametersHelp(Constants.ADMIN_COMMANDS + this.getLabel() + ".parameters");
this.setDescription(Constants.ADMIN_COMMANDS + this.getLabel() + ".description");

this.setOnlyPlayer(false);
}


@Override
public boolean execute(User user, String label, List<String> args)
{
// If args are not right, show help
if (args.size() != 1)
{
this.showHelp(this, user);
return false;
}

// Get target
UUID targetUUID = Util.getUUID(args.get(0));

if (targetUUID == null)
{
Utils.sendMessage(user,
user.getTranslation("general.errors.unknown-player", TextVariables.NAME, args.get(0)));
return false;
}

return this.executeForTarget(user, args.get(0), targetUUID);
}


/**
* Runs the subcommand for the resolved target player.
*
* @param user the user running the command.
* @param targetName the player name argument as typed.
* @param targetUUID the resolved target player UUID.
* @return {@code true} if the command executed successfully.
*/
protected abstract boolean executeForTarget(User user, String targetName, UUID targetUUID);


@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
{
if (args.isEmpty())
{
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}

return Optional.of(Util.tabLimit(
new ArrayList<>(Util.getOnlinePlayerList(user)),
args.get(args.size() - 1)));
}
}


/**
* This is a confirmation command for importing generators from template file. It requires confirmation as it
* removes every data from database.
Expand Down Expand Up @@ -164,7 +241,7 @@
* This is a debug command for admins. Admins could use to check which generator user is using and could faster find
* an issue.
*/
private static class GeneratorWhyCommand extends ConfirmableCommand
private static class GeneratorWhyCommand extends PlayerTargetCommand
{
/**
* This is simple constructor for initializing /{admin_command} why generator command.
Expand All @@ -178,58 +255,9 @@
}


/**
* Setups anything that is needed for this command. <br/><br/> It is recommended you do the following in this
* method:
* <ul>
* <li>Register any of the sub-commands of this command;</li>
* <li>Define the permission required to use this command using {@link
* CompositeCommand#setPermission(String)};</li>
* <li>Define whether this command can only be run by players or not using {@link
* CompositeCommand#setOnlyPlayer(boolean)};</li>
* </ul>
*/
@Override
public void setup()
{
this.setPermission("admin.stone-generator.why");
this.setParametersHelp(Constants.ADMIN_COMMANDS + "why.parameters");
this.setDescription(Constants.ADMIN_COMMANDS + "why.description");

this.setOnlyPlayer(false);
}


/**
* Defines what will be executed when this command is run.
*
* @param user the {@link User} who is executing this command.
* @param label the label which has been used to execute this command. It can be {@link
* CompositeCommand#getLabel()} or an alias.
* @param args the command arguments.
* @return {@code true} if the command executed successfully, {@code false} otherwise.
*/
@Override
public boolean execute(User user, String label, List<String> args)
protected boolean executeForTarget(User user, String targetName, UUID targetUUID)
{
// If args are not right, show help
if (args.size() != 1)
{
this.showHelp(this, user);
return false;
}

// Get target
UUID targetUUID = Util.getUUID(args.get(0));

if (targetUUID == null)
{
Utils.sendMessage(user,
user.getTranslation("general.errors.unknown-player",
TextVariables.NAME, args.get(0)));
return false;
}

// Set meta data on player
Island island = this.getAddon().getIslands().getIsland(this.getWorld(), targetUUID);

Expand Down Expand Up @@ -281,31 +309,55 @@

return true;
}
}


/**
* This command resets a single player's island generator data (unlocked, purchased and active generators) without
* touching the rest of the database. Requires confirmation as it is destructive.
*/
private static class ResetCommand extends PlayerTargetCommand
{
/**
* Tab Completer for CompositeCommands. Note that any registered sub-commands will be automatically added to the
* list. Use this to add tab-complete for things like names.
* This is simple constructor for initializing /{admin_command} generator reset command.
*
* @param user the {@link User} who is executing this command.
* @param alias alias for command
* @param args command arguments
* @return List of strings that could be used to complete this command.
* @param addon StoneGeneratorAddon addon.
* @param parentCommand Parent Command where we hook our command into.
*/
public ResetCommand(StoneGeneratorAddon addon, CompositeCommand parentCommand)
{
super(addon, parentCommand, "reset");
}


@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
protected boolean executeForTarget(User user, String targetName, UUID targetUUID)
{
if (args.isEmpty())
{
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
else
Island island = this.getAddon().getIslands().getIsland(this.getWorld(), targetUUID);

if (island == null)
{
return Optional.of(Util.tabLimit(
new ArrayList<>(Util.getOnlinePlayerList(user)),
args.get(args.size() - 1)));
Utils.sendMessage(user, user.getTranslation("general.errors.player-has-no-island"));
return false;
}

// Fall back to the typed argument if the server cannot resolve a name for the UUID.
final String resolvedName = this.getPlayers().getName(targetUUID);
final String displayName = resolvedName == null || resolvedName.isEmpty() ? targetName : resolvedName;

Check warning on line 346 in src/main/java/world/bentobox/magiccobblestonegenerator/commands/admin/GeneratorAdminCommand.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this expression which always evaluates to "false"

See more on https://sonarcloud.io/project/issues?id=BentoBoxWorld_MagicCobblestoneGenerator&issues=AZ8vmlVDPgOMskWILmZj&open=AZ8vmlVDPgOMskWILmZj&pullRequest=160

this.askConfirmation(user,
user.getTranslation(Constants.CONVERSATIONS + "prefix") +
user.getTranslation(Constants.ADMIN_COMMANDS + "reset.confirmation",
Constants.PLAYER, displayName),
() ->
{
this.<StoneGeneratorAddon>getAddon().getAddonManager().resetIslandData(island);
Utils.sendMessage(user,
user.getTranslation(Constants.MESSAGES + "generator-data-reset",
Constants.PLAYER, displayName));
});

return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,24 @@ public void wipeGeneratorData(GeneratorDataObject dataObject) {
this.wipeGeneratorData(dataObject.getUniqueId());
}

/**
* This method resets all generator data for the given island: its stored data is removed and a fresh default data
* object is recreated. Unlocked, purchased and active generators are cleared (#149).
*
* @param island Island whose generator data must be reset.
*/
public void resetIslandData(@NotNull Island island) {
this.wipeGeneratorData(island.getUniqueId());
// Recreate a fresh, default data object so the island keeps working immediately. addIslandData works even
// for ownerless islands (e.g. spawn), unlike validateIslandData which returns early when there is no owner.
this.addIslandData(island);

if (island.getOwner() != null) {
// For owned islands, also re-apply owner bundles/limits and re-evaluate unlocks.
this.validateIslandData(island);
}
}

// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ stone-generator:
why:
parameters: "<player>"
description: "toggles Magic Cobblestone Generator debug messages"
reset:
parameters: "<player>"
description: "resets a player's island generator data"
confirmation: "This will reset [player]'s generator data (unlocked, purchased and active generators) - please confirm"
database:
description: "Main database command"
import-database:
Expand Down Expand Up @@ -999,6 +1003,8 @@ stone-generator:
generator-exhausted: "<red>Generator </red> '[generator]' <red>has reached its generation limit and is now on cooldown for [number] more minute(s).</red>"
# Message that appears when purchasing the generator.
generator-purchased: "<yellow>Generator </yellow> '[generator]' <yellow>is purchased.</yellow>"
# Message that appears after an admin resets a player's generator data.
generator-data-reset: "<yellow>Generator data for </yellow>[player]<yellow> has been reset.</yellow>"
# Message that appears when trying to buy already purchased generator.
generator-already-purchased: "<red>Generator </red> '[generator]' <red>is already purchased.</red>"
# Message that appears when trying to buy generator without required island level
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class GeneratorAdminCommandTest extends CommonTestSetup {
private Settings settings;
private CompositeCommand ic;
private CompositeCommand why;
private CompositeCommand reset;

@Override
@BeforeEach
Expand Down Expand Up @@ -78,6 +79,7 @@ public void setUp() throws Exception {
gac = new GeneratorAdminCommand(addon, ac);
ic = gac.getSubCommand("import").get();
why = gac.getSubCommand("why").get();
reset = gac.getSubCommand("reset").get();
}

@Test
Expand Down Expand Up @@ -130,7 +132,7 @@ void testExecuteUserStringListOfStringHelp() {
"stone-generator.commands.admin.main.parameters");
verify(user).getTranslation(
"stone-generator.commands.admin.main.description");
verify(user, times(4)).isPlayer();
verify(user, times(5)).isPlayer();
verify(user).sendMessage(
"commands.help.syntax-no-parameters",
"[usage]",
Expand All @@ -155,6 +157,12 @@ void testExecuteUserStringListOfStringHelp() {
"/null generator database",
"[description]",
"stone-generator.commands.admin.database.description");
verify(user).sendMessage(
"commands.help.syntax-no-parameters",
"[usage]",
"/null generator reset",
"[description]",
"stone-generator.commands.admin.reset.description");
verify(user).sendMessage(
"commands.help.end");
}
Expand All @@ -181,4 +189,35 @@ void testExecuteUserStringListOfStringWhyPlayer() {
"stone-generator.conversations.prefixgeneral.errors.player-is-not-owner");
}

@Test
void testSetupReset() {
assertEquals("admin.stone-generator.reset", reset.getPermission());
assertEquals("stone-generator.commands.admin.reset.parameters", reset.getParameters());
assertEquals("stone-generator.commands.admin.reset.description", reset.getDescription());
assertFalse(reset.isOnlyPlayer());
}

@Test
void testExecuteResetNoArgs() {
assertFalse(reset.execute(user, "bskyblock", List.of()));
verify(user).sendMessage(
"commands.help.header",
"[label]",
"BSkyBlock World");
verify(user).getTranslationOrNothing(
"stone-generator.commands.admin.reset.parameters");
verify(user).getTranslation(
"stone-generator.commands.admin.reset.description");
verify(user).sendMessage(
"commands.help.end");
}

@Test
void testExecuteResetPlayerNoIsland() {
// Target resolves to a UUID but has no island in this world.
assertFalse(reset.execute(user, "bskyblock", List.of("tastybento")));
verify(user).sendMessage(
"stone-generator.conversations.prefixgeneral.errors.player-has-no-island");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,14 @@ void testWipeGeneratorDataString() {
assertDoesNotThrow(() -> sgm.wipeGeneratorData(uuid.toString()));
}

@Test
void testResetIslandData() {
when(island.getUniqueId()).thenReturn("island-149");
assertDoesNotThrow(() -> sgm.resetIslandData(island));
// The island's stored data is deleted as part of the reset.
verify(h).deleteID("island-149");
}

@Test
void testWipeGeneratorDataGeneratorDataObject() {
assertDoesNotThrow(() -> sgm.wipeGeneratorData(generatorData));
Expand Down
Loading