Skip to content

Commit 9502586

Browse files
authored
Merge pull request #614 from Woodstop/main
Add /mvinv view Command for player inventory inspection across worlds
2 parents 21954f4 + fd117a3 commit 9502586

12 files changed

Lines changed: 1464 additions & 3 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package org.mvplugins.multiverse.inventories.commands;
2+
3+
import com.dumptruckman.minecraft.util.Logging;
4+
import org.bukkit.Bukkit;
5+
import org.bukkit.ChatColor;
6+
import org.bukkit.OfflinePlayer;
7+
import org.bukkit.entity.Player;
8+
import org.bukkit.inventory.Inventory;
9+
import org.jvnet.hk2.annotations.Service;
10+
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
11+
import org.mvplugins.multiverse.core.world.MultiverseWorld;
12+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion;
13+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission;
14+
import org.mvplugins.multiverse.external.acf.commands.annotation.Description;
15+
import org.mvplugins.multiverse.external.acf.commands.annotation.Flags;
16+
import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand;
17+
import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax;
18+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
19+
import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull;
20+
import org.mvplugins.multiverse.inventories.MultiverseInventories;
21+
import org.mvplugins.multiverse.inventories.view.InventoryDataProvider;
22+
import org.mvplugins.multiverse.inventories.view.InventoryGUIHelper;
23+
import org.mvplugins.multiverse.inventories.view.ModifiableInventoryHolder;
24+
import org.mvplugins.multiverse.inventories.view.PlayerInventoryData;
25+
26+
@Service
27+
final class InventoryModifyCommand extends InventoriesCommand {
28+
29+
private final InventoryDataProvider inventoryDataProvider;
30+
private final MultiverseInventories inventories;
31+
private final InventoryGUIHelper inventoryGUIHelper;
32+
33+
@Inject
34+
InventoryModifyCommand(
35+
@NotNull InventoryDataProvider inventoryDataProvider,
36+
@NotNull MultiverseInventories inventories,
37+
@NotNull InventoryGUIHelper inventoryGUIHelper
38+
) {
39+
this.inventoryDataProvider = inventoryDataProvider;
40+
this.inventories = inventories;
41+
this.inventoryGUIHelper = inventoryGUIHelper;
42+
}
43+
44+
// This method contains the logic for the /mvinv modify command
45+
@Subcommand("modify")
46+
@CommandPermission("multiverse.inventories.view.modify") // Specific permission for modification
47+
@CommandCompletion("@mvinvplayernames @mvworlds")
48+
@Syntax("<player> <world>")
49+
@Description("Modify a player's inventory in a specific world.")
50+
void onInventoryModifyCommand(
51+
@NotNull MVCommandIssuer issuer,
52+
53+
// to make sure the command is only run by players
54+
@Flags("resolve=issuerOnly")
55+
@NotNull Player player,
56+
57+
@Syntax("<player>")
58+
@Description("Online or offline player")
59+
OfflinePlayer targetPlayer,
60+
61+
@Syntax("<world>")
62+
@Description("The world the player's inventory is in")
63+
MultiverseWorld world
64+
) {
65+
String worldName = world.getName();
66+
issuer.sendInfo(ChatColor.YELLOW + "Loading inventory data for " + targetPlayer.getName() + "...");
67+
handleInventoryLoadAndDisplay(issuer, player, targetPlayer, worldName);
68+
}
69+
70+
/**
71+
* Handles the asynchronous loading of player inventory data and the display of the GUI.
72+
*
73+
* @param issuer The command issuer.
74+
* @param player The player who will view/modify the inventory.
75+
* @param targetPlayer The offline or online player whose inventory data is being loaded.
76+
* @param worldName The name of the world for which inventory data is loaded.
77+
*/
78+
private void handleInventoryLoadAndDisplay(
79+
@NotNull MVCommandIssuer issuer,
80+
@NotNull Player player,
81+
@NotNull OfflinePlayer targetPlayer,
82+
@NotNull String worldName
83+
) {
84+
inventoryDataProvider.loadPlayerInventoryData(targetPlayer, worldName)
85+
.thenAccept(playerInventoryData -> {
86+
// Ensure GUI operations run on the main thread
87+
Bukkit.getScheduler().runTask(inventories, () -> {
88+
89+
// If the player tries to modify their own live inventory, stop
90+
if (player.getUniqueId().equals(targetPlayer.getUniqueId())
91+
&& player.getWorld().getName().equalsIgnoreCase(worldName)) {
92+
issuer.sendError(ChatColor.RED + "You cannot modify your own live inventory using this command. Use your regular inventory.");
93+
return; // Stop here if it's a live self-inventory
94+
}
95+
createAndOpenGUI(issuer, player, targetPlayer, worldName, playerInventoryData);
96+
});
97+
})
98+
99+
100+
.exceptionally(throwable -> {
101+
// This block runs if an exception occurs during data loading
102+
String errorMessage = throwable.getMessage();
103+
if (errorMessage == null || errorMessage.isEmpty()) {
104+
errorMessage = "An unknown error occurred while loading inventory data.";
105+
}
106+
issuer.sendError(ChatColor.RED + errorMessage);
107+
Logging.fine("Error loading inventory for " + targetPlayer.getName() + ": " + throwable.getMessage());
108+
throwable.printStackTrace();
109+
return null;
110+
});
111+
}
112+
113+
/**
114+
* Creates and opens the custom inventory GUI for modification.
115+
* This method must be called on the main server thread.
116+
*
117+
* @param issuer The command issuer.
118+
* @param player The player who will view/modify the inventory.
119+
* @param targetPlayer The offline player whose inventory is being displayed.
120+
* @param worldName The name of the world for which inventory data is displayed.
121+
* @param playerInventoryData The loaded inventory data.
122+
*/
123+
private void createAndOpenGUI(
124+
@NotNull MVCommandIssuer issuer,
125+
@NotNull Player player,
126+
@NotNull OfflinePlayer targetPlayer,
127+
@NotNull String worldName,
128+
@NotNull PlayerInventoryData playerInventoryData
129+
) {
130+
String title = "Modify " + targetPlayer.getName() + " @ " + worldName;
131+
Inventory inv = Bukkit.createInventory(
132+
new ModifiableInventoryHolder(
133+
targetPlayer,
134+
worldName,
135+
playerInventoryData.profileTypeUsed // Use the determined profile type
136+
),
137+
45, // 5 rows for 36 main + 4 armor + 1 off-hand + 4 fillers
138+
title
139+
);
140+
141+
// Call the helper method to populate the GUI
142+
inventoryGUIHelper.populateInventoryGUI(inv, playerInventoryData, true);
143+
player.openInventory(inv);
144+
issuer.sendInfo(ChatColor.GREEN + playerInventoryData.status.getFormattedMessage(targetPlayer.getName(), worldName) + ". Changes will save on close.");
145+
}
146+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package org.mvplugins.multiverse.inventories.commands;
2+
3+
import com.dumptruckman.minecraft.util.Logging;
4+
import org.bukkit.Bukkit;
5+
import org.bukkit.ChatColor;
6+
import org.bukkit.OfflinePlayer;
7+
import org.bukkit.entity.Player;
8+
import org.bukkit.inventory.Inventory;
9+
import org.jvnet.hk2.annotations.Service;
10+
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
11+
import org.mvplugins.multiverse.core.world.MultiverseWorld;
12+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion;
13+
import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission;
14+
import org.mvplugins.multiverse.external.acf.commands.annotation.Description;
15+
import org.mvplugins.multiverse.external.acf.commands.annotation.Flags;
16+
import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand;
17+
import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax;
18+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
19+
import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull;
20+
import org.mvplugins.multiverse.inventories.MultiverseInventories;
21+
import org.mvplugins.multiverse.inventories.view.InventoryGUIHelper;
22+
import org.mvplugins.multiverse.inventories.view.PlayerInventoryData;
23+
import org.mvplugins.multiverse.inventories.view.ReadOnlyInventoryHolder;
24+
import org.mvplugins.multiverse.inventories.view.InventoryDataProvider;
25+
26+
@Service
27+
final class InventoryViewCommand extends InventoriesCommand {
28+
29+
private final InventoryDataProvider inventoryDataProvider;
30+
private final MultiverseInventories inventories;
31+
private final InventoryGUIHelper inventoryGUIHelper;
32+
33+
@Inject
34+
InventoryViewCommand(
35+
@NotNull InventoryDataProvider inventoryDataProvider,
36+
@NotNull MultiverseInventories inventories,
37+
@NotNull InventoryGUIHelper inventoryGUIHelper
38+
) {
39+
this.inventories = inventories;
40+
this.inventoryDataProvider = inventoryDataProvider;
41+
this.inventoryGUIHelper = inventoryGUIHelper;
42+
}
43+
44+
@Subcommand("view")
45+
@CommandPermission("multiverse.inventories.view")
46+
@CommandCompletion("@mvinvplayernames @mvworlds")
47+
@Syntax("<player> <world>")
48+
@Description("View a player's inventory in a specific world.")
49+
void onInventoryViewCommand(
50+
@NotNull MVCommandIssuer issuer,
51+
52+
// Ensure the command is run by a player
53+
@Flags("resolve=issuerOnly")
54+
@NotNull Player player,
55+
56+
@Syntax("<player>")
57+
@Description("Online or offline player")
58+
OfflinePlayer targetPlayer,
59+
60+
@Syntax("<world>")
61+
@Description("The world the player's inventory is in")
62+
MultiverseWorld world
63+
) {
64+
String worldName = world.getName();
65+
66+
// Asynchronously load data using InventoryDataProvider
67+
issuer.sendInfo(ChatColor.YELLOW + "Loading inventory data for " + targetPlayer.getName() + "...");
68+
handleInventoryLoadAndDisplay(issuer, player, targetPlayer, worldName);
69+
}
70+
71+
/**
72+
* Handles the asynchronous loading of player inventory data and the display of the GUI.
73+
*
74+
* @param issuer The command issuer.
75+
* @param player The player who will view the inventory.
76+
* @param targetPlayer The offline player whose inventory data is being loaded.
77+
* @param worldName The name of the world for which inventory data is loaded.
78+
*/
79+
private void handleInventoryLoadAndDisplay(
80+
@NotNull MVCommandIssuer issuer,
81+
@NotNull Player player,
82+
@NotNull OfflinePlayer targetPlayer,
83+
@NotNull String worldName) {
84+
inventoryDataProvider.loadPlayerInventoryData(targetPlayer, worldName)
85+
.thenAccept(playerInventoryData -> {
86+
// Ensure GUI operations run on the main thread
87+
Bukkit.getScheduler().runTask(inventories, () -> {
88+
createAndOpenGUI(issuer, player, targetPlayer, worldName, playerInventoryData);
89+
}); // End of Bukkit.getScheduler().runTask()
90+
})
91+
.exceptionally(throwable -> {
92+
// This block runs if an exception occurs during data loading
93+
issuer.sendError(ChatColor.RED + "Failed to load inventory data: " + throwable.getMessage());
94+
Logging.severe("Error loading inventory for " + targetPlayer.getName() + ": " + throwable.getMessage());
95+
throwable.printStackTrace();
96+
return null; // Must return null for CompletableFuture<Void> in exceptionally
97+
});
98+
}
99+
100+
/**
101+
* Creates and opens the custom inventory GUI for viewing.
102+
* This method must be called on the main server thread.
103+
*
104+
* @param issuer The command issuer.
105+
* @param player The player who will view/modify the inventory.
106+
* @param targetPlayer The offline player whose inventory is being displayed.
107+
* @param worldName The name of the world for which inventory data is displayed.
108+
* @param playerInventoryData The loaded inventory data.
109+
*/
110+
private void createAndOpenGUI(
111+
@NotNull MVCommandIssuer issuer,
112+
@NotNull Player player,
113+
@NotNull OfflinePlayer targetPlayer,
114+
@NotNull String worldName,
115+
@NotNull PlayerInventoryData playerInventoryData
116+
) {
117+
String title = targetPlayer.getName() + " @ " + worldName;
118+
Inventory inv = Bukkit.createInventory(new ReadOnlyInventoryHolder(),
119+
45, // 5 rows for 36 main + 4 armor + 1 off-hand + 4 fillers
120+
title
121+
);
122+
123+
// Call the helper method to populate the GUI
124+
inventoryGUIHelper.populateInventoryGUI(inv, playerInventoryData, false);
125+
player.openInventory(inv);
126+
issuer.sendInfo(ChatColor.GREEN + playerInventoryData.status.getFormattedMessage(targetPlayer.getName(), worldName) + ". Changes will save on close.");
127+
}
128+
}

0 commit comments

Comments
 (0)