diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
new file mode 100644
index 0000000..322c5bb
--- /dev/null
+++ b/.github/workflows/create-release.yml
@@ -0,0 +1,34 @@
+name: Create Release
+
+on:
+ workflow_dispatch:
+
+jobs:
+ create-release:
+ name: Create Release
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'zulu'
+
+ - name: Get Previous tag
+ id: previoustag
+ uses: WyriHaximus/github-action-get-previous-tag@v1
+
+ - name: Build and package the shaded jar
+ run: mvn clean package shade:shade
+
+ - name: Upload shaded jar as a release artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: shaded-jar-artifact
+ path: target/shops-${{ steps.get-latest-tag.outputs.tag }}.jar
\ No newline at end of file
diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml
index 19f2149..83adeae 100644
--- a/.github/workflows/pipeline.yml
+++ b/.github/workflows/pipeline.yml
@@ -3,16 +3,14 @@ name: CI Pipeline
on:
push:
branches:
- - main
- develop
pull_request:
branches:
- main
- - develop
jobs:
- build:
-
+ build-and-test:
+ name: Build and Test
runs-on: ubuntu-latest
steps:
@@ -28,26 +26,44 @@ jobs:
- name: Build and run unit tests
run: mvn clean install
- #- name: Generate code coverage report
- # run: mvn jacoco:report
+ - name: Generate code coverage report
+ run: mvn jacoco:report
- #- name: Add report to PR
- # id: jacoco
- # uses: madrapps/jacoco-report@v1.3
- # with:
- # paths: ${{ github.workspace }}/build/reports/jacoco/testCoverage/testCoverage.xml
- # token: ${{ secrets.GITHUB_TOKEN }}
- # min-coverage-overall: 40
- # min-coverage-changed-files: 60
+ - name: Upload coverage reports to Codecov
+ uses: codecov/codecov-action@v3
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- - name: Run static code analysis
- run: mvn spotbugs:check
+ integration-tests:
+ name: Integration Tests
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'zulu'
- name: Run integration tests
run: mvn verify
- - name: Check documentation
- run: mvn validate site
+ documentation:
+ name: Documentation and Reports
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'zulu'
- - name: Run security scans
- run: mvn org.owasp:dependency-check-maven:check
\ No newline at end of file
+ - name: Check documentation and publish reports
+ run: mvn validate site
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 16e7095..20597af 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -51,6 +51,11 @@
+
+
+
+
+
diff --git a/README.md b/README.md
index c753860..269113b 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,11 @@
# Shops
[![CI Pipeline](https://github.com/MrSparkzz/Shops/actions/workflows/pipeline.yml/badge.svg)](https://github.com/MrSparkzz/Shops/actions/workflows/pipeline.yml)
+![Codecov](https://img.shields.io/codecov/c/github/MrSparkzz/Shops?logo=codecov&logoColor=white&label=Coverage)
Shops plugin for Bukkit/Spigot 2022+ [Built for Spigot 1.18]
+![Shops social image](https://repository-images.githubusercontent.com/388618586/0d033997-0fcd-44db-a53d-c635f8bc38f5)
+
**Depends on:** [Vault](https://github.com/MilkBowl/Vault)
**Requires economy plugin (compatible with Vault) such as:** [EssentialsX](https://github.com/EssentialsX/Essentials)
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..6062ed2
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,15 @@
+coverage:
+ status:
+ project:
+ default:
+ target: 70% # Set the default target to 60%
+ threshold: 40% # Set the threshold for the "yellow" range to 40%
+ base: auto
+ paths:
+ - "src/main/java"
+ range: 40..70 # Set the range to "40..70" for the "yellow" range
+ round: down
+ precision: 2
+parsers:
+ jacoco:
+ partials_as_hits: true #false by default
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index fdf2268..648e7a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,26 +5,37 @@
4.0.0
- 2.15.0
- 0.8.7
+
+ 3.11.0
+ 3.5.0
+ 3.0.0
+ 0.8.10
+ 4.7.3.5
+
+
+ 2.20.0
+ 32.1.1-jre
+ 4.1.2
+ 2.15.224.0.05.9.2
- 4.1.2
+ 1.33
+ 1.7.1
+ 1.7.3
+
+
+ 2.85.2
+ 1.18
+ ${minecraft.server.version}.2-R0.1-SNAPSHOT
+
+
1616
- 3.11.08.3.1
+ 3.5.03.4.5
- 2.4.3
- 3.0.0
- 2.85.2
- 1.184.0.0-M8
- UTF-8
- UTF-8
- ${minecraft.server.version}.2-R0.1-SNAPSHOT
- 4.7.3.5
- [1.7,2)
+ UTF-8Shops
@@ -48,7 +59,7 @@
BrendonButlerBrendon Butlercontact@sparkzz.net
- ${url}
+ https://github.com/BrendonButlerarchitectdeveloper
@@ -64,6 +75,7 @@
+
org.apache.maven.pluginsmaven-compiler-plugin
@@ -73,6 +85,8 @@
${maven.compiler.target}
+
+
org.apache.maven.pluginsmaven-shade-plugin
@@ -87,12 +101,29 @@
*:*
+ *.html
+ module-info.class
+ META-INF/versions/9/module-info.classMETA-INF/*.SF
+ META-INF/*.MFMETA-INF/*.DSAMETA-INF/*.RSA
+ META-INF/DEPENDENCIES
+ META-INF/LICENSE
+ META-INF/LICENSE.txt
+ META-INF/NOTICE
+ META-INF/NOTICE.txt
+ META-INF/sisu/javax.inject.Named
+ META-INF/plexus/components.xml
+
+
+ **
+
+
+ truefalse
@@ -104,6 +135,8 @@
+
+
org.apache.maven.pluginsmaven-surefire-plugin
@@ -115,6 +148,8 @@
+
+
org.jacocojacoco-maven-plugin
@@ -125,41 +160,20 @@
prepare-agent
-
- report
- test
-
- report
-
-
-
-
-
- org.owasp
- dependency-check-maven
- ${maven.dependency-check.version}
-
-
-
- check
-
-
-
- com.github.spotbugs
- spotbugs-maven-plugin
- ${spotbugs.version}
-
- spotbugs-filter.xml
-
-
+
+
org.apache.maven.pluginsmaven-site-plugin${maven.site.version}
+
+ ${project.build.encoding}
+
+
src/main/resources
@@ -197,71 +211,232 @@
+
+
+
+
+ org.spigotmc
+ spigot-api
+ ${spigot-api.version}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${jupiter.version}
+
+
+
+
+ com.github.seeseemelk
+ MockBukkit-v${minecraft.server.version}
+ ${mockbukkit.version}
+
+
+
+
+ com.github.MilkBowl
+ VaultAPI
+ ${vault-api.version}
+
+
+
+
+ com.github.MilkBowl
+ Vault
+ ${vault.version}
+
+
+
+
+ net.essentialsx
+ EssentialsX
+ ${essentialsx.version}
+
+
+
+
+ org.spongepowered
+ configurate-hocon
+ ${hocon.version}
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson-databind.version}
+
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+
+ org.jetbrains
+ annotations
+ ${jetbrains.annotations.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ ${maven.reports.version}
+
+
+
+
+
org.spigotmcspigot-api
- ${spigot-api.version}
- compile
+ provided
+
+
+
+
+ org.yaml
+ snakeyaml
+ test
-
+
+
- org.junit.jupiter
- junit-jupiter-api
- ${jupiter.version}
+ com.google.guava
+ guavatest
-
+
+
com.github.seeseemelkMockBukkit-v${minecraft.server.version}
- ${mockbukkit.version}test
-
+
com.github.MilkBowlVaultAPI
- ${vault.version}
- compile
+ provided
-
+
+
com.github.MilkBowlVault
- ${vault.version}
- test
+ provided
-
+
+
net.essentialsxEssentialsX
- 2.20.0test
-
+
+
org.spongepoweredconfigurate-hocon${hocon.version}
-
+
+
com.fasterxml.jackson.corejackson-databind
- ${jackson-databind.version}
+
+
org.jetbrainsannotations
- ${jetbrains.annotations.version}
- compile
+ provided
+
+
org.apache.maven.pluginsmaven-project-info-reports-plugin
- ${maven.reports.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven.javadoc.version}
+
+ ${project.build.encoding}
+ net.sparkzz.command.sub
+ all,-missing
+
+
+
+
+ javadoc
+
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+
+ report
+
+
+
+
+
+
+
+ org.owasp
+ dependency-check-maven
+ ${maven.dependency-check.version}
+
+ Dependency Analysis
+
+
+
+
+ aggregate
+
+
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.version}
+
+ spotbugs-filter.xml
+
+
+
+
diff --git a/src/main/java/net/sparkzz/command/AddSubCommand.java b/src/main/java/net/sparkzz/command/AddSubCommand.java
deleted file mode 100644
index 5928c1a..0000000
--- a/src/main/java/net/sparkzz/command/AddSubCommand.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package net.sparkzz.command;
-
-import net.sparkzz.shops.Store;
-import net.sparkzz.util.InventoryManagementSystem;
-import org.bukkit.Material;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-
-import static org.bukkit.ChatColor.*;
-
-/**
- * Add subcommand used for adding items to a shop
- *
- * @author Brendon Butler
- */
-public class AddSubCommand implements ISubCommand {
-
- @Override
- public boolean process(CommandSender sender, Command command, String label, String[] args)
- throws NumberFormatException {
- Material material = Material.matchMaterial(args[1]);
- Player player = (Player) sender;
- Store store = InventoryManagementSystem.locateCurrentShop(player);
- int quantity = 0;
- String message = "";
-
- if (material != null) {
- if (args.length == 3) {
- quantity = (args[2].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[2]));
-
- if (!store.containsMaterial(material)) {
- sender.sendMessage(String.format("%sThis material doesn't currently exist in the shop, use `/shop add %s` to add this item", RED, material));
- return true;
- }
-
- if (quantity < 0 && !player.hasPermission("shops.update.inf-stock")) {
- sender.sendMessage(String.format("%sYou do not have permission to set infinite stock in your Shop (try using a positive quantity)!", RED));
- return true;
- }
-
- if (!InventoryManagementSystem.canRemove(player, material, quantity)) {
- sender.sendMessage(String.format("%sYou don't have enough of this item to stock the store, try leaving out the quantity and adding it later!", RED));
- return true;
- }
-
- store.addItem(material, quantity);
- message = String.format("%sYou have successfully added %s%s%s to the shop!", GREEN, GOLD, (quantity > 0) ? String.valueOf(quantity) + GREEN + " of " + GOLD + material : material, GREEN);
- }
-
- if (args.length == 6) {
- quantity = (args[5].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[5]));
-
- double buyPrice = Double.parseDouble(args[2]);
- double sellPrice = Double.parseDouble(args[3]);
- int maxQuantity = Integer.parseInt(args[4]);
-
- if (store.containsMaterial(material)) {
- sender.sendMessage(String.format("%sThis material already exists in the shop, use `/shop update %s` to update this item", RED, material));
- return true;
- }
-
- if (quantity < 0 && !player.hasPermission("shops.update.inf-stock")) {
- sender.sendMessage(String.format("%sYou do not have permission to set infinite stock in your Shop (try using a positive quantity)!", RED));
- return true;
- }
-
- if (!InventoryManagementSystem.canRemove(player, material, quantity)) {
- sender.sendMessage(String.format("%sYou don't have enough of this item to stock the store, try leaving out the quantity and adding it later!", RED));
- return true;
- }
-
- store.addItem(material, quantity, maxQuantity, buyPrice, sellPrice);
- message = String.format("%sYou have successfully added %s%s%s to the shop with a buy price of %s%.2f%s, a sell price of %s%.2f%s, and a max quantity of %s%d%s!", GREEN, GOLD, (quantity > 0) ? String.valueOf(quantity) + GREEN + " of " + GOLD + material : material, GREEN, GOLD, buyPrice, GREEN, GOLD, sellPrice, GREEN, GOLD, maxQuantity, GREEN);
- }
-
- player.getInventory().removeItem(new ItemStack(material, quantity));
- sender.sendMessage(message);
- return true;
- }
-
- sender.sendMessage(String.format("%sInvalid material (%s)!", RED, args[1]));
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/CommandManager.java b/src/main/java/net/sparkzz/command/CommandManager.java
index 32cd0c3..f6eae37 100644
--- a/src/main/java/net/sparkzz/command/CommandManager.java
+++ b/src/main/java/net/sparkzz/command/CommandManager.java
@@ -1,5 +1,6 @@
package net.sparkzz.command;
+import net.sparkzz.util.Notifiable;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -14,8 +15,13 @@
*
* @author Brendon Butler
*/
-public abstract class CommandManager implements TabExecutor {
+public abstract class CommandManager extends Notifiable implements TabExecutor {
+ /**
+ * Registers commands to the server for the plugin
+ *
+ * @param plugin the plugin to register commands for
+ */
public static void registerCommands(JavaPlugin plugin) {
// Set command executor(s)
Optional.ofNullable(plugin.getCommand("shop"))
@@ -29,9 +35,27 @@ public static void registerCommands(JavaPlugin plugin) {
.ifPresent(cmd -> cmd.setTabCompleter(new ShopCommand()));
}
+ /**
+ * TabCompleter for generating suggestions when a player starts typing a command
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return a list of options as strings for the player
+ */
@Override
public abstract List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args);
+ /**
+ * The command processing method
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return whether the command was successful or not
+ */
@Override
public abstract boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args);
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/DeleteSubCommand.java b/src/main/java/net/sparkzz/command/DeleteSubCommand.java
deleted file mode 100644
index 80f13a4..0000000
--- a/src/main/java/net/sparkzz/command/DeleteSubCommand.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package net.sparkzz.command;
-
-import net.sparkzz.shops.Store;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static org.bukkit.ChatColor.*;
-
-/**
- * Delete subcommand used for deleting a shop
- *
- * @author Brendon Butler
- */
-public class DeleteSubCommand implements ISubCommand {
-
- private List stores = new ArrayList<>();
-
- @Override
- public boolean process(CommandSender sender, Command command, String label, String[] args)
- throws NumberFormatException {
- Optional foundStore = identifyStore(args[1]);
-
- if (stores.size() > 1) {
- sender.sendMessage(String.format("%sMultiple shops matched, please specify the shop's UUID!", RED));
- return true;
- }
-
- if (foundStore.isEmpty()) {
- sender.sendMessage(String.format("%sCould not find a store with the name and/or UUID of: %s%s%s!", RED, GOLD, args[1], RED));
- return true;
- }
-
- // TODO: determine a way to check if a player can remove all items from the shop, if they can, remove them all
- // TODO: add force flags (-f will ignore all inventory, then process) (-F will ignore all inventory and finances, then process)
-
- Store store = foundStore.get();
-
- String name = store.getName();
- boolean success = Store.STORES.remove(store);
-
- if (success)
- sender.sendMessage(String.format("%sYou have successfully deleted %s%s%s!", GREEN, GOLD, name, GREEN));
- else sender.sendMessage(String.format("%sSomething went wrong when attempting to delete the shop!", RED));
- return true;
- }
-
- private Optional identifyStore(String nameOrUUID) {
- Optional store = Optional.empty();
-
- if (nameOrUUID.contains("~")) {
- String[] input = nameOrUUID.split("~");
-
- stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(input[0]) && s.getUUID().toString().equalsIgnoreCase(input[1])).collect(Collectors.toCollection(ArrayList::new));
-
- if (stores.size() == 1)
- store = Optional.of(stores.get(0));
- } else {
- stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(nameOrUUID) || s.getUUID().toString().equalsIgnoreCase(nameOrUUID)).collect(Collectors.toCollection(ArrayList::new));
-
- if (stores.size() == 1)
- store = Optional.of(stores.get(0));
- }
-
- return store;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/ISubCommand.java b/src/main/java/net/sparkzz/command/ISubCommand.java
deleted file mode 100644
index e923544..0000000
--- a/src/main/java/net/sparkzz/command/ISubCommand.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.sparkzz.command;
-
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-/**
- * Interface for sub command layout
- *
- * @author Brendon Butler
- */
-public interface ISubCommand {
-
- boolean process(CommandSender sender, Command command, String label, String[] args);
-}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/InfoCommand.java b/src/main/java/net/sparkzz/command/InfoCommand.java
index ddd52b9..8b19c00 100644
--- a/src/main/java/net/sparkzz/command/InfoCommand.java
+++ b/src/main/java/net/sparkzz/command/InfoCommand.java
@@ -1,6 +1,7 @@
package net.sparkzz.command;
import net.sparkzz.shops.Shops;
+import net.sparkzz.util.Notifier;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@@ -14,10 +15,19 @@
*/
public class InfoCommand extends CommandManager {
+ /**
+ * The base command for the plugin to provide plugin details
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return whether the command was successful
+ */
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!sender.hasPermission("shops.cmd.shops")) {
- sender.sendMessage("§cYou don't have permission to use this command!");
+ Notifier.process(sender, Notifier.CipherKey.NO_PERMS_CMD, null);
return true;
}
@@ -25,6 +35,15 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
return true;
}
+ /**
+ * TabCompleter for generating suggestions when a player starts typing the /shops command
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return null as there are no options for this command
+ */
@Override
public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
return null;
diff --git a/src/main/java/net/sparkzz/command/SellSubCommand.java b/src/main/java/net/sparkzz/command/SellSubCommand.java
deleted file mode 100644
index db19642..0000000
--- a/src/main/java/net/sparkzz/command/SellSubCommand.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package net.sparkzz.command;
-
-import net.sparkzz.util.InventoryManagementSystem;
-import net.sparkzz.util.Transaction;
-import org.bukkit.Material;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-
-import static org.bukkit.ChatColor.*;
-
-/**
- * Sell subcommand used for processing sell transactions
- *
- * @author Brendon Butler
- */
-public class SellSubCommand implements ISubCommand {
-
- @Override
- public boolean process(CommandSender sender, Command command, String label, String[] args)
- throws NumberFormatException {
- Material material = Material.matchMaterial(args[1]);
-
- int quantity = 1;
-
- if (args.length == 3)
- quantity = (args[2].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[2]));
-
- // quantity less than or equal to 0, or greater than 2304 (max inventory capacity) is invalid
- if (quantity <= 0 || quantity > 2304) {
- sender.sendMessage(String.format("%sInvalid quantity (%d)!", RED, quantity));
- return true;
- }
-
- if (material != null) {
- Transaction transaction = new Transaction((Player) sender, new ItemStack(material, quantity), Transaction.TransactionType.SALE);
-
- if (args.length == 2 && transaction.getTotalCost() != -1) {
- sender.sendMessage(String.format("%sPrice: %s%.2f", BLUE, GREEN, transaction.getTotalCost()));
- return true;
- }
-
- if (!transaction.validateReady()) {
- sender.sendMessage(transaction.getTransactionMessage());
- return true;
- }
-
- transaction.process();
- sender.sendMessage(String.format("%sSuccess! You have sold %s%s%s of %s%s%s for %s$%.2f%s.",
- GREEN, GOLD, quantity, GREEN, GOLD, material, GREEN, GOLD, transaction.getTotalCost(), GREEN));
- return true;
- }
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/ShopCommand.java b/src/main/java/net/sparkzz/command/ShopCommand.java
index 5ea8191..f87b1de 100644
--- a/src/main/java/net/sparkzz/command/ShopCommand.java
+++ b/src/main/java/net/sparkzz/command/ShopCommand.java
@@ -1,14 +1,23 @@
package net.sparkzz.command;
+import net.sparkzz.command.sub.*;
import net.sparkzz.shops.Shops;
import net.sparkzz.shops.Store;
+import net.sparkzz.util.Notifier;
+import net.sparkzz.util.Notifier.CipherKey;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
import static org.bukkit.ChatColor.RED;
@@ -20,19 +29,29 @@
*/
public class ShopCommand extends CommandManager {
- private final Map subCommands = new HashMap<>() {{
- put("add", new AddSubCommand());
- put("buy", new BuySubCommand());
- put("create", new CreateSubCommand());
- put("delete", new DeleteSubCommand());
- put("deposit", new DepositSubCommand());
- put("sell", new SellSubCommand());
- put("transfer", new TransferSubCommand());
- put("remove", new RemoveSubCommand());
- put("update", new UpdateSubCommand());
- put("withdraw", new WithdrawSubCommand());
+ private final Map subCommands = new HashMap<>() {{
+ put("add", new AddCommand());
+ put("browse", new BrowseCommand());
+ put("buy", new BuyCommand());
+ put("create", new CreateCommand());
+ put("delete", new DeleteCommand());
+ put("deposit", new DepositCommand());
+ put("sell", new SellCommand());
+ put("transfer", new TransferCommand());
+ put("remove", new RemoveCommand());
+ put("update", new UpdateCommand());
+ put("withdraw", new WithdrawCommand());
}};
+ /**
+ * TabCompleter for generating suggestions when a player starts typing the /shop command
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return a list of options for the /shop command arguments
+ */
@Override
@SuppressWarnings("all")
public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
@@ -45,6 +64,9 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman
return subCommands.keySet().stream().toList();
if (args.length == 2) {
+ if (args[0].equalsIgnoreCase("browse"))
+ return Arrays.asList("");
+
if (args[0].equalsIgnoreCase("deposit"))
return Arrays.asList("");
@@ -132,31 +154,48 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman
return new ArrayList<>();
}
+ /**
+ * The base command for all shop user subcommands
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return whether the command was successful
+ */
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ resetAttributes();
+ setAttribute("sender", sender);
+ setArgsAsAttributes(args);
+
if (!(sender instanceof Player)) {
- sender.sendMessage(String.format("%sOnly players can use this command!", RED));
+ Notifier.process(sender, CipherKey.ONLY_PLAYERS_CMD, getAttributes());
return true;
}
try {
- if (args.length < 2) throw new IllegalArgumentException();
+ if (args.length == 0 || (args.length < 2 && !(args[0].equalsIgnoreCase("browse") || args[0].equalsIgnoreCase("update")))) throw new IllegalArgumentException();
String subCommand = args[0].toLowerCase();
if (!sender.hasPermission(String.format("shops.cmd.%s", subCommand))) {
- sender.sendMessage(String.format("%sYou do not have permission to use this command!", RED));
+ Notifier.process(sender, CipherKey.NO_PERMS_CMD, getAttributes());
return true;
}
if (subCommands.containsKey(subCommand))
return subCommands.get(subCommand).process(sender, command, label, args);
} catch (NumberFormatException exception) {
- sender.sendMessage(String.format("%sInvalid numerical value (%s)", RED, exception.getMessage().subSequence(exception.getMessage().indexOf("\"") + 1, exception.getMessage().length() - 1)));
+ sender.sendMessage(String.format("%sInvalid numerical value (%s)!", RED, exception.getMessage().subSequence(exception.getMessage().indexOf("\"") + 1, exception.getMessage().length() - 1)));
} catch (IllegalArgumentException exception) {
- sender.sendMessage(String.format("%sInvalid number of arguments!", RED));
+ Notifier.process(sender, CipherKey.INVALID_ARG_CNT, getAttributes());
}
+ // send the CommandSender a usage message based on the subcommand instead of the default
+ if (command.getName().equalsIgnoreCase("shop") && args.length > 0)
+ return Notifier.usageSubCommand(sender, args);
+
return false;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/SubCommand.java b/src/main/java/net/sparkzz/command/SubCommand.java
new file mode 100644
index 0000000..12d8e58
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/SubCommand.java
@@ -0,0 +1,59 @@
+package net.sparkzz.command;
+
+import net.sparkzz.shops.Store;
+import net.sparkzz.util.Notifiable;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Interface for sub command layout
+ *
+ * @author Brendon Butler
+ */
+public abstract class SubCommand extends Notifiable {
+
+ /**
+ * Holds any stores that are identified based on the identifyStore method
+ */
+ protected List stores = new ArrayList<>();
+
+ /**
+ * The process method is where the subcommands are built
+ *
+ * @param sender the sender attempting the command
+ * @param command the command to be processed
+ * @param label the command label
+ * @param args the arguments following the command
+ * @return whether the command was successful
+ */
+ public abstract boolean process(CommandSender sender, Command command, String label, String[] args);
+
+ /**
+ * The identifyStore method is a common method for identifying a store based on a string name, UUID or a combination
+ * using the format name~UUID
+ *
+ * @param nameOrUUID input name or UUID
+ * @return the optional store if found or optional empty if not found or duplicates are found
+ */
+ protected Optional identifyStore(String nameOrUUID) {
+ Optional store = Optional.empty();
+
+ if (nameOrUUID.contains("~")) {
+ String[] input = nameOrUUID.split("~");
+
+ stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(input[0]) && s.getUUID().toString().equalsIgnoreCase(input[1])).collect(Collectors.toCollection(ArrayList::new));
+ } else {
+ stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(nameOrUUID) || s.getUUID().toString().equalsIgnoreCase(nameOrUUID)).collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ if (stores.size() == 1)
+ store = Optional.of(stores.get(0));
+
+ return store;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/TransferSubCommand.java b/src/main/java/net/sparkzz/command/TransferSubCommand.java
deleted file mode 100644
index 2b5aeeb..0000000
--- a/src/main/java/net/sparkzz/command/TransferSubCommand.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package net.sparkzz.command;
-
-import net.sparkzz.shops.Shops;
-import net.sparkzz.shops.Store;
-import org.bukkit.OfflinePlayer;
-import org.bukkit.Server;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import static org.bukkit.ChatColor.*;
-
-/**
- * Transfer subcommand used for transferring a shop from one player to another
- *
- * @author Brendon Butler
- */
-public class TransferSubCommand implements ISubCommand {
-
- private List stores = new ArrayList<>();
-
- @Override
- public boolean process(CommandSender sender, Command command, String label, String[] args)
- throws NumberFormatException {
- Optional foundStore = identifyStore(args[1]);
-
- if (stores.size() > 1) {
- sender.sendMessage(String.format("%sMultiple shops matched, please specify the shop's UUID!", RED));
- return true;
- }
-
- if (foundStore.isEmpty()) {
- sender.sendMessage(String.format("%sCould not find a store with the name and/or UUID of: %s%s%s!", RED, GOLD, args[1], RED));
- return true;
- }
-
- boolean isUUID = args[2].matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
-
- Server server = Shops.getPlugin(Shops.class).getServer();
- OfflinePlayer targetPlayer = (!isUUID) ? server.getPlayer(args[2]) : server.getOfflinePlayer(UUID.fromString(args[2]));
-
- if (targetPlayer == null) {
- sender.sendMessage(String.format("%sPlayer (%s) not found!", RED, args[2]));
- return true;
- }
-
- Store store = foundStore.get();
-
- store.setOwner(targetPlayer.getUniqueId());
- sender.sendMessage(String.format("%sYou have successfully transferred %s%s%s to player %s%s%s!", GREEN, GOLD, store.getName(), GREEN, GOLD, (targetPlayer.getName() == null) ? args[2] : targetPlayer.getName(), GREEN));
- return true;
- }
-
- private Optional identifyStore(String nameOrUUID) {
- Optional store = Optional.empty();
-
- if (nameOrUUID.contains("~")) {
- String[] input = nameOrUUID.split("~");
-
- stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(input[0]) && s.getUUID().toString().equalsIgnoreCase(input[1])).collect(Collectors.toCollection(ArrayList::new));
-
- if (stores.size() == 1)
- store = Optional.of(stores.get(0));
- } else {
- stores = Store.STORES.stream().filter(s -> s.getName().equalsIgnoreCase(nameOrUUID) || s.getUUID().toString().equalsIgnoreCase(nameOrUUID)).collect(Collectors.toCollection(ArrayList::new));
-
- if (stores.size() == 1)
- store = Optional.of(stores.get(0));
- }
-
- return store;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/sub/AddCommand.java b/src/main/java/net/sparkzz/command/sub/AddCommand.java
new file mode 100644
index 0000000..bc9d38d
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/sub/AddCommand.java
@@ -0,0 +1,92 @@
+package net.sparkzz.command.sub;
+
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.shops.Store;
+import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
+import org.bukkit.Material;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import static net.sparkzz.util.Notifier.CipherKey.*;
+
+/**
+ * Add subcommand used for adding items to a shop
+ *
+ * @author Brendon Butler
+ */
+public class AddCommand extends SubCommand {
+
+ @Override
+ public boolean process(CommandSender sender, Command command, String label, String[] args)
+ throws NumberFormatException {
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Material material = (Material) setAttribute("material", Material.matchMaterial(args[1]));
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = (Store) setAttribute("store", InventoryManagementSystem.locateCurrentStore(player));
+ int quantity = (Integer) setAttribute("quantity", 0);
+ String message = "";
+
+ if (material != null) {
+ if (args.length == 3) {
+ quantity = (int) setAttribute("quantity", args[2].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[2]));
+
+ if (!store.containsMaterial(material)) {
+ Notifier.process(sender, MATERIAL_MISSING_STORE, getAttributes());
+ return true;
+ }
+
+ if (quantity < 0 && !player.hasPermission("shops.update.inf-stock")) {
+ Notifier.process(sender, NO_PERMS_INF_STOCK, getAttributes());
+ return true;
+ }
+
+ if (!InventoryManagementSystem.canRemove(player, material, quantity)) {
+ Notifier.process(sender, INSUFFICIENT_STOCK_PLAYER, getAttributes());
+ return true;
+ }
+
+ store.addItem(material, quantity);
+ message = Notifier.compose((quantity > 0 ? ADD_SUCCESS_QUANTITY : ADD_SUCCESS), getAttributes());
+ }
+
+ if (args.length == 6) {
+ quantity = (int) setAttribute("quantity", args[5].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[5]));
+
+ double buyPrice = (double) setAttribute("buy-price", Double.parseDouble(args[2]));
+ double sellPrice = (double) setAttribute("sell-price", Double.parseDouble(args[3]));
+ int maxQuantity = (int) setAttribute("max-quantity", Integer.parseInt(args[4]));
+
+ if (store.containsMaterial(material)) {
+ Notifier.process(sender, MATERIAL_EXISTS_STORE, getAttributes());
+ return true;
+ }
+
+ if (quantity < 0 && !player.hasPermission("shops.update.inf-stock")) {
+ Notifier.process(sender, NO_PERMS_INF_STOCK, getAttributes());
+ return true;
+ }
+
+ if (!InventoryManagementSystem.canRemove(player, material, quantity)) {
+ Notifier.process(sender, INSUFFICIENT_STOCK_PLAYER, getAttributes());
+ return true;
+ }
+
+ store.addItem(material, quantity, maxQuantity, buyPrice, sellPrice);
+ message = Notifier.compose((quantity > 0 ? ADDED_MATERIAL_TO_STORE_QUANTITY : ADDED_MATERIAL_TO_STORE), getAttributes());
+ }
+
+ if (quantity <= 0 && message.isBlank()) throw new IllegalArgumentException();
+ player.getInventory().removeItem(new ItemStack(material, quantity));
+ sender.sendMessage(message);
+ return true;
+ }
+
+ setAttribute("material", args[1]);
+ Notifier.process(sender, INVALID_MATERIAL, getAttributes());
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/sub/BrowseCommand.java b/src/main/java/net/sparkzz/command/sub/BrowseCommand.java
new file mode 100644
index 0000000..aa45fc8
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/sub/BrowseCommand.java
@@ -0,0 +1,46 @@
+package net.sparkzz.command.sub;
+
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.shops.Store;
+import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import static net.sparkzz.util.Notifier.CipherKey.INVALID_PAGE_NUM;
+import static net.sparkzz.util.Notifier.CipherKey.STORE_NOT_FOUND;
+
+/**
+ * Browse subcommand used for browsing items to a shop
+ *
+ * @author Brendon Butler
+ */
+public class BrowseCommand extends SubCommand {
+
+ @Override
+ public boolean process(CommandSender sender, Command command, String label, String[] args)
+ throws NumberFormatException {
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = (Store) setAttribute("store", InventoryManagementSystem.locateCurrentStore(player));
+
+ int pageNumber = (args.length > 1) ? Integer.parseInt(args[1]) : 1;
+
+ if (store != null) {
+ String page = Notifier.Paginator.buildBrowsePage(store, pageNumber);
+
+ if (page == null) {
+ Notifier.process(sender, INVALID_PAGE_NUM, getAttributes());
+ return true;
+ }
+
+ sender.sendMessage(page);
+ return true;
+ }
+
+ Notifier.process(sender, STORE_NOT_FOUND, getAttributes());
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/BuySubCommand.java b/src/main/java/net/sparkzz/command/sub/BuyCommand.java
similarity index 50%
rename from src/main/java/net/sparkzz/command/BuySubCommand.java
rename to src/main/java/net/sparkzz/command/sub/BuyCommand.java
index f685aeb..35ae312 100644
--- a/src/main/java/net/sparkzz/command/BuySubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/BuyCommand.java
@@ -1,5 +1,8 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
import net.sparkzz.util.Transaction;
import org.bukkit.Material;
import org.bukkit.command.Command;
@@ -7,50 +10,55 @@
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
-import static org.bukkit.ChatColor.*;
+import static net.sparkzz.util.Notifier.CipherKey.*;
/**
* Buy subcommand used for processing buy transactions
*
* @author Brendon Butler
*/
-public class BuySubCommand implements ISubCommand {
+public class BuyCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
- Material material = Material.matchMaterial(args[1]);
-
- int quantity = 1;
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Material material = (Material) setAttribute("material", Material.matchMaterial(args[1]));
+ Player player = (Player) setAttribute("sender", sender);
+ int quantity = (Integer) setAttribute("quantity", 1);
+ setAttribute("store", InventoryManagementSystem.locateCurrentStore(player));
if (args.length == 3)
- quantity = Integer.parseInt(args[2]);
+ quantity = (Integer) setAttribute("quantity", Integer.parseInt(args[2]));
// quantity less than or equal to 0, or greater than 2304 (max inventory capacity) is invalid
if (quantity <= 0 || quantity > 2304) {
- sender.sendMessage(String.format("%sInvalid quantity (%d)!", RED, quantity));
+ Notifier.process(sender, INVALID_QUANTITY, getAttributes());
return true;
}
if (material != null) {
Transaction transaction = new Transaction((Player) sender, new ItemStack(material, quantity), Transaction.TransactionType.PURCHASE);
+ setAttribute("cost", transaction.getTotalCost());
if (args.length == 2 && transaction.getTotalCost() != -1) {
- sender.sendMessage(String.format("%sPrice: %s%.2f", BLUE, GREEN, transaction.getTotalCost()));
+ Notifier.process(sender, PRICE, getAttributes());
return true;
}
if (!transaction.validateReady()) {
- sender.sendMessage(transaction.getTransactionMessage());
+ transaction.getMessage().processIndividual(sender);
return true;
}
transaction.process();
- sender.sendMessage(String.format("%sSuccess! You have purchased %s%s%s of %s%s%s for %s$%.2f%s.",
- GREEN, GOLD, quantity, GREEN, GOLD, material, GREEN, GOLD, transaction.getTotalCost(), GREEN));
+ Notifier.process(sender, BUY_SUCCESS, getAttributes());
return true;
}
+ setAttribute("material", args[1]);
+ Notifier.process(sender, INVALID_MATERIAL, getAttributes());
return false;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/CreateSubCommand.java b/src/main/java/net/sparkzz/command/sub/CreateCommand.java
similarity index 59%
rename from src/main/java/net/sparkzz/command/CreateSubCommand.java
rename to src/main/java/net/sparkzz/command/sub/CreateCommand.java
index 334fc5c..5a6d84a 100644
--- a/src/main/java/net/sparkzz/command/CreateSubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/CreateCommand.java
@@ -1,27 +1,31 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
import net.sparkzz.shops.Store;
+import net.sparkzz.util.Notifier;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import static org.bukkit.ChatColor.GOLD;
-import static org.bukkit.ChatColor.GREEN;
-
/**
* Create subcommand used for creating a shop
*
* @author Brendon Butler
*/
-public class CreateSubCommand implements ISubCommand {
+public class CreateCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
+ resetAttributes();
+ setAttribute("sender", sender);
+ setArgsAsAttributes(args);
// TODO: new permission to limit a player to a number of shops (shops.create.)
Store store = new Store(args[1], ((Player) sender).getUniqueId());
- sender.sendMessage(String.format("%sYou have successfully created %s%s%s!", GREEN, GOLD, store.getName(), GREEN));
+
+ setAttribute("store", store.getName());
+ Notifier.process(sender, Notifier.CipherKey.STORE_CREATE_SUCCESS, getAttributes());
return true;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/sub/DeleteCommand.java b/src/main/java/net/sparkzz/command/sub/DeleteCommand.java
new file mode 100644
index 0000000..80239be
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/sub/DeleteCommand.java
@@ -0,0 +1,52 @@
+package net.sparkzz.command.sub;
+
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.shops.Store;
+import net.sparkzz.util.Notifier;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.util.Optional;
+
+import static net.sparkzz.util.Notifier.CipherKey.*;
+
+/**
+ * Delete subcommand used for deleting a shop
+ *
+ * @author Brendon Butler
+ */
+public class DeleteCommand extends SubCommand {
+
+ @Override
+ public boolean process(CommandSender sender, Command command, String label, String[] args)
+ throws NumberFormatException {
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Optional foundStore = identifyStore(args[1]);
+ setAttribute("sender", sender);
+ setAttribute("store", (foundStore.isPresent() ? foundStore.get() : args[1]));
+
+ if (stores.size() > 1) {
+ Notifier.process(sender, STORE_MULTI_MATCH, getAttributes());
+ return true;
+ }
+
+ if (foundStore.isEmpty()) {
+ Notifier.process(sender, STORE_NO_STORE_FOUND, getAttributes());
+ return true;
+ }
+
+ // TODO: determine a way to check if a player can remove all items from the shop, if they can, remove them all
+ // TODO: add force flags (-f will ignore all inventory, then process) (-F will ignore all inventory and finances, then process)
+
+ Store store = foundStore.get();
+
+ setAttribute("store", store.getName());
+ boolean success = Store.STORES.remove(store);
+
+ if (success)
+ Notifier.process(sender, STORE_DELETE_SUCCESS, getAttributes());
+ else Notifier.process(sender, STORE_DELETE_FAIL, getAttributes());
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/DepositSubCommand.java b/src/main/java/net/sparkzz/command/sub/DepositCommand.java
similarity index 53%
rename from src/main/java/net/sparkzz/command/DepositSubCommand.java
rename to src/main/java/net/sparkzz/command/sub/DepositCommand.java
index db0a7c5..171990f 100644
--- a/src/main/java/net/sparkzz/command/DepositSubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/DepositCommand.java
@@ -1,48 +1,53 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
import net.sparkzz.shops.Shops;
import net.sparkzz.shops.Store;
import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import static org.bukkit.ChatColor.*;
+import static net.sparkzz.util.Notifier.CipherKey.*;
/**
* Deposit subcommand used for depositing finances into a shop
*
* @author Brendon Butler
*/
-public class DepositSubCommand implements ISubCommand {
+public class DepositCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
- Player player = (Player) sender;
- Store store = InventoryManagementSystem.locateCurrentShop(player);
- double amount = Double.parseDouble(args[1]);
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = InventoryManagementSystem.locateCurrentStore(player);
+ setAttribute("store", store.getName());
+ double amount = (Double) setAttribute("amount", Double.parseDouble(args[1]));
if (amount < 0) throw new NumberFormatException(String.format("Invalid amount: \"%s\"", args[1]));
if (!store.getOwner().equals(player.getUniqueId())) {
- player.sendMessage(String.format("%sYou are not the owner of this shop, you cannot perform this command!", RED));
+ Notifier.process(sender, NOT_OWNER, getAttributes());
return true;
}
if (store.hasInfiniteFunds()) {
- player.sendMessage(String.format("%sThis store has infinite funds, depositing funds isn't necessary!", RED));
+ Notifier.process(sender, DEPOSIT_INF_FUNDS, getAttributes());
return true;
}
if (amount > Shops.getEconomy().getBalance(player)) {
- player.sendMessage(String.format("%sYou have insufficient funds!", RED));
+ Notifier.process(sender, INSUFFICIENT_FUNDS_PLAYER, getAttributes());
return true;
}
Shops.getEconomy().withdrawPlayer(player, amount);
store.addFunds(amount);
- sender.sendMessage(String.format("%sYou have successfully deposited %s%s%s to the shop!", GREEN, GOLD, amount, GREEN));
+ Notifier.process(sender, DEPOSIT_SUCCESS, getAttributes());
return true;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/RemoveSubCommand.java b/src/main/java/net/sparkzz/command/sub/RemoveCommand.java
similarity index 53%
rename from src/main/java/net/sparkzz/command/RemoveSubCommand.java
rename to src/main/java/net/sparkzz/command/sub/RemoveCommand.java
index 43755a3..b171f60 100644
--- a/src/main/java/net/sparkzz/command/RemoveSubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/RemoveCommand.java
@@ -1,28 +1,33 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
import net.sparkzz.shops.Store;
import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
-import static org.bukkit.ChatColor.*;
+import static net.sparkzz.util.Notifier.CipherKey.*;
/**
* Remove subcommand used for removing items from a shop
*
* @author Brendon Butler
*/
-public class RemoveSubCommand implements ISubCommand {
+public class RemoveCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
- Material material = Material.matchMaterial(args[1]);
- Player player = (Player) sender;
- Store store = InventoryManagementSystem.locateCurrentShop(player);
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Material material = (Material) setAttribute("material", Material.matchMaterial(args[1]));
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = InventoryManagementSystem.locateCurrentStore(player);
+ setAttribute("store", store.getName());
int quantity = 0;
@@ -31,19 +36,19 @@ public boolean process(CommandSender sender, Command command, String label, Stri
if (material != null) {
if (!store.containsMaterial(material)) {
- sender.sendMessage(String.format("%sThis material (%s) does not currently exist in the shop!", RED, material));
+ Notifier.process(sender, MATERIAL_MISSING_STORE, getAttributes());
return true;
}
- int moveQuantity = (quantity <= 0) ? store.getAttributes(material).get("quantity").intValue() : quantity;
+ int moveQuantity = (Integer) setAttribute("quantity", (quantity <= 0) ? store.getAttributes(material).get("quantity").intValue() : quantity);
if (!InventoryManagementSystem.containsAtLeast(store, new ItemStack(material, moveQuantity))) {
- sender.sendMessage(String.format("%sThe Shop currently doesn't have enough %s%s%s!", RED, GOLD, material, RED));
+ Notifier.process(sender, INSUFFICIENT_INV_STORE, getAttributes());
return true;
}
if (!InventoryManagementSystem.canInsert(player, material, moveQuantity)) {
- sender.sendMessage(String.format("%sYou don't have enough inventory space to remove %s%s%s from your shop, please try specifying a quantity then removing once the shop quantity is lesser!", RED, GOLD, moveQuantity + " " + material, RED));
+ Notifier.process(sender, REMOVE_INSUFFICIENT_INV_PLAYER, getAttributes());
return true;
}
@@ -53,11 +58,12 @@ public boolean process(CommandSender sender, Command command, String label, Stri
player.getInventory().addItem(new ItemStack(material, moveQuantity));
- sender.sendMessage(String.format("%sYou have successfully removed %s%s%s from the shop!", GREEN, GOLD, (quantity > 0) ? String.valueOf(quantity) + GREEN + " of " + GOLD + material : material, GREEN));
+ Notifier.process(sender, (quantity > 0 ? REMOVE_SUCCESS_QUANTITY : REMOVE_SUCCESS), getAttributes());
return true;
}
- sender.sendMessage(String.format("%sInvalid material (%s)!", RED, args[1]));
+ setAttribute("material", args[1]);
+ Notifier.process(sender, INVALID_MATERIAL, getAttributes());
return false;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/sub/SellCommand.java b/src/main/java/net/sparkzz/command/sub/SellCommand.java
new file mode 100644
index 0000000..94a1b5e
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/sub/SellCommand.java
@@ -0,0 +1,64 @@
+package net.sparkzz.command.sub;
+
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
+import net.sparkzz.util.Transaction;
+import org.bukkit.Material;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import static net.sparkzz.util.Notifier.CipherKey.*;
+
+/**
+ * Sell subcommand used for processing sell transactions
+ *
+ * @author Brendon Butler
+ */
+public class SellCommand extends SubCommand {
+
+ @Override
+ public boolean process(CommandSender sender, Command command, String label, String[] args)
+ throws NumberFormatException {
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Material material = (Material) setAttribute("material", Material.matchMaterial(args[1]));
+ Player player = (Player) setAttribute("sender", sender);
+ setAttribute("store", InventoryManagementSystem.locateCurrentStore(player));
+ int quantity = (Integer) setAttribute("quantity", 1);
+
+ if (args.length == 3)
+ quantity = (Integer) setAttribute("quantity", args[2].equalsIgnoreCase("all") ? InventoryManagementSystem.countQuantity((Player) sender, material) : Integer.parseInt(args[2]));
+
+ // quantity less than or equal to 0, or greater than 2304 (max inventory capacity) is invalid
+ if (quantity <= 0 || quantity > 2304) {
+ Notifier.process(sender, INVALID_QUANTITY, getAttributes());
+ return true;
+ }
+
+ if (material != null) {
+ Transaction transaction = new Transaction((Player) sender, new ItemStack(material, quantity), Transaction.TransactionType.SALE);
+ setAttribute("cost", transaction.getTotalCost());
+
+ if (args.length == 2 && transaction.getTotalCost() != -1) {
+ Notifier.process(sender, PRICE, getAttributes());
+ return true;
+ }
+
+ if (!transaction.validateReady()) {
+ transaction.getMessage().processIndividual(sender);
+ return true;
+ }
+
+ transaction.process();
+ Notifier.process(sender, SELL_SUCCESS, getAttributes());
+ return true;
+ }
+
+ setAttribute("material", args[1]);
+ Notifier.process(sender, INVALID_MATERIAL, getAttributes());
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/sub/TransferCommand.java b/src/main/java/net/sparkzz/command/sub/TransferCommand.java
new file mode 100644
index 0000000..a36267b
--- /dev/null
+++ b/src/main/java/net/sparkzz/command/sub/TransferCommand.java
@@ -0,0 +1,61 @@
+package net.sparkzz.command.sub;
+
+import net.sparkzz.command.SubCommand;
+import net.sparkzz.shops.Shops;
+import net.sparkzz.shops.Store;
+import net.sparkzz.util.Notifier;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.Server;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import static net.sparkzz.util.Notifier.CipherKey.*;
+
+/**
+ * Transfer subcommand used for transferring a shop from one player to another
+ *
+ * @author Brendon Butler
+ */
+public class TransferCommand extends SubCommand {
+
+ @Override
+ public boolean process(CommandSender sender, Command command, String label, String[] args)
+ throws NumberFormatException {
+ resetAttributes();
+ setArgsAsAttributes(args);
+ setAttribute("sender", sender);
+ Optional foundStore = identifyStore(args[1]);
+ setAttribute("store", (foundStore.isPresent() ? foundStore.get() : args[1]));
+
+ if (stores.size() > 1) {
+ Notifier.process(sender, STORE_MULTI_MATCH, getAttributes());
+ return true;
+ }
+
+ if (foundStore.isEmpty()) {
+ Notifier.process(sender, STORE_NO_STORE_FOUND, getAttributes());
+ return true;
+ }
+
+ boolean isUUID = args[2].matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
+
+ // TODO: remove mock references once Server mocking is updated to fix issues with getServer()
+ Server server = (!Shops.isTest()) ? Shops.getPlugin(Shops.class).getServer() : Shops.getMockServer();
+ OfflinePlayer targetPlayer = (!isUUID) ? server.getPlayer(args[2]) : server.getOfflinePlayer(UUID.fromString(args[2]));
+
+ if (targetPlayer == null) {
+ Notifier.process(sender, PLAYER_NOT_FOUND, getAttributes());
+ return true;
+ }
+
+ Store store = foundStore.get();
+
+ setAttribute("target", targetPlayer.getName());
+ store.setOwner(targetPlayer.getUniqueId());
+ Notifier.process(sender, STORE_TRANSFER_SUCCESS, getAttributes());
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/UpdateSubCommand.java b/src/main/java/net/sparkzz/command/sub/UpdateCommand.java
similarity index 65%
rename from src/main/java/net/sparkzz/command/UpdateSubCommand.java
rename to src/main/java/net/sparkzz/command/sub/UpdateCommand.java
index 118a880..0ab8e03 100644
--- a/src/main/java/net/sparkzz/command/UpdateSubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/UpdateCommand.java
@@ -1,7 +1,9 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
import net.sparkzz.shops.Store;
import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@@ -10,28 +12,30 @@
import java.util.HashMap;
import java.util.Map;
-import static org.bukkit.ChatColor.*;
+import static net.sparkzz.util.Notifier.CipherKey.*;
/**
* Update subcommand used for updating items in a shop
*
* @author Brendon Butler
*/
-public class UpdateSubCommand implements ISubCommand {
+public class UpdateCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
- Material material = Material.matchMaterial(args[1]);
- Player player = (Player) sender;
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = InventoryManagementSystem.locateCurrentStore(player);
+ setAttribute("store", store.getName());
+ if (args.length >= 2) setAttribute("material", args[1]);
if (args.length == 3) {
- Store store = InventoryManagementSystem.locateCurrentShop(player);
-
switch (args[1].toLowerCase()) {
case "infinite-funds" -> {
if (!player.hasPermission("shops.update.inf-funds")) {
- sender.sendMessage(String.format("%sYou do not have permission to set infinite funds in your Shop!", RED));
+ Notifier.process(sender, NO_PERMS_INF_FUNDS, getAttributes());
return true;
}
@@ -39,7 +43,7 @@ public boolean process(CommandSender sender, Command command, String label, Stri
}
case "infinite-stock" -> {
if (!player.hasPermission("shops.update.inf-stock")) {
- sender.sendMessage(String.format("%sYou do not have permission to set infinite stock in your Shop!", RED));
+ Notifier.process(sender, NO_PERMS_INF_STOCK, getAttributes());
return true;
}
@@ -51,23 +55,23 @@ public boolean process(CommandSender sender, Command command, String label, Stri
}
}
- sender.sendMessage(String.format("%sYou have successfully updated %s%s%s to %s%s%s in the shop!", GREEN, GOLD, args[1], GREEN, GOLD, args[2], GREEN));
-
+ Notifier.process(sender, STORE_UPDATE_SUCCESS, getAttributes());
return true;
}
if (args.length < 4)
return false;
+ Material material = Material.matchMaterial(args[1]);
+
double value = (args[3].equalsIgnoreCase("true")) ? -1D :
(args[3].equalsIgnoreCase("false") ? 0D : Double.parseDouble(args[3]));
if (material != null) {
- Store store = InventoryManagementSystem.locateCurrentShop(player);
Map inputMapping = new HashMap<>();
if (!store.containsMaterial(material)) {
- sender.sendMessage(String.format("%sThis material (%s) does not currently exist in the shop!", RED, material));
+ Notifier.process(sender, MATERIAL_MISSING_STORE, getAttributes());
return true;
}
@@ -82,23 +86,22 @@ public boolean process(CommandSender sender, Command command, String label, Stri
if (mapped.equals("quantity")) {
if (!player.hasPermission("shops.update.inf-stock")) {
- sender.sendMessage(String.format("%sYou do not have permission to set infinite stock in your Shop!", RED));
+ Notifier.process(sender, NO_PERMS_INF_STOCK, getAttributes());
return true;
}
if (args[3].equalsIgnoreCase("true") && store.getAttributes(material).get("quantity").intValue() > 0) {
- player.sendMessage(String.format("%sPlease ensure there is no stock in the shop for this item and try again", RED));
+ Notifier.process(sender, STORE_UPDATE_NO_STOCK, getAttributes());
return true;
}
}
store.getItems().get(material).replace(mapped, (mapped.equals("max_quantity") || mapped.equals("quantity")) ? (int) value : value);
-
- sender.sendMessage(String.format("%sYou have successfully updated %s%s%s for %s%s%s in the shop to %s%s%s!", GREEN, GOLD, args[2], GREEN, GOLD, material, GREEN, GOLD, args[3], GREEN));
+ Notifier.process(sender, STORE_UPDATE_SUCCESS_2, getAttributes());
return true;
}
- sender.sendMessage(String.format("%sInvalid material (%s)!", RED, args[1]));
+ Notifier.process(sender, INVALID_MATERIAL, getAttributes());
return false;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/command/WithdrawSubCommand.java b/src/main/java/net/sparkzz/command/sub/WithdrawCommand.java
similarity index 52%
rename from src/main/java/net/sparkzz/command/WithdrawSubCommand.java
rename to src/main/java/net/sparkzz/command/sub/WithdrawCommand.java
index 2d4b395..5827541 100644
--- a/src/main/java/net/sparkzz/command/WithdrawSubCommand.java
+++ b/src/main/java/net/sparkzz/command/sub/WithdrawCommand.java
@@ -1,43 +1,47 @@
-package net.sparkzz.command;
+package net.sparkzz.command.sub;
+import net.sparkzz.command.SubCommand;
import net.sparkzz.shops.Shops;
import net.sparkzz.shops.Store;
import net.sparkzz.util.InventoryManagementSystem;
+import net.sparkzz.util.Notifier;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import static org.bukkit.ChatColor.*;
+import static net.sparkzz.util.Notifier.CipherKey.*;
/**
* Withdraw subcommand used for withdrawing funds from a shop
*
* @author Brendon Butler
*/
-public class WithdrawSubCommand implements ISubCommand {
+public class WithdrawCommand extends SubCommand {
@Override
public boolean process(CommandSender sender, Command command, String label, String[] args)
throws NumberFormatException {
- Player player = (Player) sender;
- Store store = InventoryManagementSystem.locateCurrentShop(player);
- double amount = (args[1].equalsIgnoreCase("all")) ? store.getBalance() : Double.parseDouble(args[1]);
+ resetAttributes();
+ setArgsAsAttributes(args);
+ Player player = (Player) setAttribute("sender", sender);
+ Store store = (Store) setAttribute("store", InventoryManagementSystem.locateCurrentStore(player));
+ double amount = (Double) setAttribute("amount", (args[1].equalsIgnoreCase("all")) ? store.getBalance() : Double.parseDouble(args[1]));
if (amount < 0) throw new NumberFormatException(String.format("Invalid amount: \"%s\"", args[1]));
if (!store.getOwner().equals(player.getUniqueId())) {
- player.sendMessage(String.format("%sYou are not the owner of this shop, you cannot perform this command!", RED));
+ Notifier.process(sender, NOT_OWNER, getAttributes());
return true;
}
if (amount > store.getBalance()) {
- player.sendMessage(String.format("%sThe Store has insufficient funds!", RED));
+ Notifier.process(sender, INSUFFICIENT_FUNDS_STORE, getAttributes());
return true;
}
store.removeFunds(amount);
Shops.getEconomy().depositPlayer(player, amount);
- sender.sendMessage(String.format("%sYou have successfully withdrawn %s%s%s from the shop!", GREEN, GOLD, amount, GREEN));
+ Notifier.process(sender, WITHDRAW_SUCCESS, getAttributes());
return true;
}
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/shops/Shops.java b/src/main/java/net/sparkzz/shops/Shops.java
index 870e5dd..fc63ff4 100644
--- a/src/main/java/net/sparkzz/shops/Shops.java
+++ b/src/main/java/net/sparkzz/shops/Shops.java
@@ -3,6 +3,7 @@
import net.milkbowl.vault.economy.Economy;
import net.sparkzz.command.CommandManager;
import net.sparkzz.util.Warehouse;
+import org.bukkit.Server;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
@@ -18,17 +19,29 @@
*/
public class Shops extends JavaPlugin {
- public static Store shop;
- public static Economy econ;
- public static PluginDescriptionFile desc;
+ private static boolean isTest = false;
+ private static Server server;
+ private static Store shop;
+ private static Economy econ;
+ private static PluginDescriptionFile desc;
private final Logger log = getLogger();
- private boolean isTest = false;
+ /**
+ * Default constructor for Spigot plugin
+ */
public Shops() {
super();
}
+ /**
+ * Constructor for MockBukkit mocking
+ *
+ * @param loader mocked plugin loader
+ * @param description the plugin description which describes the plugin to the loader
+ * @param dataFolder the data folder containing the plugin data
+ * @param file the file of the plugin
+ */
protected Shops(
JavaPluginLoader loader,
PluginDescriptionFile description,
@@ -38,6 +51,9 @@ protected Shops(
isTest = true;
}
+ /**
+ * Tears down the plugin and saves configurations
+ */
@Override
public void onDisable() {
if (!isTest) Warehouse.saveConfig();
@@ -45,6 +61,9 @@ public void onDisable() {
log.info("Shops has been disabled!");
}
+ /**
+ * Configures the plugin and all the plugin's resources
+ */
@Override
public void onEnable() {
if (!setupEconomy()) {
@@ -75,20 +94,66 @@ private boolean setupEconomy() {
return econ != null;
}
+ /**
+ * Checks whether the plugin is configured in test mode
+ *
+ * @return whether test mode is configured
+ */
+ public static boolean isTest() {
+ return isTest;
+ }
+
+ /**
+ * Get the configured economy configuration from Vault
+ *
+ * @return the economy configuration
+ */
+ public static Economy getEconomy() {
+ return econ;
+ }
+
+ /**
+ * Get the plugin description from Shops
+ *
+ * @return the plugin description
+ */
public static PluginDescriptionFile getDesc() {
return desc;
}
- public static Store getDefaultShop() {
- return shop;
+ /**
+ * Get the mock server (this should only be used in test)
+ *
+ * @return the mock server from tests
+ */
+ public static Server getMockServer() {
+ return server;
}
-
- public static Economy getEconomy() {
- return econ;
+ /**
+ * Get the default store, which will be replaced in the future once location-based stores are enabled
+ *
+ * @return the default store
+ */
+ public static Store getDefaultShop() {
+ return shop;
}
+ /**
+ * Sets the default store, which will be replaced in the future once location-based shops are enabled
+ *
+ * @param store the store to be set as default
+ */
public static void setDefaultShop(Store store) {
shop = store;
}
+
+ /**
+ * Configures the mock server for tests
+ *
+ * @param mockServer the mock server to be set
+ */
+ public static void setMockServer(Server mockServer) {
+ server = mockServer;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/sparkzz/shops/Store.java b/src/main/java/net/sparkzz/shops/Store.java
index 04bb760..0b3d5f2 100644
--- a/src/main/java/net/sparkzz/shops/Store.java
+++ b/src/main/java/net/sparkzz/shops/Store.java
@@ -10,23 +10,25 @@
import java.util.Map;
import java.util.UUID;
+/**
+ * The Store class is instantiable and serialized/deserialized around the data.shops file
+ */
@ConfigSerializable
public class Store {
+ /**
+ * This List contains all stores that have been created
+ */
public static final ArrayList STORES = new ArrayList<>();
- @Setting
- private UUID uuid;
-
- // item : attribute, value (block_dirt : quantity, 550)
- @Setting
- private Map> items;
-
@Setting private boolean infFunds = false;
@Setting private boolean infStock = false;
@Setting private double balance;
@Setting private String name;
+ // item : attribute, value (block_dirt : quantity, 550)
+ @Setting private Map> items;
@Setting private UUID owner;
+ @Setting private UUID uuid;
/**
* This constructor is required for the deserializer
@@ -36,6 +38,11 @@ public class Store {
@Deprecated
public Store() {}
+ /**
+ * Creates a store with the provided name
+ *
+ * @param name the name of the store to be created
+ */
public Store(String name) {
uuid = UUID.randomUUID();
items = new HashMap<>();
@@ -44,63 +51,144 @@ public Store(String name) {
STORES.add(this);
}
+ /**
+ * Creates a store with the provided name
+ *
+ * @param name the name of the store to be created
+ * @param owner the owner's UUID to be added to the store
+ */
public Store(String name, UUID owner) {
this(name);
this.owner = owner;
}
+ /**
+ * Check if the store contains the provided material
+ *
+ * @param material the material to be identified within the store
+ * @return whether the store contains the provided material
+ */
public boolean containsMaterial(Material material) {
return items.containsKey(material);
}
+ /**
+ * Checks if the store has the infinite funds flag set
+ *
+ * @return whether the store has infinite funds
+ */
public boolean hasInfiniteFunds() {
return infFunds;
}
+ /**
+ * Checks if the store has the infinite stock flag set
+ *
+ * @return whether the store has infinite stock
+ */
public boolean hasInfiniteStock() {
return infStock;
}
+ /**
+ * Checks the balance of the store
+ *
+ * @return the balance of the store
+ */
public double getBalance() {
return balance;
}
+ /**
+ * Checks the buy price of a material
+ *
+ * @param material the material to be queried for its buy price
+ * @return the buy price of the provided material
+ */
public double getBuyPrice(Material material) {
return (items.containsKey(material) ? items.get(material).get("buy").doubleValue() : -1D);
}
+ /**
+ * Checks the sell price of a material
+ *
+ * @param material the material to be queried for its sell price
+ * @return the sell price of the provided material
+ */
public double getSellPrice(Material material) {
return (items.containsKey(material) ? items.get(material).get("sell").doubleValue() : -1D);
}
+ /**
+ * Get the store's unique ID
+ *
+ * @return the store's UUID
+ */
public UUID getUUID() {
return uuid;
}
+ /**
+ * Get the items within the store with their attributes
+ *
+ * @return the items and attributes within the store
+ */
public Map> getItems() {
return items;
}
+ /**
+ * Get the attributes of a material in the store
+ *
+ * @param material the material to be queried for its attributes
+ * @return the attributes of the provided material
+ */
public Map getAttributes(Material material) {
return items.get(material);
}
+ /**
+ * Get the name of the store
+ *
+ * @return the store name
+ */
public String getName() {
return name;
}
+ /**
+ * Get the store owner's UUID
+ *
+ * @return the UUID of the store owner
+ */
public UUID getOwner() {
return owner;
}
+ /**
+ * Add funds to the store
+ *
+ * @param amount the amount of funds to be added to the store
+ */
public void addFunds(double amount) {
balance += amount;
}
+ /**
+ * Add an item stack to the store
+ *
+ * @param itemStack the item stack to be added to the store
+ */
public void addItem(ItemStack itemStack) {
addItem(itemStack.getType(), itemStack.getAmount());
}
+ /**
+ * Add a material and quantity to the store
+ *
+ * @param material the material to be added to the store
+ * @param quantity the quantity of the provided material to be added to the store
+ */
public void addItem(Material material, int quantity) {
if (items.containsKey(material)) {
int currentQuantity = items.get(material).get("quantity").intValue();
@@ -117,6 +205,15 @@ public void addItem(Material material, int quantity) {
}
}
+ /**
+ * Adds a new item to the store
+ *
+ * @param material the material to be added to the store
+ * @param quantity the quantity of the provided material to be added to the store
+ * @param maxQuantity the max quantity of the provided material
+ * @param buyValue the buy value of the provided material
+ * @param sellValue the sell value of the provided material
+ */
public void addItem(Material material, int quantity, int maxQuantity, double buyValue, double sellValue) {
addItem(material, quantity);
@@ -125,20 +222,41 @@ public void addItem(Material material, int quantity, int maxQuantity, double buy
items.get(material).put("sell", sellValue);
}
+ /**
+ * Removes funds from the store based on the input amount
+ *
+ * @param amount the amount of funds to be removed from the store
+ */
public void removeFunds(double amount) {
if (balance <= amount)
balance = 0;
else balance -= amount;
}
+ /**
+ * Removes an item stack from the store (not the entire item entry)
+ *
+ * @param itemStack the item stack to be removed from the store
+ */
public void removeItem(ItemStack itemStack) {
removeItem(itemStack.getType(), itemStack.getAmount());
}
+ /**
+ * Removes an item from the store's selection
+ *
+ * @param material the material to be removed from the store
+ */
public void removeItem(Material material) {
items.remove(material);
}
+ /**
+ * Removes the provided quantity of a provided material from the store
+ *
+ * @param material the material to be removed from the store
+ * @param quantity the quantity of the provided material to be removed from the store
+ */
public void removeItem(Material material, int quantity) {
Map attributes = items.get(material);
int newQuantity = attributes.get("quantity").intValue() - quantity;
@@ -147,23 +265,58 @@ public void removeItem(Material material, int quantity) {
items.put(material, attributes);
}
+ /**
+ * Sets the balance of the store
+ *
+ * @param balance the amount to set the stores balance to
+ */
public void setBalance(double balance) {
this.balance = balance;
}
+ /**
+ * Sets the infinite funds flag based on the input value
+ *
+ * @param value the value to be applied to the infinite funds flag
+ */
public void setInfiniteFunds(boolean value) {
infFunds = value;
}
+ /**
+ * Sets the infinite stock flag based on the input value
+ *
+ * @param value the value to be applied to the infinite stock flag
+ */
public void setInfiniteStock(boolean value) {
infStock = value;
}
+ /**
+ * Sets the name of the store to the input name
+ *
+ * @param name the name to be set for the store
+ */
public void setName(String name) {
this.name = name;
}
+ /**
+ * Sets the stores owner UUID to the input unique id
+ *
+ * @param uuid the unique id of the new store owner
+ */
public void setOwner(UUID uuid) {
this.owner = uuid;
}
+
+ /**
+ * Generates the string of the store's attributes
+ *
+ * @return the name of the store as the toString result
+ */
+ @Override
+ public String toString() {
+ return this.name;
+ }
}
diff --git a/src/main/java/net/sparkzz/util/InventoryManagementSystem.java b/src/main/java/net/sparkzz/util/InventoryManagementSystem.java
index bccef55..641db19 100644
--- a/src/main/java/net/sparkzz/util/InventoryManagementSystem.java
+++ b/src/main/java/net/sparkzz/util/InventoryManagementSystem.java
@@ -16,24 +16,54 @@
*/
public class InventoryManagementSystem {
+ /**
+ * Checks whether the provided material and quantity can be added to the player's inventory
+ *
+ * @param player the player to have their inventory checked
+ * @param material the material to be checked if it can be added to the player's inventory
+ * @param quantity the quantity of the material to be checked if it can be added to the store
+ * @return whether the provided quantity of material can be added to the player's inventory
+ */
public static boolean canInsert(Player player, Material material, int quantity) {
int availableSpace = getAvailableSpace(player, material);
return (quantity <= availableSpace);
}
+ /**
+ * Checks whether the provided material and quantity can be removed from the player's inventory
+ *
+ * @param player the player to have their inventory checked
+ * @param material the material to be checked if it can be removed from the player's inventory
+ * @param quantity the quantity of the material to be checked if it can be removed from the store
+ * @return whether the provided quantity of material can be removed from the player's inventory
+ */
public static boolean canRemove(Player player, Material material, int quantity) {
int inInventory = countQuantity(player, material);
return (quantity <= inInventory);
}
+ /**
+ * Checks if the store contains at least the quantity of materials in the item stack
+ *
+ * @param store the store to have its inventory queried
+ * @param itemStack the item stack to be used in the query
+ * @return whether the store contains at least the quantity of materials in the item stack
+ */
public static boolean containsAtLeast(Store store, ItemStack itemStack) {
int storeQuantity = countQuantity(store, itemStack.getType());
return store.hasInfiniteStock() || storeQuantity >= itemStack.getAmount();
}
+ /**
+ * Counts the quantity of the provided material in the player's inventory
+ *
+ * @param player the player to have their inventory queried
+ * @param material the material to be queried in the player's inventory
+ * @return the quantity of the provided material in the player's inventory
+ */
public static int countQuantity(Player player, Material material) {
ListIterator iterator = player.getInventory().iterator();
int quantity = 0;
@@ -48,6 +78,13 @@ public static int countQuantity(Player player, Material material) {
return quantity;
}
+ /**
+ * Counts the quantity of the provided material in the store
+ *
+ * @param store the store to have its inventory queried
+ * @param material the material to be queried in the store
+ * @return the quantity of the provided material in the player's inventory
+ */
public static int countQuantity(Store store, Material material) {
int quantity = -1;
@@ -60,6 +97,14 @@ public static int countQuantity(Store store, Material material) {
return quantity;
}
+ /**
+ * Gets the available space in the player's inventory based on the material's max stack size, it will even check
+ * partial stacks of the input material
+ *
+ * @param player the player to have their inventory queried
+ * @param material the material to be used to query the player's inventory
+ * @return the available space based on the material's stack size and inventory space
+ */
private static int getAvailableSpace(Player player, Material material) {
ListIterator iterator = player.getInventory().iterator();
int availableSpace = 0;
@@ -76,6 +121,13 @@ else if (stack.getType().equals(material))
return availableSpace;
}
+ /**
+ * Gets the available space in the store
+ *
+ * @param store the store to have its inventory queried
+ * @param material the material to be used to query the store
+ * @return the available space based on the max quantity and current quantity of the provided material
+ */
public static int getAvailableSpace(Store store, Material material) {
int availableSpace = 0;
@@ -91,7 +143,13 @@ public static int getAvailableSpace(Store store, Material material) {
return availableSpace;
}
- public static Store locateCurrentShop(Player player) {
+ /**
+ * Gets the current store based on the player's location
+ *
+ * @param player the player to have its location checked for the current store
+ * @return the store the player is currently located in
+ */
+ public static Store locateCurrentStore(Player player) {
// TODO: locate the player within the bounds of a current shop
return Shops.getDefaultShop();
}
diff --git a/src/main/java/net/sparkzz/util/Notifiable.java b/src/main/java/net/sparkzz/util/Notifiable.java
new file mode 100644
index 0000000..6b49787
--- /dev/null
+++ b/src/main/java/net/sparkzz/util/Notifiable.java
@@ -0,0 +1,60 @@
+package net.sparkzz.util;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Helper class for attributes on Notifiable classes
+ */
+public abstract class Notifiable {
+
+ private final Map attributes = new HashMap<>();
+
+ protected void resetAttributes() {
+ attributes.clear();
+ }
+
+ /**
+ * Maps the provided arguments based on the pattern: ("arg#", "args[#]")
+ *
+ * @param args the arguments to be mapped
+ */
+ protected void setArgsAsAttributes(String[] args) {
+ for (int i = 0; i < args.length; i++)
+ attributes.put("arg" + i, args[i]);
+ }
+
+ /**
+ * Get the attributes that can be used in translations
+ *
+ * @return the attributes set by classes extending this class
+ */
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Gets a specific attribute from the attributes map
+ *
+ * @param key the attribute key to be retrieved
+ * @return the value mapped to the provided key
+ */
+ public Optional