mirror of
https://github.com/garbagemule/MobArena.git
synced 2024-11-29 06:05:15 +01:00
Add support for saved items.
Introduces the concept of a _saved item_; an in-game item that has been captured in a YAML file via Bukkit's item serialization mechanism. These items can be referenced in the config-file in all places that any other normal item can be used, assuming the ThingManager is in charge of the parsing. This should help bridge the gap between class chests and the config-file by allowing any Bukkit serializable item stack to be stored and referenced as if MobArena's item syntax directly supported it. Three new setup commands are introduced to help manage the items, such that they can be created, deleted, and loaded (for "editing" purposes). The commands are somewhat rough around the edges and may need a little bit of polish going forward. Together with the new inventory referencing Things, this functionality should help provide most of the flexibility people have been missing from the item syntax for about a decade... Hell, it's about time. Closes #212
This commit is contained in:
parent
d7336526e1
commit
feb257213c
@ -13,6 +13,7 @@ These changes will (most likely) be included in the next version.
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Support for chest references in item syntax. The new `inv` syntax allows for referencing container indices in the config-file. This should help bridge the gap between class chests and various other parts of the config-file, such as rewards and upgrade waves.
|
||||
- Support for saved items. The new `/ma save-item` command can be used to save the currently held item to disk, which allows it to be used in various places in the config-file. This should help bridge the gap between the config-file and class chests for config-file centric setups.
|
||||
|
||||
### Fixed
|
||||
- Explosion damage caused by Exploding Sheep now correctly counts as monster damage. This means that the explosions only affect other mobs if the per-arena setting `monster-infight` is set to `true`.
|
||||
|
@ -8,6 +8,7 @@ import com.garbagemule.MobArena.formula.FormulaMacros;
|
||||
import com.garbagemule.MobArena.formula.FormulaManager;
|
||||
import com.garbagemule.MobArena.framework.Arena;
|
||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||
import com.garbagemule.MobArena.items.SavedItemsManager;
|
||||
import com.garbagemule.MobArena.listeners.MAGlobalListener;
|
||||
import com.garbagemule.MobArena.metrics.ArenaCountChart;
|
||||
import com.garbagemule.MobArena.metrics.ClassChestsChart;
|
||||
@ -67,6 +68,8 @@ public class MobArena extends JavaPlugin
|
||||
private FormulaManager formman;
|
||||
private FormulaMacros macros;
|
||||
|
||||
private SavedItemsManager itemman;
|
||||
|
||||
private SignListeners signListeners;
|
||||
|
||||
@Override
|
||||
@ -106,6 +109,7 @@ public class MobArena extends JavaPlugin
|
||||
try {
|
||||
createDataFolder();
|
||||
setupFormulaMacros();
|
||||
setupSavedItemsManager();
|
||||
setupArenaMaster();
|
||||
setupCommandHandler();
|
||||
|
||||
@ -134,6 +138,10 @@ public class MobArena extends JavaPlugin
|
||||
macros = FormulaMacros.create(this);
|
||||
}
|
||||
|
||||
private void setupSavedItemsManager() {
|
||||
itemman = new SavedItemsManager(this);
|
||||
}
|
||||
|
||||
private void setupArenaMaster() {
|
||||
arenaMaster = new ArenaMasterImpl(this);
|
||||
}
|
||||
@ -190,6 +198,7 @@ public class MobArena extends JavaPlugin
|
||||
reloadConfig();
|
||||
reloadGlobalMessenger();
|
||||
reloadFormulaMacros();
|
||||
reloadSavedItemsManager();
|
||||
reloadArenaMaster();
|
||||
reloadAnnouncementsFile();
|
||||
reloadSigns();
|
||||
@ -226,6 +235,10 @@ public class MobArena extends JavaPlugin
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadSavedItemsManager() {
|
||||
itemman.reload();
|
||||
}
|
||||
|
||||
private void reloadArenaMaster() {
|
||||
arenaMaster.getArenas().forEach(Arena::forceEnd);
|
||||
arenaMaster.initialize();
|
||||
@ -317,4 +330,8 @@ public class MobArena extends JavaPlugin
|
||||
public FormulaMacros getFormulaMacros() {
|
||||
return macros;
|
||||
}
|
||||
|
||||
public SavedItemsManager getSavedItemsManager() {
|
||||
return itemman;
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,15 @@ import com.garbagemule.MobArena.commands.setup.AutoGenerateCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.CheckDataCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.CheckSpawnsCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.ClassChestCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.DeleteItemCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.EditArenaCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.ListClassesCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.LoadItemCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.RemoveArenaCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.RemoveContainerCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.RemoveLeaderboardCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.RemoveSpawnpointCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.SaveItemCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.SettingCommand;
|
||||
import com.garbagemule.MobArena.commands.setup.SetupCommand;
|
||||
import com.garbagemule.MobArena.commands.user.ArenaListCommand;
|
||||
@ -340,6 +343,10 @@ public class CommandHandler implements CommandExecutor, TabCompleter
|
||||
|
||||
register(RemoveLeaderboardCommand.class);
|
||||
register(AutoGenerateCommand.class);
|
||||
|
||||
register(SaveItemCommand.class);
|
||||
register(DeleteItemCommand.class);
|
||||
register(LoadItemCommand.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,60 @@
|
||||
package com.garbagemule.MobArena.commands.setup;
|
||||
|
||||
import com.garbagemule.MobArena.commands.Command;
|
||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||
import com.garbagemule.MobArena.items.SavedItemsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@CommandInfo(
|
||||
name = "delete-item",
|
||||
pattern = "delete(-)?item",
|
||||
usage = "/ma delete-item <identifier>",
|
||||
desc = "delete the item with the given identifier",
|
||||
permission = "mobarena.setup.deleteitem"
|
||||
)
|
||||
public class DeleteItemCommand implements Command {
|
||||
|
||||
@Override
|
||||
public boolean execute(ArenaMaster am, CommandSender sender, String... args) {
|
||||
if (args.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String key = args[0];
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
try {
|
||||
items.deleteItem(key);
|
||||
} catch (Exception e) {
|
||||
am.getGlobalMessenger().tell(sender, "Couldn't delete " + ChatColor.YELLOW + key + ChatColor.RESET + ", because: " + ChatColor.RED + e.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
am.getGlobalMessenger().tell(sender, "Saved item " + ChatColor.YELLOW + key + ChatColor.RESET + " deleted.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tab(ArenaMaster am, Player player, String... args) {
|
||||
if (args.length > 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String prefix = args[0].toLowerCase();
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
List<String> keys = items.getKeys();
|
||||
|
||||
return keys.stream()
|
||||
.filter(key -> key.startsWith(prefix))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.garbagemule.MobArena.commands.setup;
|
||||
|
||||
import com.garbagemule.MobArena.Msg;
|
||||
import com.garbagemule.MobArena.commands.Command;
|
||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
||||
import com.garbagemule.MobArena.commands.Commands;
|
||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||
import com.garbagemule.MobArena.items.SavedItemsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@CommandInfo(
|
||||
name = "load-item",
|
||||
pattern = "load(-)?item",
|
||||
usage = "/ma load-item <identifier>",
|
||||
desc = "load the item saved by the given identifier into your hand",
|
||||
permission = "mobarena.setup.loaditem"
|
||||
)
|
||||
public class LoadItemCommand implements Command {
|
||||
|
||||
@Override
|
||||
public boolean execute(ArenaMaster am, CommandSender sender, String... args) {
|
||||
if (!Commands.isPlayer(sender)) {
|
||||
am.getGlobalMessenger().tell(sender, Msg.MISC_NOT_FROM_CONSOLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String key = args[0];
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
ItemStack stack = items.getItem(key);
|
||||
if (stack == null) {
|
||||
am.getGlobalMessenger().tell(sender, "No saved item with identifier " + ChatColor.YELLOW + key + ChatColor.RESET + " found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = Commands.unwrap(sender);
|
||||
player.getInventory().setItemInMainHand(stack);
|
||||
|
||||
am.getGlobalMessenger().tell(sender, "Saved item " + ChatColor.YELLOW + key + ChatColor.RESET + " loaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tab(ArenaMaster am, Player player, String... args) {
|
||||
if (args.length > 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String prefix = args[0].toLowerCase();
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
List<String> keys = items.getKeys();
|
||||
|
||||
return keys.stream()
|
||||
.filter(key -> key.startsWith(prefix))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.garbagemule.MobArena.commands.setup;
|
||||
|
||||
import com.garbagemule.MobArena.Msg;
|
||||
import com.garbagemule.MobArena.commands.Command;
|
||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
||||
import com.garbagemule.MobArena.commands.Commands;
|
||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||
import com.garbagemule.MobArena.items.SavedItemsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@CommandInfo(
|
||||
name = "save-item",
|
||||
pattern = "save(-)?item",
|
||||
usage = "/ma save-item <identifier>",
|
||||
desc = "save the currently held item for use in the config-file",
|
||||
permission = "mobarena.setup.saveitem"
|
||||
)
|
||||
public class SaveItemCommand implements Command {
|
||||
|
||||
@Override
|
||||
public boolean execute(ArenaMaster am, CommandSender sender, String... args) {
|
||||
if (!Commands.isPlayer(sender)) {
|
||||
am.getGlobalMessenger().tell(sender, Msg.MISC_NOT_FROM_CONSOLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String key = args[0];
|
||||
if (!key.matches("^[\\p{IsAlphabetic}\\d_]+$")) {
|
||||
am.getGlobalMessenger().tell(sender, "The identifier must contain only letters, numbers, and underscores");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = Commands.unwrap(sender);
|
||||
ItemStack stack = player.getInventory().getItemInMainHand();
|
||||
if (stack.getType() == Material.AIR) {
|
||||
am.getGlobalMessenger().tell(sender, "You must be holding an item.");
|
||||
return true;
|
||||
}
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
try {
|
||||
items.saveItem(key, stack);
|
||||
} catch (Exception e) {
|
||||
am.getGlobalMessenger().tell(sender, "Couldn't save " + ChatColor.YELLOW + key + ChatColor.RESET + ", because: " + ChatColor.RED + e.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
am.getGlobalMessenger().tell(sender, "Item saved as " + ChatColor.YELLOW + key + ChatColor.RESET + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tab(ArenaMaster am, Player player, String... args) {
|
||||
if (args.length > 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String prefix = args[0].toLowerCase();
|
||||
|
||||
SavedItemsManager items = am.getPlugin().getSavedItemsManager();
|
||||
List<String> keys = items.getKeys();
|
||||
|
||||
return keys.stream()
|
||||
.filter(key -> key.startsWith(prefix))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.garbagemule.MobArena.items;
|
||||
|
||||
import com.garbagemule.MobArena.MobArena;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SavedItemsManager {
|
||||
|
||||
private static final String FOLDER_NAME = "items";
|
||||
private static final String FILE_EXT = ".yml";
|
||||
private static final String YAML_KEY = "item";
|
||||
|
||||
private final MobArena plugin;
|
||||
private final Path folder;
|
||||
private final Map<String, ItemStack> items;
|
||||
|
||||
public SavedItemsManager(MobArena plugin) {
|
||||
this.plugin = plugin;
|
||||
this.folder = plugin.getDataFolder().toPath().resolve(FOLDER_NAME);
|
||||
this.items = new HashMap<>();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
try {
|
||||
Files.createDirectories(folder);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to create items folder", e);
|
||||
}
|
||||
|
||||
loadItems(folder);
|
||||
}
|
||||
|
||||
private void loadItems(Path folder) {
|
||||
items.clear();
|
||||
|
||||
try (Stream<Path> candidates = Files.list(folder)) {
|
||||
candidates.forEach(this::loadItem);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to load saved items", e);
|
||||
}
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
plugin.getLogger().info("Loaded " + items.size() + " saved item(s).");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadItem(Path path) {
|
||||
if (!Files.isRegularFile(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String filename = path.getFileName().toString();
|
||||
if (!filename.endsWith(FILE_EXT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
try {
|
||||
yaml.load(path.toFile());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to load saved item from " + path, e);
|
||||
}
|
||||
|
||||
ItemStack stack = yaml.getItemStack(YAML_KEY);
|
||||
if (stack == null) {
|
||||
throw new IllegalStateException("No item found in saved item file " + path);
|
||||
}
|
||||
|
||||
String key = filename.substring(0, filename.length() - FILE_EXT.length());
|
||||
items.put(key, stack);
|
||||
}
|
||||
|
||||
public List<String> getKeys() {
|
||||
return new ArrayList<>(items.keySet());
|
||||
}
|
||||
|
||||
public ItemStack getItem(String key) {
|
||||
ItemStack stack = items.get(key);
|
||||
if (stack == null) {
|
||||
return null;
|
||||
}
|
||||
return stack.clone();
|
||||
}
|
||||
|
||||
public void saveItem(String key, ItemStack stack) throws IOException {
|
||||
Path file = folder.resolve(key + FILE_EXT);
|
||||
Files.deleteIfExists(file);
|
||||
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
yaml.set(YAML_KEY, stack);
|
||||
yaml.save(file.toFile());
|
||||
|
||||
items.put(key, stack.clone());
|
||||
}
|
||||
|
||||
public void deleteItem(String key) throws IOException {
|
||||
items.remove(key);
|
||||
|
||||
Path file = folder.resolve(key + FILE_EXT);
|
||||
try {
|
||||
Files.delete(file);
|
||||
} catch (NoSuchFileException e) {
|
||||
throw new IllegalArgumentException("File " + file + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import com.garbagemule.MobArena.MobArena;
|
||||
import com.garbagemule.MobArena.items.SavedItemsManager;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
class SavedItemParser implements ItemStackParser {
|
||||
|
||||
private final MobArena plugin;
|
||||
|
||||
SavedItemParser(MobArena plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack parse(String s) {
|
||||
// Note that the saved items manager is not available during
|
||||
// the load procedure, so we need to defer the call to the
|
||||
// getter until we actually need it. We don't really have to
|
||||
// check for null here, but without it, we would need to set
|
||||
// up a dummy manager for the ThingManager test suite, which
|
||||
// is a bit of a hassle.
|
||||
SavedItemsManager items = plugin.getSavedItemsManager();
|
||||
if (items == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (s.contains(":")) {
|
||||
return parseWithAmount(s, items);
|
||||
} else {
|
||||
return parseSimple(s, items);
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack parseWithAmount(String s, SavedItemsManager items) {
|
||||
String[] parts = s.split(":");
|
||||
if (parts.length > 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStack stack = parseSimple(parts[0], items);
|
||||
if (stack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
int amount = Integer.parseInt(parts[1]);
|
||||
stack.setAmount(amount);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private ItemStack parseSimple(String s, SavedItemsManager items) {
|
||||
return items.getItem(s);
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,7 @@ public class ThingManager implements ThingParser {
|
||||
parsers.add(new PotionEffectThingParser());
|
||||
parsers.add(new InventoryThingParser(plugin.getServer()));
|
||||
items = parser;
|
||||
items.register(new SavedItemParser(plugin));
|
||||
}
|
||||
|
||||
public ThingManager(MobArena plugin) {
|
||||
|
@ -85,6 +85,9 @@ permissions:
|
||||
mobarena.setup.leaderboards: true
|
||||
mobarena.setup.autogenerate: true
|
||||
mobarena.setup.autodegenerate: true
|
||||
mobarena.setup.saveitem: true
|
||||
mobarena.setup.deleteitem: true
|
||||
mobarena.setup.loaditem: true
|
||||
mobarena.setup.config:
|
||||
description: Save or reload the config-file
|
||||
default: false
|
||||
@ -130,3 +133,12 @@ permissions:
|
||||
mobarena.setup.autodegenerate:
|
||||
description: Auto-degenerate an arena.
|
||||
default: false
|
||||
mobarena.setup.saveitem:
|
||||
description: Store the currently held item as a saved item.
|
||||
default: false
|
||||
mobarena.setup.deleteitem:
|
||||
description: Delete a saved item.
|
||||
default: false
|
||||
mobarena.setup.loaditem:
|
||||
description: Set a saved item as the currently held item.
|
||||
default: false
|
||||
|
Loading…
Reference in New Issue
Block a user