Skip to content

Commit f78c746

Browse files
authored
Merge pull request #646 from Multiverse/feat/bulkedit-clone
Refactor bulkedit commands to remove "bulkedit" prefix and add clone functionality
2 parents c841ffb + a67b569 commit f78c746

24 files changed

+586
-77
lines changed

src/main/java/org/mvplugins/multiverse/inventories/command/MVInvCommandCompletion.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.mvplugins.multiverse.inventories.command;
22

3+
import com.google.common.collect.Streams;
34
import org.bukkit.Bukkit;
45
import org.bukkit.generator.WorldInfo;
56
import org.jetbrains.annotations.NotNull;
@@ -14,8 +15,10 @@
1415
import org.mvplugins.multiverse.inventories.config.InventoriesConfig;
1516
import org.mvplugins.multiverse.inventories.dataimport.DataImportManager;
1617
import org.mvplugins.multiverse.inventories.profile.PlayerNamesMapper;
18+
import org.mvplugins.multiverse.inventories.profile.ProfileDataSource;
1719
import org.mvplugins.multiverse.inventories.profile.group.WorldGroup;
1820
import org.mvplugins.multiverse.inventories.profile.group.WorldGroupManager;
21+
import org.mvplugins.multiverse.inventories.profile.key.ContainerType;
1922
import org.mvplugins.multiverse.inventories.profile.key.GlobalProfileKey;
2023
import org.mvplugins.multiverse.inventories.profile.key.ProfileType;
2124
import org.mvplugins.multiverse.inventories.profile.key.ProfileTypes;
@@ -26,6 +29,7 @@
2629
import java.util.Collection;
2730
import java.util.Collections;
2831
import java.util.List;
32+
import java.util.Locale;
2933
import java.util.Objects;
3034
import java.util.Set;
3135
import java.util.stream.Collectors;
@@ -39,25 +43,31 @@ public final class MVInvCommandCompletion {
3943
private final WorldGroupManager worldGroupManager;
4044
private final DataImportManager dataImportManager;
4145
private final PlayerNamesMapper playerNamesMapper;
46+
private final ProfileDataSource profileDataSource;
4247

4348
@Inject
4449
private MVInvCommandCompletion(
4550
@NotNull InventoriesConfig inventoriesConfig,
4651
@NotNull WorldGroupManager worldGroupManager,
4752
@NotNull DataImportManager dataImportManager,
4853
@NotNull MVCommandManager mvCommandManager,
49-
@NotNull PlayerNamesMapper playerNamesMapper
54+
@NotNull PlayerNamesMapper playerNamesMapper,
55+
@NotNull ProfileDataSource profileDataSource
5056
) {
5157
this.inventoriesConfig = inventoriesConfig;
5258
this.worldGroupManager = worldGroupManager;
5359
this.dataImportManager = dataImportManager;
5460
this.playerNamesMapper = playerNamesMapper;
61+
this.profileDataSource = profileDataSource;
5562

5663
MVCommandCompletions commandCompletions = mvCommandManager.getCommandCompletions();
5764
commandCompletions.registerAsyncCompletion("dataimporters", this::suggestDataImporters);
5865
commandCompletions.registerStaticCompletion("mvinvconfigs", inventoriesConfig.getStringPropertyHandle().getAllPropertyNames());
5966
commandCompletions.registerAsyncCompletion("mvinvconfigvalues", this::suggestConfigValues);
67+
commandCompletions.registerAsyncCompletion("mvinvplayername", this::suggestPlayerName);
6068
commandCompletions.registerAsyncCompletion("mvinvplayernames", this::suggestPlayerNames);
69+
commandCompletions.registerAsyncCompletion("mvinvcontainerkey", this::suggestContainerKey);
70+
commandCompletions.registerAsyncCompletion("mvinvcontainerkeys", this::suggestContainerKeys);
6171
commandCompletions.registerAsyncCompletion("mvinvprofiletypes", this::suggestProfileTypes);
6272
commandCompletions.registerAsyncCompletion("sharables", this::suggestSharables);
6373
commandCompletions.registerAsyncCompletion("shares", this::suggestShares);
@@ -77,6 +87,10 @@ private Collection<String> suggestConfigValues(BukkitCommandCompletionContext co
7787
.getOrElse(Collections.emptyList());
7888
}
7989

90+
private Collection<String> suggestPlayerName(BukkitCommandCompletionContext context) {
91+
return getPlayerNames();
92+
}
93+
8094
private Collection<String> suggestPlayerNames(BukkitCommandCompletionContext context) {
8195
if (Objects.equals(context.getInput(), "@all")) {
8296
return Collections.emptyList();
@@ -96,6 +110,21 @@ private List<String> getPlayerNames() {
96110
.collect(Collectors.toList());
97111
}
98112

113+
private Collection<String> suggestContainerKey(BukkitCommandCompletionContext context) {
114+
return Arrays.stream(ContainerType.values())
115+
.flatMap(containerType -> profileDataSource.listContainerDataNames(containerType)
116+
.stream()
117+
.map(dataName -> containerType.name().toLowerCase(Locale.ENGLISH) + "=" + dataName))
118+
.collect(Collectors.toList());
119+
}
120+
121+
private Collection<String> suggestContainerKeys(BukkitCommandCompletionContext context) {
122+
//todo handle multiple worlds and/or groups
123+
Collection<String> strings = suggestContainerKey(context);
124+
strings.add("@all");
125+
return strings;
126+
}
127+
99128
private Collection<String> suggestProfileTypes(BukkitCommandCompletionContext context) {
100129
if (!context.hasConfig("multiple")) {
101130
return ProfileTypes.getTypes().stream()

src/main/java/org/mvplugins/multiverse/inventories/command/MVInvCommandContexts.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
1111
import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull;
1212
import org.mvplugins.multiverse.external.vavr.control.Option;
13+
import org.mvplugins.multiverse.external.vavr.control.Try;
1314
import org.mvplugins.multiverse.inventories.config.InventoriesConfig;
1415
import org.mvplugins.multiverse.inventories.profile.PlayerNamesMapper;
1516
import org.mvplugins.multiverse.inventories.profile.ProfileDataSource;
@@ -29,6 +30,7 @@
2930
import java.util.Arrays;
3031
import java.util.Collections;
3132
import java.util.List;
33+
import java.util.Objects;
3234

3335
@Service
3436
public final class MVInvCommandContexts {
@@ -52,7 +54,9 @@ private MVInvCommandContexts(
5254
this.profileDataSource = profileDataSource;
5355

5456
CommandContexts<BukkitCommandExecutionContext> commandContexts = commandManager.getCommandContexts();
57+
commandContexts.registerContext(ContainerKey.class, this::parseContainerKey);
5558
commandContexts.registerContext(ContainerKey[].class, this::parseContainerKeyArray);
59+
commandContexts.registerContext(GlobalProfileKey.class, this::parseGlobalProfileKey);
5660
commandContexts.registerContext(GlobalProfileKey[].class, this::parseGlobalProfileKeyArray);
5761
commandContexts.registerIssuerAwareContext(ProfileType.class, this::parseProfileType);
5862
commandContexts.registerIssuerAwareContext(ProfileType[].class, this::parseProfileTypeArray);
@@ -70,6 +74,22 @@ private ProfileType parseProfileType(BukkitCommandExecutionContext context) {
7074
.getOrElseThrow(() -> new InvalidCommandArgument("Invalid profile type: " + profileType));
7175
}
7276

77+
private ContainerKey parseContainerKey(BukkitCommandExecutionContext context) {
78+
String input = context.popFirstArg();
79+
String[] keyValueSplit = REPatterns.EQUALS.split(input, 2);
80+
if (keyValueSplit.length != 2) {
81+
throw new InvalidCommandArgument("Invalid world/group format: " + input + ". Expected format: type=name");
82+
}
83+
ContainerType containerType = Try.of(() -> ContainerType.valueOf(keyValueSplit[0].toUpperCase()))
84+
.getOrElseThrow(() -> new InvalidCommandArgument("Unknown container type: " + keyValueSplit[0]));
85+
String dataName = keyValueSplit[1];
86+
List<String> availableDataNames = profileDataSource.listContainerDataNames(containerType);
87+
if (!availableDataNames.contains(dataName)) {
88+
throw new InvalidCommandArgument("The " + keyValueSplit[0] + " name " + dataName + " does not have any data.");
89+
}
90+
return ContainerKey.create(containerType, dataName);
91+
}
92+
7393
private ContainerKey[] parseContainerKeyArray(BukkitCommandExecutionContext context) {
7494
String keyStrings = context.popFirstArg();
7595
if (keyStrings.equals("@all")) {
@@ -84,8 +104,7 @@ private ContainerKey[] parseContainerKeyArray(BukkitCommandExecutionContext cont
84104
for (String typeSplit : typesSplit) {
85105
String[] keyValueSplit = REPatterns.EQUALS.split(typeSplit, 2);
86106
if (keyValueSplit.length != 2) {
87-
// todo: Probably error invalid format
88-
continue;
107+
throw new InvalidCommandArgument("Invalid worlds/groups format: " + typeSplit + ". Expected format: type=name1,name2;type2=name3");
89108
}
90109
ContainerType containerType = ContainerType.valueOf(keyValueSplit[0].toUpperCase());
91110
String[] dataNameSplit = REPatterns.COMMA.split(keyValueSplit[1]);
@@ -99,9 +118,16 @@ private ContainerKey[] parseContainerKeyArray(BukkitCommandExecutionContext cont
99118
return containerKeys.toArray(new ContainerKey[0]);
100119
}
101120

121+
private GlobalProfileKey parseGlobalProfileKey(BukkitCommandExecutionContext context) {
122+
String keyString = context.popFirstArg();
123+
// todo: UUID parsing
124+
return playerNamesMapper.getKey(keyString)
125+
.getOrElseThrow(() -> new InvalidCommandArgument("Unknown player name: " + keyString));
126+
}
127+
102128
private GlobalProfileKey[] parseGlobalProfileKeyArray(BukkitCommandExecutionContext context) {
103129
String keyStrings = context.popFirstArg();
104-
if (keyStrings.equals("@all")) {
130+
if (Objects.equals(keyStrings, "@all")) {
105131
return playerNamesMapper.getKeys().toArray(GlobalProfileKey[]::new);
106132
}
107133
// todo: UUID parsing
@@ -114,16 +140,26 @@ private GlobalProfileKey[] parseGlobalProfileKeyArray(BukkitCommandExecutionCont
114140
}
115141

116142
private ProfileType[] parseProfileTypeArray(BukkitCommandExecutionContext context) {
117-
String keyStrings = context.popFirstArg();
118-
if (keyStrings.equals("@all")) {
143+
String keyStrings = context.getFirstArg();
144+
if (keyStrings == null) {
145+
return ProfileTypes.getTypes().toArray(ProfileType[]::new);
146+
}
147+
if (Objects.equals(keyStrings, "@all")) {
148+
context.popFirstArg();
119149
return ProfileTypes.getTypes().toArray(ProfileType[]::new);
120150
}
121151
String[] profileNames = REPatterns.COMMA.split(keyStrings);
122-
return Arrays.stream(profileNames)
152+
List<ProfileType> list = Arrays.stream(profileNames)
123153
.map(ProfileTypes::forName)
124154
.filter(Option::isDefined)
125155
.map(Option::get)
126-
.toArray(ProfileType[]::new);
156+
.toList();
157+
if (list.isEmpty()) {
158+
return ProfileTypes.getTypes().toArray(ProfileType[]::new);
159+
}
160+
161+
context.popFirstArg();
162+
return list.toArray(new ProfileType[0]);
127163
}
128164

129165
private Sharable<?> parseSharable(BukkitCommandExecutionContext context) {

src/main/java/org/mvplugins/multiverse/inventories/commands/bulkedit/BulkEditCommand.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.jvnet.hk2.annotations.Contract;
55
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
66
import org.mvplugins.multiverse.core.utils.StringFormatter;
7+
import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand;
78
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
89
import org.mvplugins.multiverse.inventories.commands.InventoriesCommand;
910
import org.mvplugins.multiverse.inventories.profile.bulkedit.BulkEditAction;
@@ -12,6 +13,7 @@
1213

1314
@Contract
1415
@ApiStatus.Internal
16+
@Subcommand("bulkedit")
1517
public abstract class BulkEditCommand extends InventoriesCommand {
1618

1719
protected final BulkEditCreator bulkEditCreator;

src/main/java/org/mvplugins/multiverse/inventories/commands/bulkedit/globalprofile/ClearCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ final class ClearCommand extends BulkEditCommand {
4242
this.flags = flags;
4343
}
4444

45-
@Subcommand("bulkedit globalprofile clear")
45+
@Subcommand("globalprofile clear")
4646
@CommandPermission("multiverse.inventories.bulkedit")
4747
@CommandCompletion("@mvinvplayernames @flags:groupName=" + Flags.NAME)
4848
@Syntax("<players> [--clear-all-player-profiles]")

src/main/java/org/mvplugins/multiverse/inventories/commands/bulkedit/globalprofile/ModifyCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class ModifyCommand extends InventoriesCommand {
3131
this.profileDataSource = profileDataSource;
3232
}
3333

34-
@Subcommand("bulkedit globalprofile modify")
34+
@Subcommand("globalprofile modify")
3535
@CommandPermission("multiverse.inventories.bulkedit")
3636
@CommandCompletion("load-on-login|last-world @empty @mvinvplayernames")
3737
@Syntax("<property> <value> <players>")

src/main/java/org/mvplugins/multiverse/inventories/commands/bulkedit/playerprofile/ClearCommand.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,23 @@ final class ClearCommand extends BulkEditCommand {
3737
this.flags = flags;
3838
}
3939

40-
@Subcommand("bulkedit playerprofile clear")
40+
@Subcommand("playerprofile clear")
4141
@CommandPermission("multiverse.inventories.bulkedit")
42-
@CommandCompletion("@mvinvplayernames @empty @mvinvprofiletypes:multiple @flags:groupName=" + IncludeGroupsWorldsFlag.NAME)
43-
@Syntax("<players> <groups|worlds> [profile-type] [--include-groups-worlds]")
42+
@CommandCompletion("@mvinvplayernames @mvinvcontainerkeys @mvinvprofiletypes:multiple @flags:groupName=" + IncludeGroupsWorldsFlag.NAME)
43+
@Syntax("<players> <groups/worlds> [profile-type] [--include-groups-worlds]")
4444
void onCommand(
4545
MVCommandIssuer issuer,
46+
47+
@Syntax("<players>")
4648
GlobalProfileKey[] globalProfileKeys,
49+
50+
@Syntax("<groups/worlds>")
4751
ContainerKey[] containerKeys,
52+
53+
@Syntax("[profile-types]")
4854
ProfileType[] profileTypes,
55+
56+
@Syntax("[--include-groups-worlds]")
4957
String[] flagArray
5058
) {
5159
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.mvplugins.multiverse.inventories.commands.bulkedit.playerprofile;
2+
3+
import org.jvnet.hk2.annotations.Service;
4+
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
5+
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
6+
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
7+
import org.mvplugins.multiverse.core.command.queue.CommandQueuePayload;
8+
import org.mvplugins.multiverse.core.locale.message.Message;
9+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion;
10+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission;
11+
import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand;
12+
import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax;
13+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
14+
import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull;
15+
import org.mvplugins.multiverse.inventories.commands.bulkedit.BulkEditCommand;
16+
import org.mvplugins.multiverse.inventories.profile.bulkedit.BulkEditAction;
17+
import org.mvplugins.multiverse.inventories.profile.bulkedit.BulkEditCreator;
18+
import org.mvplugins.multiverse.inventories.profile.bulkedit.PlayerProfilesPayload;
19+
import org.mvplugins.multiverse.inventories.profile.key.ContainerKey;
20+
import org.mvplugins.multiverse.inventories.profile.key.GlobalProfileKey;
21+
import org.mvplugins.multiverse.inventories.profile.key.ProfileType;
22+
23+
@Service
24+
public class ClonePlayerCommand extends BulkEditCommand {
25+
26+
private final @NotNull CommandQueueManager commandQueueManager;
27+
private final @NotNull IncludeGroupsWorldsFlag flags;
28+
29+
@Inject
30+
ClonePlayerCommand(@NotNull BulkEditCreator bulkEditCreator,
31+
@NotNull CommandQueueManager commandQueueManager,
32+
@NotNull IncludeGroupsWorldsFlag flags
33+
) {
34+
super(bulkEditCreator);
35+
this.commandQueueManager = commandQueueManager;
36+
this.flags = flags;
37+
}
38+
39+
@Subcommand("playerprofile clone-player")
40+
@CommandPermission("multiverse.inventories.bulkedit")
41+
@CommandCompletion("@mvinvplayername @mvinvplayername @mvinvcontainerkeys @mvinvprofiletypes:multiple " +
42+
"@flags:groupName=" + IncludeGroupsWorldsFlag.NAME)
43+
@Syntax("<from-player> <to-player> <groups/worlds> [profile-types] [--include-groups-worlds]")
44+
void onCommand(
45+
MVCommandIssuer issuer,
46+
47+
@Syntax("<from-player>")
48+
GlobalProfileKey fromPlayer,
49+
50+
@Syntax("<to-player>")
51+
GlobalProfileKey[] toPlayers,
52+
53+
@Syntax("<groups/worlds>")
54+
ContainerKey[] containerKeys,
55+
56+
@Syntax("[profile-types]")
57+
ProfileType[] profileTypes,
58+
59+
@Syntax("[--include-groups-worlds]")
60+
String[] flagArray
61+
) {
62+
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
63+
64+
BulkEditAction<?> bulkEditAction = bulkEditCreator.playerProfileClonePlayer(
65+
fromPlayer,
66+
new PlayerProfilesPayload(
67+
toPlayers,
68+
containerKeys,
69+
profileTypes,
70+
parsedFlags.hasFlag(flags.includeGroupsWorlds)
71+
)
72+
);
73+
74+
outputActionSummary(issuer, bulkEditAction);
75+
76+
commandQueueManager.addToQueue(CommandQueuePayload.issuer(issuer)
77+
.prompt(Message.of("Are you sure you want to clone profiles from %s to %s for the selected groups/worlds?"
78+
.formatted(fromPlayer.getPlayerName(),
79+
toPlayers.length == 1 ? toPlayers[0].getPlayerName() : toPlayers.length + " players")))
80+
.action(() -> runBulkEditAction(issuer, bulkEditAction)));
81+
}
82+
}

0 commit comments

Comments
 (0)