Compare commits

...

9 Commits

Author SHA1 Message Date
Len e554e2643d Fix shops resetting itemstack size to qty 1 2026-01-18 20:49:06 +01:00
Len 49fb85229d Refactor PlayerShops to use Dialogs. 2025-12-28 19:58:45 +01:00
Len ffdafd07d2 Update old item matching to use the new ItemMatcher class 2025-12-26 00:18:08 +01:00
Len bcba3413c5 Add MaterialSprites.java as a helper class to display ObjectComponents. 2025-12-26 00:15:03 +01:00
Len b4cca9227e Add ItemMatcher.java 2025-12-26 00:14:22 +01:00
Len f80408502b Update to 1.21.11 2025-12-24 21:37:53 +01:00
Len 4d531ea7b5 Refactor PlayerShopCommand to use Brigadier. 2025-11-20 20:47:58 +01:00
Len aaebd64294 Refactor to PaperPlugin 2025-11-20 19:33:54 +01:00
Len 33f51eed09 Update cosmos repo and cosmos version. 2025-08-02 11:05:24 +02:00
40 changed files with 3264 additions and 1535 deletions

View File

@ -2,11 +2,12 @@ import java.io.ByteArrayOutputStream
import java.io.FileOutputStream
import java.net.URL
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
import net.minecrell.pluginyml.paper.PaperPluginDescription
plugins {
id("java")
id("java-library")
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("de.eldoria.plugin-yml.paper") version "0.8.0"
id("xyz.jpenilla.run-paper") version "1.0.6"
id("maven-publish")
}
@ -40,7 +41,7 @@ tasks {
file.parentFile.mkdirs()
}
if (!file.exists()) {
download("https://jenkins.destro.xyz/job/Cosmos/lastSuccessfulBuild/artifact/cosmos-server/build/libs/cosmos-bundler-1.21.6-R0.1-SNAPSHOT-mojmap.jar", fileName)
download("https://jenkins.destro.xyz/job/Cosmos/lastSuccessfulBuild/artifact/cosmos-server/build/libs/cosmos-bundler-1.21.11-R0.1-SNAPSHOT-mojmap.jar", fileName)
}
serverJar(file)
minecraftVersion("1.21")
@ -61,8 +62,10 @@ publishing {
configure<PublishingExtension> {
repositories {
maven {
name = "maven"
url = uri("https://repo.destro.xyz/snapshots/")
name = "AlttdNexus"
url = uri(
"https://repo.alttd.com/repository/alttd-snapshot/"
)
credentials(PasswordCredentials::class)
}
}
@ -70,7 +73,7 @@ publishing {
}
dependencies {
compileOnly("com.alttd.cosmos:cosmos-api:1.21.6-R0.1-SNAPSHOT")
compileOnly("com.alttd.cosmos:cosmos-api:1.21.11-R0.1-SNAPSHOT")
compileOnly("com.github.milkbowl:VaultAPI:1.7") {
exclude("org.bukkit","bukkit")
}
@ -97,14 +100,23 @@ fun download(link: String, path: String) {
}
}
bukkit {
paper {
name = rootProject.name
main = "$group.${rootProject.name}"
bootstrapper = "$group.${rootProject.name}Bootstrap"
version = "${rootProject.version}-${gitCommit()}"
apiVersion = "1.20"
apiVersion = "1.21"
authors = listOf("destro174")
depend = listOf("Vault")
softDepend = listOf("GriefPrevention")
serverDependencies {
register("Vault") {
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
register("GriefPrevention") {
required = false
}
}
permissions {
register("playershops.admin") {

View File

@ -3,7 +3,14 @@ rootProject.name = "PlayerShops"
dependencyResolutionManagement {
repositories {
mavenCentral()
maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy
maven {
name = "AlttdNexus"
url = uri(
"https://repo.alttd.com/repository/alttd-snapshot/"
)
credentials(PasswordCredentials::class)
}
maven("https://repo.destro.xyz/snapshots")
maven("https://jitpack.io") { // Vault
content {
includeGroup("com.github.milkbowl")

View File

@ -1,21 +1,26 @@
package com.alttd.playershops;
import com.alttd.playershops.commands.PlayerShopCommands;
import com.alttd.playershops.commands.PlayerShopCommand;
import com.alttd.playershops.config.Config;
import com.alttd.playershops.config.DatabaseConfig;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.gui.GuiIcon;
import com.alttd.playershops.handler.ShopHandler;
import com.alttd.playershops.listener.*;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.storage.database.DatabaseManager;
import com.alttd.playershops.storage.database.DatabaseHelper;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import lombok.Getter;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
public class PlayerShops extends JavaPlugin {
@Getter
@ -31,7 +36,6 @@ public class PlayerShops extends JavaPlugin {
private ShopListener shopListener;
private PlayerListener playerListener;
private TransactionListener transactionListener;
private InventoryListener inventoryListener;
private GriefPreventionListener griefPreventionListener;
@Getter
@ -40,14 +44,14 @@ public class PlayerShops extends JavaPlugin {
public void onEnable() {
instance = this;
if(!setupEconomy()) {
Bukkit.getLogger().warning("Error loading Vault and economy.\n Disabling plugin");
getLogger().warning("Error loading Vault and economy.\n Disabling plugin");
this.setEnabled(false);
return;
}
Bukkit.getLogger().info("Hooked into Vault economy provided by " + econ.getName());
getLogger().info("Hooked into Vault economy provided by " + econ.getName());
reloadConfigs();
if(!setupDatabase()) {
Bukkit.getLogger().warning("Error setting up database connection.\n Disabling plugin");
getLogger().warning("Error setting up database connection.\n Disabling plugin");
this.setEnabled(false);
return;
}
@ -95,7 +99,6 @@ public class PlayerShops extends JavaPlugin {
shopListener = new ShopListener(this);
playerListener = new PlayerListener(this);
transactionListener = new TransactionListener(this);
inventoryListener = new InventoryListener(this);
griefPreventionListener = new GriefPreventionListener(this); // TODO hook into GP
}
@ -103,11 +106,14 @@ public class PlayerShops extends JavaPlugin {
shopListener.unregister();
playerListener.unregister();
transactionListener.unregister();
inventoryListener.unregister();
}
private void registerCommands() {
PlayerShopCommands.registerCommands();
LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
final Commands commands = event.registrar();
commands.register(new PlayerShopCommand().command(), null, List.of("shop", "shops"));
});
}
public void reloadConfigs() {
@ -119,9 +125,6 @@ public class PlayerShops extends JavaPlugin {
for (ShopType shopType : ShopType.values()) {
// preload ShopType to get the configs active
}
for (GuiIcon guiIcon : GuiIcon.values()) {
// preload to get config values generated
}
}
}

View File

@ -0,0 +1,12 @@
package com.alttd.playershops;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
@SuppressWarnings("UnstableApiUsage")
public class PlayerShopsBootstrap implements PluginBootstrap {
@Override
public void bootstrap(BootstrapContext context) {}
}

View File

@ -1,147 +1,95 @@
package com.alttd.playershops.commands;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.commands.arguments.OfflinePlayerArgument;
import com.alttd.playershops.commands.subcommands.CheckStockCommand;
import com.alttd.playershops.commands.subcommands.ReloadCommand;
import com.alttd.playershops.commands.subcommands.TransferShopsCommand;
import com.alttd.playershops.gui.HomeGui;
import com.alttd.playershops.utils.Util;
import it.unimi.dsi.fastutil.Pair;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import com.alttd.playershops.dialog.ShopDialog;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.stream.Collectors;
public class PlayerShopCommand {
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
public static final String BASE_PERM = "playershops.command";
public class PlayerShopCommand extends Command {
public @NotNull LiteralCommandNode<CommandSourceStack> command() {
final LiteralArgumentBuilder<CommandSourceStack> builder =
Commands.literal("playershop")
.requires(
commandSourceStack -> commandSourceStack.getSender().hasPermission(BASE_PERM)
&& commandSourceStack.getSender() instanceof Player
)
.executes((source) -> {
if (!(source.getSource().getSender() instanceof Player player))
return 1;
public static final String BASE_PERM = "playershops.command"; // TODO load from config
ShopDialog shopDialog = new ShopDialog(player.getUniqueId());
shopDialog.open();
return 1;
})
.then(
Commands.literal("reload")
.requires(commandSourceStack -> commandSourceStack.getSender().hasPermission(BASE_PERM + ".reload"))
.executes(source -> {
PlayerShops.getInstance().reloadConfigs();
return 1;
})
)
.then(
Commands.literal("checkstock")
.requires(commandSourceStack -> commandSourceStack.getSender().hasPermission(BASE_PERM + ".checkstock")
&& commandSourceStack.getSender() instanceof Player)
.then(
Commands.argument("range", IntegerArgumentType.integer(1, 100))
.executes(source -> {
if (!(source.getSource().getSender() instanceof Player player))
return 1;
// subcommand label -> subcommand
private static final Map<String, Subcommand> SUBCOMMANDS = PlayerShopCommands.make(() -> {
final Map<Set<String>, Subcommand> commands = new HashMap<>();
int range = source.getArgument("range", Integer.class);
int stock = -1;
CheckStockCommand.doStockCheck(player, range, stock);
return 1;
})
.then(
Commands.argument("stock", IntegerArgumentType.integer())
.executes(source -> {
if (!(source.getSource().getSender() instanceof Player player))
return 1;
commands.put(Set.of("reload"), new ReloadCommand());
commands.put(Set.of("checkstock"), new CheckStockCommand());
commands.put(Set.of("transfershops"), new TransferShopsCommand());
int range = source.getArgument("range", Integer.class);
int stock = source.getArgument("stock", Integer.class);
CheckStockCommand.doStockCheck(player, range, stock);
return 1;
})
)
)
)
.then(
Commands.literal("transfershops")
.requires(commandSourceStack -> commandSourceStack.getSender().hasPermission(BASE_PERM + ".transfershops"))
.then(
Commands.argument("old", new OfflinePlayerArgument())
.then(
Commands.argument("new", new OfflinePlayerArgument())
)
.executes(
source -> {
OfflinePlayer oldOwner = source.getArgument("old", OfflinePlayer.class);
OfflinePlayer newOwner = source.getArgument("new", OfflinePlayer.class);
PlayerShops.getInstance().getShopHandler().transferShops(source.getSource().getSender(), oldOwner, newOwner);
return commands.entrySet().stream()
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
});
// alias -> subcommand label
private static final Map<String, String> ALIASES = PlayerShopCommands.make(() -> {
final Map<String, Set<String>> aliases = new HashMap<>();
aliases.put("reload", Set.of("reloadconfig"));
return aliases.entrySet().stream()
.flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
});
public PlayerShopCommand() {
super("playershop");
this.description = "PlayerShop related commands";
this.usageMessage = "/playershop [" + String.join(" | ", SUBCOMMANDS.keySet()) + "]";
this.setAliases(List.of("shop", "shops"));
final List<String> permissions = new ArrayList<>();
permissions.add(BASE_PERM);
permissions.addAll(SUBCOMMANDS.keySet().stream().map(s -> BASE_PERM + "." + s).toList());
this.setPermission(String.join(";", permissions));
final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
for (final String perm : permissions) {
pluginManager.addPermission(new Permission(perm, PermissionDefault.OP));
return 1;
}
)
)
)
;
return builder.build();
}
private static boolean testPermission(final CommandSender sender, final String permission) {
if (sender.hasPermission(BASE_PERM + "." + permission)) {
return true;
}
sender.sendMessage(Bukkit.permissionMessage());
return false;
}
@Override
public @NotNull List<String> tabComplete(
final @NotNull CommandSender sender,
final @NotNull String alias,
final String[] args,
final @Nullable Location location
) throws IllegalArgumentException {
if (args.length <= 1) {
return PlayerShopCommands.getListMatchingLast(sender, args, SUBCOMMANDS.keySet(), BASE_PERM);
}
final @Nullable Pair<String, Subcommand> subCommand = resolveCommand(args[0]);
if (subCommand != null) {
return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length));
}
return Collections.emptyList();
}
@Override
public boolean execute(
final @NotNull CommandSender sender,
final @NotNull String commandLabel,
final String[] args
) {
if (!testPermission(sender)) {
return true;
}
if (args.length == 0) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Util.parseMiniMessage("<red>Only players can use this command."));
return false;
}
HomeGui gui = new HomeGui(player.getUniqueId());
gui.open();
return true;
}
final @Nullable Pair<String, Subcommand> subCommand = resolveCommand(args[0]);
if (subCommand == null) {
sender.sendMessage(text("Usage: " + this.usageMessage, RED));
return false;
}
if (!testPermission(sender, subCommand.first())) {
return true;
}
final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length);
return subCommand.second().execute(sender, subCommand.first(), choppedArgs);
}
private static @Nullable Pair<String, Subcommand> resolveCommand(String label) {
label = label.toLowerCase(Locale.ENGLISH);
@Nullable Subcommand subCommand = SUBCOMMANDS.get(label);
if (subCommand == null) {
final @Nullable String command = ALIASES.get(label);
if (command != null) {
label = command;
subCommand = SUBCOMMANDS.get(command);
}
}
if (subCommand != null) {
return Pair.of(label, subCommand);
}
return null;
}
}

View File

@ -1,58 +0,0 @@
package com.alttd.playershops.commands;
import com.google.common.base.Functions;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.*;
import java.util.function.Supplier;
public class PlayerShopCommands {
private PlayerShopCommands() {
}
private static final Map<String, Command> COMMANDS = new HashMap<>();
static {
COMMANDS.put("playershop", new PlayerShopCommand());
}
public static void registerCommands() {
COMMANDS.forEach((s, command) -> {
Bukkit.getCommandMap().register(s, command.getName(), command);
});
}
// Code from Mojang - copyright them | Altered to fit our needs
public static List<String> getListMatchingLast(final CommandSender sender, final String[] args, final String basePermission, final String... matches) {
return getListMatchingLast(sender, args, Arrays.asList(matches), basePermission);
}
public static boolean matches(final String s, final String s1) {
return s1.regionMatches(true, 0, s, 0, s.length());
}
public static List<String> getListMatchingLast(final CommandSender sender, final String[] strings, final Collection<?> collection, final String basePermission) {
String last = strings[strings.length - 1];
ArrayList<String> results = Lists.newArrayList();
if (!collection.isEmpty()) {
for (String s1 : collection.stream().map(Functions.toStringFunction()).toList()) {
if (matches(last, s1) && (sender.hasPermission(basePermission + "." + s1))) {
results.add(s1);
}
}
}
return results;
}
public static <T> T make(Supplier<T> factory) {
return factory.get();
}
// end copy stuff
}

View File

@ -1,19 +0,0 @@
package com.alttd.playershops.commands;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import java.util.Collections;
import java.util.List;
@DefaultQualifier(NonNull.class)
public interface Subcommand {
boolean execute(CommandSender sender, String subCommand, String[] args);
default List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,49 @@
package com.alttd.playershops.commands.arguments;
import com.mojang.brigadier.Message;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.papermc.paper.command.brigadier.MessageComponentSerializer;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
public class OfflinePlayerArgument implements CustomArgumentType.Converted<OfflinePlayer, String> {
@Override
public @NotNull OfflinePlayer convert(String nativeType) throws CommandSyntaxException {
try {
return Bukkit.getOfflinePlayer(nativeType);
} catch (Exception e) {
Message message = MessageComponentSerializer.message().serialize(Component.text("Invalid PlayerName %s!".formatted(nativeType), NamedTextColor.RED));
throw new CommandSyntaxException(new SimpleCommandExceptionType(message), message);
}
}
@Override
public @NotNull ArgumentType<String> getNativeType() {
return StringArgumentType.word();
}
@Override
public <S> @NotNull CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
.filter(name -> name.toLowerCase(Locale.ROOT).startsWith(builder.getRemainingLowerCase()))
.forEach(builder::suggest);
return builder.buildFuture();
}
}

View File

@ -2,8 +2,7 @@ package com.alttd.playershops.commands.subcommands;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.commands.PlayerShopCommand;
import com.alttd.playershops.commands.PlayerShopCommands;
import com.alttd.playershops.commands.Subcommand;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
@ -13,78 +12,26 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@DefaultQualifier(NonNull.class)
public class CheckStockCommand implements Subcommand {
public class CheckStockCommand {
@Override
public boolean execute(CommandSender sender, String subCommand, String[] args) {
return this.doStockCheck(sender, args);
}
@Override
public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
// TODO give some default tab completions
if (args.length == 1) {
return PlayerShopCommands.getListMatchingLast(sender, args, PlayerShopCommand.BASE_PERM + ".checkstock", "help");
} else if (args.length == 2) {
// return PlayerShopCommands.getListMatchingLast(sender, args, "radius");
return Collections.emptyList();
}
return Collections.emptyList();
}
private int minimumStock = -1;
public boolean doStockCheck(final CommandSender sender, final String[] args) {
if (!(sender instanceof Player player)) {
sender.sendRichMessage("<red>Only players can use this command.");
return false;
}
if (args.length != 1 && args.length != 2) {
player.sendRichMessage("<red>Invalid command syntax, use /checkstock <radius> [minimum stock]");
return false;
}
int radius;
try {
radius = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
player.sendRichMessage("<red>radius has to be a valid number, use /checkstock <radius> [minimum stock]");
return false;
}
if (radius > 100 || radius <= 0) {
player.sendRichMessage("<red>Please keep the radius between 1 and 100");
return false;
}
if (args.length == 2) {
try {
minimumStock = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
player.sendRichMessage("<red>minimum stock has to be a valid number, use /checkstock <radius> [minimum stock]");
return false;
}
}
List<Stock> stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius, player);
sendStockMessage(player, stockList);
public static void doStockCheck(final Player player, int radius, int minimumStock) {
List<Stock> stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius, player, minimumStock);
sendStockMessage(player, stockList, minimumStock);
highlightLowStock(player, stockList);
return true;
}
//Stole this code from AlttdUtility by ___Kappa___ and reworked it slightly
private void highlightLowStock(Player player, List<Stock> stockList) {
private static void highlightLowStock(Player player, List<Stock> stockList) {
ParticleBuilder particleBuilder = new ParticleBuilder(Particle.DUST);
particleBuilder.color(255, 255, 255);
particleBuilder.receivers(player);
@ -139,7 +86,7 @@ public class CheckStockCommand implements Subcommand {
* @param caller Player who is checking for stock, can only see the stock from their shops unless they have base_perm.checkstock.bypass
* @return A list of Stock that the player is allowed to see
*/
private List<Stock> checkStock(int x, int z, int radius, Player caller) {
private static List<Stock> checkStock(int x, int z, int radius, Player caller, int minimumStock) {
List<PlayerShop> shops = PlayerShops.getInstance().getShopHandler().getShopsInRadius(x, z, radius);
Stream<PlayerShop> playerShopStream = shops.stream();
if (minimumStock != -1)
@ -157,8 +104,7 @@ public class CheckStockCommand implements Subcommand {
}).collect(Collectors.toList());
}
private static final String putInConfig = "<yellow><click:run_command:'/cmi tppos <x> <y> <z>'>[<aqua><item_component></aqua>] <amount> left in stock</click></yellow>";
private void sendStockMessage(Player player, List<Stock> stockList) {
private static void sendStockMessage(Player player, List<Stock> stockList, int minimumStock) {
Component component = null;
for (Stock stock : stockList) {
TagResolver resolver = TagResolver.resolver(
@ -169,9 +115,9 @@ public class CheckStockCommand implements Subcommand {
Placeholder.parsed("amount", stock.stock + "")
);
if (component == null)
component = Util.parseMiniMessage(putInConfig, resolver);
component = Util.parseMiniMessage(MessageConfig.SHOP_STOCK_CHECK, resolver);
else
component = component.append(Component.newline()).append(Util.parseMiniMessage(putInConfig, resolver));
component = component.append(Component.newline()).append(Util.parseMiniMessage(MessageConfig.SHOP_STOCK_CHECK, resolver));
}
if (component == null)
if (minimumStock == -1)

View File

@ -1,19 +0,0 @@
package com.alttd.playershops.commands.subcommands;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.commands.Subcommand;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public class ReloadCommand implements Subcommand {
@Override
public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
PlayerShops.getInstance().reloadConfigs();
// Todo message when config is reloaded
return true;
}
}

View File

@ -1,50 +0,0 @@
package com.alttd.playershops.commands.subcommands;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.commands.Subcommand;
import com.alttd.playershops.shop.PlayerShop;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import java.util.List;
import java.util.UUID;
@DefaultQualifier(NonNull.class)
public class TransferShopsCommand implements Subcommand {
@Override
public boolean execute(CommandSender sender, String subCommand, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendRichMessage("<red>Only players can use this command.");
return false;
}
if (args.length != 1 && args.length != 2) {
player.sendRichMessage("<red>Invalid command syntax, use /transfershops <uuid> <newplayer>");
return false;
}
OfflinePlayer oldOfflinePlayer = Bukkit.getOfflinePlayer(args[0]);
if (!oldOfflinePlayer.hasPlayedBefore()) {
player.sendRichMessage("<red>" + args[0] + " has not joined this server before.");
return false;
}
Player newShopOwner = Bukkit.getPlayer(args[1]);
if (newShopOwner == null) {
player.sendRichMessage("<red>" + args[1] + " is not online and has to be online for this process.");
return false;
}
UUID oldUUID = oldOfflinePlayer.getUniqueId();
List<PlayerShop> playerShops = PlayerShops.getInstance().getShopHandler().getShops(oldUUID);
sender.sendRichMessage("<red>Starting the transfer process now, this might lag the server.");
for (PlayerShop playerShop : playerShops) {
playerShop.setOwner(newShopOwner);
}
newShopOwner.sendRichMessage(playerShops.size() + " have been transferred to you.");
sender.sendRichMessage(playerShops.size() + " have been transferred to " + args[1] + ".");
return true;
}
}

View File

@ -143,9 +143,11 @@ public class MessageConfig {
public static String SHOP_INFO = "<yellow>This shop <action> <gold><amount></gold> <item> for <gold><price></gold>.</yellow>";
public static String SHOP_STOCK_INFO = "<yellow>This <type> shop has <gold><stock></gold> stock.</yellow>";
public static String SHOP_STOCK_CHECK = "<yellow><click:run_command:'/cmi tppos <x> <y> <z>'>[<aqua><item_component></aqua>] <amount> left in stock</click></yellow>";
private static void otherMessages() {
SHOP_INFO = getString("messages.shop-info", SHOP_INFO);
SHOP_STOCK_INFO = getString("messages.shop-stock-info", SHOP_STOCK_INFO);
SHOP_STOCK_CHECK = getString("messages.shop-stock-check", SHOP_STOCK_CHECK);
}
public static String CHANGE_PRICE_PROMPT = "What should the price be? Type cancel to cancel this action.";

View File

@ -1,216 +0,0 @@
package com.alttd.playershops.conversation;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.gui.ShopManagementGui;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.utils.EconomyUtils;
import com.alttd.playershops.utils.Util;
import com.google.common.base.Joiner;
import net.kyori.adventure.text.Component;
import org.bukkit.conversations.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public class ConversationManager implements ConversationAbandonedListener {
PlayerShop playerShop;
Player player;
Conversation conversation;
public ConversationManager(JavaPlugin plugin, Player player, ConversationType conversationType, PlayerShop playerShop) {
if (player.isConversing()) return;
this.player = player;
this.playerShop = playerShop;
ConversationFactory conversationFactory = new ConversationFactory(plugin)
.withModality(true)
.withFirstPrompt(getPrompt(conversationType))
.addConversationAbandonedListener(this)
.withEscapeSequence("cancel");
conversation = conversationFactory.buildConversation(player);
conversation.setLocalEchoEnabled(false); // why is this not in the builder
conversation.begin();
}
Prompt getPrompt(ConversationType conversationType) {
switch (conversationType) {
case CHANGE_ITEM -> {
return new ChangeItemPrompt();
}
case CHANGE_AMOUNT -> {
return new ChangeAmountPrompt();
}
case CHANGE_TYPE -> {
return new ChangeTypePrompt();
}
case WITHDRAW_BALANCE -> {
return new WithdrawBalancePrompt();
}
case ADD_BALANCE -> {
return new AddBalancePrompt();
}
case CHANGE_PRICE -> {
return new ChangePricePrompt();
}
};
return null;
}
private void openGui() {
player.abandonConversation(conversation);
ShopManagementGui shopManagementGui = new ShopManagementGui(player.getUniqueId(), playerShop);
shopManagementGui.open();
}
@Override
public void conversationAbandoned(@NotNull ConversationAbandonedEvent abandonedEvent) {
// abandonedEvent.getContext().getForWhom().sendRawMessage("Conversation ended.");
}
private class ChangeTypePrompt extends FixedSetPrompt {
public ChangeTypePrompt() {
super("buy", "sell", "none", "random");
}
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage("What shoptype would you like to use: " + Joiner.on(", ").join(fixedSet) + " Type cancel to cancel this action.");
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, String input) {
ShopType newType = ShopType.fromString(input);
playerShop.setShopType(newType);
openGui();
return END_OF_CONVERSATION;
}
}
private class ChangePricePrompt extends NumericPrompt {
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage(MessageConfig.CHANGE_PRICE_PROMPT);
}
@Override
protected boolean isNumberValid(ConversationContext context, Number input) {
return input.doubleValue() >= 0;
}
@Override
protected String getFailedValidationText(ConversationContext context, Number invalidInput) {
return MessageConfig.CHANGE_PRICE_FAILED_PROMPT;
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number number) {
playerShop.setPrice(number.doubleValue());
openGui();
return END_OF_CONVERSATION;
}
}
private class ChangeAmountPrompt extends NumericPrompt {
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage(MessageConfig.CHANGE_AMOUNT_PROMPT);
}
@Override
protected boolean isNumberValid(ConversationContext context, Number input) {
return input.intValue() > 0 && input.intValue() <= 64;
}
@Override
protected String getFailedValidationText(ConversationContext context, Number invalidInput) {
return MessageConfig.CHANGE_AMOUNT_FAILED_PROMPT;
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number number) {
playerShop.setAmount(number.intValue());
openGui();
return END_OF_CONVERSATION;
}
}
private class AddBalancePrompt extends NumericPrompt {
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage(MessageConfig.ADD_BALANCE_PROMPT);
}
@Override
protected boolean isNumberValid(ConversationContext context, Number input) {
if (input.doubleValue() < 0)
return false;
return EconomyUtils.hasSufficientFunds(player, input.doubleValue());
}
@Override
protected String getFailedValidationText(ConversationContext context, Number invalidInput) {
return MessageConfig.ADD_BALANCE_FAILED_PROMPT;
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number number) {
playerShop.addBalance(number.doubleValue());
EconomyUtils.removeFunds(player, number.doubleValue());
openGui();
return END_OF_CONVERSATION;
}
}
private class WithdrawBalancePrompt extends NumericPrompt {
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage(MessageConfig.WITHDRAW_BALANCE_PROMPT);
}
@Override
protected boolean isNumberValid(ConversationContext context, Number input) {
if (input.doubleValue() < 0)
return false;
return EconomyUtils.hasSufficientFunds(playerShop, input.doubleValue());
}
@Override
protected String getFailedValidationText(ConversationContext context, Number invalidInput) {
return MessageConfig.WITHDRAW_BALANCE_FAILED_PROMPT;
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number number) {
playerShop.removeBalance(number.doubleValue());
EconomyUtils.addFunds(player, number.doubleValue());
openGui();
return END_OF_CONVERSATION;
}
}
private class ChangeItemPrompt extends FixedSetPrompt {
ChangeItemPrompt() {
super("continue");
}
public @NotNull Component getPromptMessage(ConversationContext context) {
return Util.parseMiniMessage(MessageConfig.CHANGE_ITEM_PROMPT);
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, String input) {
playerShop.setItemStack(player.getInventory().getItemInMainHand().clone());
openGui();
return END_OF_CONVERSATION;
}
}
}

View File

@ -1,10 +0,0 @@
package com.alttd.playershops.conversation;
public enum ConversationType {
CHANGE_ITEM,
CHANGE_AMOUNT,
CHANGE_TYPE,
CHANGE_PRICE,
WITHDRAW_BALANCE,
ADD_BALANCE;
}

View File

@ -0,0 +1,83 @@
package com.alttd.playershops.dialog;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public abstract class AbstractDialog {
Dialog dialog;
protected final int INV_SIZE = 54;
UUID uuid;
AbstractDialog lastDialog;
List<ActionButton> actions = new ArrayList<>();
List<DialogInput> inputs = new ArrayList<>();
List<DialogBody> texts = new ArrayList<>();
ActionButton closeButton;
AbstractDialog(UUID uuid) {
this.uuid = uuid;
this.lastDialog = null;
closeButton = ActionButton.builder(MiniMessage.miniMessage().deserialize("<gold>Go back</gold>")).action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
if (!hasLastGui()) {
return;
}
lastDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
).build();
}
abstract Dialog initDialog();
public void open() {
if (dialog == null)
return;
Player player = getPlayer();
if (player != null) {
// player.closeDialog();
player.showDialog(dialog);
}
}
void close() {
Player player = getPlayer();
if (player != null) {
player.closeInventory();
}
}
void initContents() {
}
Player getPlayer() {
return Bukkit.getPlayer(uuid);
}
public void setLastGui(AbstractDialog lastDialog) {
this.lastDialog = lastDialog;
}
public boolean hasLastGui() {
return this.lastDialog != null;
}
}

View File

@ -0,0 +1,90 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.object.ObjectContents;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
public class ListPlayersDialog extends AbstractDialog {
private final List<UUID> owners;
public ListPlayersDialog(UUID uuid) {
super(uuid);
this.owners = PlayerShops.getInstance().getShopHandler().getShopOwnersForThisServer();
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
3)
)
.base(DialogBase.builder(MiniMessage.miniMessage().deserialize(MessageConfig.GUI_LIST_PLAYERS_TITLE))
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
for (UUID shopOwnerUUID : owners) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(shopOwnerUUID);
String playerName;
if (!offlinePlayer.hasPlayedBefore()) {
playerName = "Unknown";
} else {
playerName = offlinePlayer.getName();
}
actions.add(
ActionButton.builder(
Component.object(ObjectContents.playerHead(shopOwnerUUID)).append(Component.space().append(Component.text(playerName))))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
ListShopsDialog listShopsDialog = new ListShopsDialog(uuid, shopOwnerUUID);
listShopsDialog.setLastGui(this);
listShopsDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);
}
}
}

View File

@ -0,0 +1,161 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.utils.EconomyUtils;
import com.alttd.playershops.utils.ShopUtil;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
public class ListShopsDialog extends AbstractDialog {
List<PlayerShop> shops;
UUID playerToList;
Component title;
TagResolver placeholders;
public ListShopsDialog(UUID uuid, UUID playerToList) {
super(uuid);
this.playerToList = playerToList;
this.shops = PlayerShops.getInstance().getShopHandler().getShops(this.playerToList);
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(this.playerToList);
placeholders = TagResolver.resolver(
Placeholder.unparsed("playername", offlinePlayer.hasPlayedBefore() ? offlinePlayer.getName() : "Unknown")
);
title = MiniMessage.miniMessage().deserialize(MessageConfig.GUI_LIST_SHOPS_TITLE, placeholders);
dialog = initDialog();
}
public ListShopsDialog(UUID uuid, List<PlayerShop> playerShops) {
super(uuid);
this.shops = playerShops;
placeholders = TagResolver.resolver(
Placeholder.unparsed("material", "Unknown")
);
title = MiniMessage.miniMessage().deserialize("Shops per item");
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
3)
)
.base(DialogBase.builder(title)
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
texts.add(DialogBody.plainMessage(MiniMessage.miniMessage().deserialize("<gold>This menu will <red>not</red> display shops that are not initialized")));
for (PlayerShop shop : shops) {
if (!shop.isInitialized()) {
continue;
}
actions.add(
ActionButton.builder(
ShopUtil.spriteComponent(shop.getItemStack()))
.tooltip(shop.getHoverComponent(getPlayer()))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
if (!ShopUtil.canManageShop(player, shop)) {
return;
}
ManageShopDialog manageShopDialog = new ManageShopDialog(player.getUniqueId(), shop);
manageShopDialog.setLastGui(this);
manageShopDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);
}
if (uuid != null && uuid.equals(playerToList) && getPlayer().hasPermission("playershops.shop.collectall")) {
actions.add(
ActionButton.builder(
MiniMessage.miniMessage().deserialize("<gold>Collect sales</gold>"))
.tooltip(MiniMessage.miniMessage().deserialize("<gold>Click to collect all balance from your shops.</gold>"))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
double d = 0;
int count = 0;
for (PlayerShop playerShop : shops) {
if (!ShopUtil.canManageShop(player, playerShop))
continue;
if (playerShop.getType().equals(ShopType.BUY))
continue;
double balance = playerShop.getBalance();
if (balance == 0)
continue;
playerShop.removeBalance(balance);
EconomyUtils.addFunds(player, balance);
d += balance;
count++;
}
TagResolver placeholders = TagResolver.resolver(
Placeholder.parsed("balance", String.valueOf(d)),
Placeholder.parsed("count", String.valueOf(count))
);
player.sendRichMessage(count > 0 ? "<green>You have collected <balance> from <count> shops." : "<red>No shops to be collected from.", placeholders);
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.build())
)
.build()
);
}
}
}

View File

@ -0,0 +1,214 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.utils.EconomyUtils;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class ManageShopDialog extends AbstractDialog {
PlayerShop shop;
public ManageShopDialog(UUID uuid, PlayerShop shop) {
super(uuid);
this.shop = shop;
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
3)
)
.base(DialogBase.builder(MiniMessage.miniMessage().deserialize("<gold>PlayerShop - <playername>", Placeholder.component("playername", getPlayer().displayName())))
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
inputs.add(
DialogInput.singleOption("shoptype", MiniMessage.miniMessage().deserialize("<gold>ShopType</gold>"),
Arrays.stream(ShopType.values()).map(
shopType -> SingleOptionDialogInput.OptionEntry.create(shopType.name(), MiniMessage.miniMessage().deserialize("<green>" + shopType.name()), shop.getType() == shopType)
).toList())
.build()
);
inputs.add(
DialogInput.text("balance", MiniMessage.miniMessage().deserialize("<gold>Balance</gold>"))
.initial(String.valueOf(shop.getBalance()))
.width(300)
.build()
);
inputs.add(
DialogInput.bool("changeitem", MiniMessage.miniMessage().deserialize("<gold>Change item?</gold>"))
.initial(false)
.build()
);
inputs.add(
DialogInput.singleOption("item", MiniMessage.miniMessage().deserialize("<gold>Item</gold>"), itemEntries()).build()
);
int maxQty = (shop.isInitialized() && shop.getItemStack() != null ? shop.getItemStack().getMaxStackSize() : 64);
int initialQty = (shop.isInitialized() ? Math.min(shop.getAmount(), maxQty) : 1);
inputs.add(
DialogInput.numberRange("amount", MiniMessage.miniMessage().deserialize("<gold>Change amount</gold>"), 1f, (float) maxQty)
.step(1f)
.initial((float) initialQty)
.width(300)
.build()
);
inputs.add(
DialogInput.text("price", MiniMessage.miniMessage().deserialize("<gold>Change price</gold>"))
.initial(String.valueOf(shop.getPrice()))
.width(300)
.build()
);
ActionButton saveButton = ActionButton.builder(MiniMessage.miniMessage().deserialize("<gold>Confirm</gold>")).action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
if (!ShopUtil.canManageShop(player, shop)) {
return;
}
// Change type
String shopTypeString = view.getText("shoptype");
ShopType shopType = ShopType.fromString(shopTypeString);
if (shop.getType() != shopType) {
shop.setShopType(shopType);
}
// Change Balance
String balanceString = view.getText("balance");
if (balanceString != null && !balanceString.isBlank()) {
Double balance;
try {
balance = EconomyUtils.parseValue(balanceString);
if (balance != null && balance >= 0) {
if (balance != shop.getBalance()) {
double difference = balance - shop.getBalance();
if (difference >= 0) {
if (EconomyUtils.hasSufficientFunds(player, difference)) {
shop.addBalance(difference);
EconomyUtils.removeFunds(player, difference);
}
} else if (difference <= 0) {
double diff = -1 * difference;
if (EconomyUtils.hasSufficientFunds(shop, diff)) {
shop.removeBalance(diff);
EconomyUtils.addFunds(player, diff);
}
}
}
}
} catch (NumberFormatException ignored) {}
}
// Change Item
if (Boolean.TRUE.equals(view.getBoolean("changeitem"))) {
String selection = view.getText("item");
if (selection != null) {
if (selection.equalsIgnoreCase("reset")) {
shop.setItemStack(null);
} else {
int slot;
try {
slot = Integer.parseInt(selection);
shop.setItemStack(player.getInventory().getItem(slot));
} catch (NumberFormatException ignored) {
}
}
}
}
// Change Amount
int amount = view.getFloat("amount").intValue();
if (amount != shop.getAmount()) {
shop.setAmount(amount);
}
// Change Price
String priceString = view.getText("price");
if (priceString != null && !priceString.isBlank()) {
Double price = EconomyUtils.parseValue(priceString);
if (price != null && price >= 0) {
if (price != shop.getPrice()) {
shop.setPrice(price);
}
}
}
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
).build();
actions.add(saveButton);
}
List<SingleOptionDialogInput.OptionEntry> itemEntries() {
List<SingleOptionDialogInput.OptionEntry> optionEntries = new ArrayList<>();
if (getPlayer() == null) {
return optionEntries;
}
optionEntries.add(
SingleOptionDialogInput.OptionEntry.create("reset", MiniMessage.miniMessage().deserialize("<gold>Reset"), false)
);
for (int i = 0; i < getPlayer().getInventory().getSize() ; i++) {
ItemStack item = getPlayer().getInventory().getItem(i);
if (item == null || item.getType() == Material.AIR) continue;
ItemStack itemStack = item.clone();
optionEntries.add(
SingleOptionDialogInput.OptionEntry.create(String.valueOf(i), ShopUtil.spriteComponent(itemStack), shop.getItemStack() == itemStack)
);
}
return optionEntries;
}
}

View File

@ -0,0 +1,79 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.utils.ShopUtil;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class SearchShopsDialog extends AbstractDialog {
List<PlayerShop> shops;
public SearchShopsDialog(UUID uuid) {
super(uuid);
// this.shops = PlayerShops.getInstance().getShopHandler().getShops(playerToList);
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
5)
)
.base(DialogBase.builder(MiniMessage.miniMessage().deserialize(MessageConfig.GUI_LIST_SHOPS_TITLE))
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
for (Map.Entry<Material, List<PlayerShop>> entry : PlayerShops.getInstance().getShopHandler().shopByMaterial().entrySet()) {
actions.add(ActionButton.builder(ShopUtil.spriteComponent(entry.getKey()))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
ListShopsDialog listShopsDialog = new ListShopsDialog(uuid, entry.getValue());
listShopsDialog.setLastGui(this);
listShopsDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build());
}
}
}

View File

@ -0,0 +1,128 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.config.MessageConfig;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.UUID;
public class ShopDialog extends AbstractDialog {
public ShopDialog(UUID uuid) {
super(uuid);
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
3)
)
.base(DialogBase.builder(MiniMessage.miniMessage().deserialize(MessageConfig.GUI_HOME_TITLE))
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
actions.add(
ActionButton.builder(
MiniMessage.miniMessage().deserialize("Your shops"))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
ListShopsDialog abstractDialog = new ListShopsDialog(uuid, uuid);
abstractDialog.setLastGui(this);
abstractDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);
actions.add(
ActionButton.builder(
MiniMessage.miniMessage().deserialize("All shops"))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
ListPlayersDialog abstractDialog = new ListPlayersDialog(uuid);
abstractDialog.setLastGui(this);
abstractDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);
actions.add(
ActionButton.builder(
MiniMessage.miniMessage().deserialize("Shops per item"))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
SearchShopsDialog abstractDialog = new SearchShopsDialog(uuid);
abstractDialog.setLastGui(this);
abstractDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);
/* actions.add(
ActionButton.builder(
MiniMessage.miniMessage().deserialize("Settings"))
.action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
ShopSettingsDialog abstractDialog = new ShopSettingsDialog(uuid);
abstractDialog.setLastGui(this);
abstractDialog.open();
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
)
.build()
);*/
}
}

View File

@ -0,0 +1,97 @@
package com.alttd.playershops.dialog;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerSettings;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.UUID;
public class ShopSettingsDialog extends AbstractDialog {
PlayerSettings playerSettings;
public ShopSettingsDialog(UUID uuid) {
super(uuid);
playerSettings = PlayerShops.getInstance().getShopHandler().getPlayerSettings(uuid);
dialog = initDialog();
}
@Override
Dialog initDialog() {
if (getPlayer() == null) {
return null;
}
initContents();
return Dialog.create(dialogRegistryBuilderFactory ->
dialogRegistryBuilderFactory.empty()
.type(DialogType.multiAction(
actions,
closeButton,
1)
)
.base(DialogBase.builder(MiniMessage.miniMessage().deserialize(MessageConfig.GUI_PLAYER_SETTINGS_TITLE))
.body(texts)
.inputs(inputs)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.build()
)
);
}
@Override
void initContents() {
super.initContents();
for(Object2BooleanMap.Entry<PlayerSettings.Option> entry : playerSettings.optionsMap.object2BooleanEntrySet()) {
String setting = entry.getKey().name();
System.out.println(setting);
inputs.add(
DialogInput.bool(setting, MiniMessage.miniMessage().deserialize("<gold>" + entry.getKey().toString() + "</gold>"))
.initial(playerSettings.getOption(entry.getKey()))
.build()
);
}
actions.add(
ActionButton.builder(MiniMessage.miniMessage().deserialize("<gold>Confirm</gold>")).action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
// if (Boolean.TRUE.equals(view.getBoolean("changeitem"))) {
//
// }
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
).build()
);
actions.add(
ActionButton.builder(MiniMessage.miniMessage().deserialize("<gold>Set shop default values</gold>")).action(
DialogAction.customClick((view, audience) -> {
if (!(audience instanceof Player player)) {
return;
}
}, ClickCallback.Options.builder()
.lifetime(Duration.ofMinutes(5))
.uses(-1)
.build())
).build()
);
}
}

View File

@ -1,158 +0,0 @@
package com.alttd.playershops.gui;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public abstract class AbstractGui implements InventoryHolder {
Inventory inventory;
protected final int INV_SIZE = 54;
int currentSlot;
UUID uuid;
int pageIndex;
AbstractGui lastGui;
AbstractGui(UUID uuid) {
this.uuid = uuid;
this.currentSlot = 0;
this.lastGui = null;
}
public void open() {
Player player = getPlayer();
if (player != null) {
player.openInventory(inventory);
}
}
boolean close() {
Player player = getPlayer();
if (player != null) {
player.closeInventory();
return true;
}
return false;
}
void scrollPageNext() {
ItemStack nextPageIcon = inventory.getItem(GuiIcon.MENUBAR_NEXT_PAGE.getSlot());
if (nextPageIcon != null && nextPageIcon.equals(getNextPageIcon())) {
inventory.setItem(GuiIcon.MENUBAR_NEXT_PAGE.getSlot(), getPrevPageIcon());
++pageIndex;
initInvContents();
}
}
void scrollPagePrev() {
ItemStack prevPageIcon = inventory.getItem(GuiIcon.MENUBAR_PREV_PAGE.getSlot());
if (prevPageIcon != null && prevPageIcon.equals(getPrevPageIcon())) {
inventory.setItem(GuiIcon.MENUBAR_PREV_PAGE.getSlot(), getNextPageIcon());
--pageIndex;
if (pageIndex == 0) {
inventory.setItem(GuiIcon.MENUBAR_PREV_PAGE.getSlot(), null);
}
initInvContents();
}
}
protected boolean addItem(int slot, ItemStack icon) {
currentSlot = slot;
return addItem(icon);
}
protected boolean addItem(ItemStack icon) {
if (currentSlot == inventory.getSize() - 9)
return false;
inventory.setItem(currentSlot, icon);
currentSlot++;
return true;
}
void initInvContents() {
clearInvBody();
currentSlot = 0;
}
protected void clearInvBody() {
for (int i = 0; i < inventory.getSize() - 9; ++i) {
// inventory.setItem(i, null);
inventory.setItem(i, getDivider());
}
}
void makeMenuBar() {
for (int i = inventory.getSize() - 9; i < inventory.getSize(); ++i) {
inventory.setItem(i, getDivider());
}
}
Player getPlayer() {
return Bukkit.getPlayer(uuid);
}
@NotNull
@Override
public Inventory getInventory() {
return inventory;
}
ItemStack getPrevPageIcon() {
return GuiIcon.MENUBAR_PREV_PAGE.getItemStack();
}
ItemStack getNextPageIcon() {
return GuiIcon.MENUBAR_NEXT_PAGE.getItemStack();
}
ItemStack getExitIcon() {
return null;
}
ItemStack getDivider() {
return GuiIcon.DIVIDER.getItemStack();
}
public void onClick(int slot, ItemStack item) {
// TODO a better way to do this.
// check all menu actions here
if (slot == GuiIcon.MENUBAR_BACK.getSlot() && GuiIcon.MENUBAR_BACK.getItemStack().equals(item)) {
if (hasLastGui())
lastGui.open();
} else if (slot == GuiIcon.MENUBAR_EXIT.getSlot() && GuiIcon.MENUBAR_EXIT.getItemStack().equals(item)) {
getPlayer().closeInventory();
} else if (slot == GuiIcon.MENUBAR_SEARCH.getSlot() && GuiIcon.MENUBAR_SEARCH.getItemStack().equals(item)) {
// TODO
} else if (slot == GuiIcon.MENUBAR_PREV_PAGE.getSlot() && GuiIcon.MENUBAR_PREV_PAGE.getItemStack().equals(item)) {
scrollPagePrev();
} else if (slot == GuiIcon.MENUBAR_NEXT_PAGE.getSlot() && GuiIcon.MENUBAR_NEXT_PAGE.getItemStack().equals(item)) {
scrollPageNext();
}
}
public void setLastGui(AbstractGui lastGui) {
this.lastGui = lastGui;
inventory.setItem(GuiIcon.MENUBAR_BACK.getSlot(), GuiIcon.MENUBAR_BACK.getItemStack());
}
public boolean hasLastGui() {
return lastGui != null;
}
void addPrevPageItem() {
inventory.setItem(GuiIcon.MENUBAR_PREV_PAGE.getSlot(), GuiIcon.MENUBAR_PREV_PAGE.getItemStack());
}
void addNextPageItem() {
inventory.setItem(GuiIcon.MENUBAR_NEXT_PAGE.getSlot(), GuiIcon.MENUBAR_NEXT_PAGE.getItemStack());
}
}

View File

@ -1,83 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.config.GuiIconConfig;
import com.alttd.playershops.utils.Util;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
public enum GuiIcon {
DIVIDER,
MENUBAR_BACK,
MENUBAR_EXIT,
MENUBAR_SEARCH,
MENUBAR_PREV_PAGE,
MENUBAR_NEXT_PAGE,
MANAGE_SHOP,
MANAGE_SHOP_BALANCE_ADD,
MANAGE_SHOP_BALANCE_REMOVE,
MANAGE_SHOP_SALES,
MANAGE_SHOP_ITEM,
MANAGE_SHOP_TYPE,
MANAGE_SHOP_AMOUNT,
MANAGE_SHOP_PRICE,
MANAGE_SHOP_COLLECT_SALES,
MANAGE_SHOP_COLLECT_SALE,
HOME_LIST_OWN_SHOPS,
HOME_LIST_PLAYERS,
HOME_SETTINGS,
LIST_SHOP,
LIST_PLAYER,
LIST_PLAYER_ADMIN,
EMPTY_SHOP,
SETTING_ON,
SETTING_OFF;
@Getter
private final int slot;
@Getter
private final ItemStack itemStack;
GuiIcon() {
GuiIconConfig config = new GuiIconConfig(this.toString());
this.slot = config.slot;
this.itemStack = createItem(config.material, config.displayName, config.lore);
}
private ItemStack createItem(String materialString, String displayName, List<String> itemlore) {
Material material = Material.getMaterial(materialString);
if (material == null)
return null;
ItemStack item = new ItemStack(material);
ItemMeta itemMeta = item.getItemMeta();
itemMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
itemMeta.displayName(format(displayName));
List<Component> lore = new ArrayList<>();
for (String line : itemlore) {
lore.add(format(line));
}
itemMeta.lore(lore);
item.setItemMeta(itemMeta);
return item;
}
private Component format(String s) {
return Util.parseMiniMessage(s);
}
@Override
public String toString() {
return name().toLowerCase();
}
}

View File

@ -1,48 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.utils.Util;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import java.util.UUID;
public class HomeGui extends AbstractGui {
public HomeGui(UUID uuid) {
super(uuid);
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_HOME_TITLE));
makeMenuBar();
initInvContents();
}
@Override
void initInvContents() {
super.initInvContents();
inventory.setItem(GuiIcon.HOME_LIST_OWN_SHOPS.getSlot(), GuiIcon.HOME_LIST_OWN_SHOPS.getItemStack());
inventory.setItem(GuiIcon.HOME_LIST_PLAYERS.getSlot(), GuiIcon.HOME_LIST_PLAYERS.getItemStack());
inventory.setItem(GuiIcon.HOME_SETTINGS.getSlot(), GuiIcon.HOME_SETTINGS.getItemStack());
}
@Override
public void onClick(int slot, ItemStack item) {
super.onClick(slot, item);
if (slot >= 45)
return;
if (slot == GuiIcon.HOME_LIST_OWN_SHOPS.getSlot() && GuiIcon.HOME_LIST_OWN_SHOPS.getItemStack().equals(item)) {
ListShopsGui listShopsGui = new ListShopsGui(uuid, uuid);
listShopsGui.setLastGui(this);
listShopsGui.open();
} else if (slot == GuiIcon.HOME_LIST_PLAYERS.getSlot() && GuiIcon.HOME_LIST_PLAYERS.getItemStack().equals(item)) {
ListPlayersGui listPlayersGui = new ListPlayersGui(uuid);
listPlayersGui.setLastGui(this);
listPlayersGui.open();
}/* else if (slot == GuiIcon.HOME_SETTINGS.getSlot() && GuiIcon.HOME_SETTINGS.getItemStack().equals(item)) {
PlayerSettingsGui playerSettingsGui = new PlayerSettingsGui(uuid);
playerSettingsGui.setLastGui(this);
playerSettingsGui.open();
}*/
}
}

View File

@ -1,63 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.UUID;
public class ListPlayersGui extends AbstractGui {
private final List<UUID> owners;
public ListPlayersGui(UUID uuid) {
super(uuid);
this.owners = PlayerShops.getInstance().getShopHandler().getShopOwnersForThisServer();
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_LIST_PLAYERS_TITLE));
makeMenuBar();
initInvContents();
}
@Override
void initInvContents() {
super.initInvContents();
int startIndex = pageIndex * 45;
ItemStack item;
for (int i = startIndex; i < owners.size(); i++) {
item = ShopUtil.getPlayerHead(owners.get(i));
if (!addItem(item)) {
addNextPageItem();
break;
}
}
if (pageIndex > 0) {
addPrevPageItem();
}
}
@Override
public void onClick(int slot, ItemStack item) {
super.onClick(slot, item);
if (slot >= 45)
return;
if (item.getType() != Material.PLAYER_HEAD)
return;
int index = pageIndex * 45 + slot;
if (index >= owners.size())
return;
UUID playerToList = owners.get(index);
ListShopsGui listShopGUI = new ListShopsGui(uuid, playerToList);
listShopGUI.setLastGui(this);
listShopGUI.open();
}
}

View File

@ -1,123 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.utils.EconomyUtils;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.UUID;
public class ListShopsGui extends AbstractGui {
List<PlayerShop> shops;
UUID playerToList;
public ListShopsGui(UUID uuid, UUID playerToList) {
super(uuid);
this.playerToList = playerToList;
this.shops = PlayerShops.getInstance().getShopHandler().getShops(playerToList);
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerToList);
TagResolver placeholders = TagResolver.resolver(
Placeholder.unparsed("playername", offlinePlayer.hasPlayedBefore() ? offlinePlayer.getName() : "error")
);
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_LIST_SHOPS_TITLE, placeholders));
makeMenuBar();
initInvContents();
}
@Override
void makeMenuBar() {
super.makeMenuBar();
if (!uuid.equals(playerToList) && getPlayer().hasPermission("playershops.shop.collectall"))
return;
inventory.setItem(GuiIcon.MANAGE_SHOP_COLLECT_SALES.getSlot(), GuiIcon.MANAGE_SHOP_COLLECT_SALES.getItemStack());
}
@Override
void initInvContents() {
super.initInvContents();
// Todo add option to sort shops?
int startIndex = pageIndex * 45;
for (int i = startIndex; i < shops.size(); i++) {
PlayerShop shop = shops.get(i);
ItemStack item = shop.getItemStack();
if (!shop.isInitialized() || item == null)
item = GuiIcon.EMPTY_SHOP.getItemStack();
if (!addItem(item)) {
addNextPageItem();
break;
}
}
if (pageIndex > 0) {
addPrevPageItem();
}
}
@Override
public void onClick(int slot, ItemStack item) {
super.onClick(slot, item);
if (slot == GuiIcon.MANAGE_SHOP_COLLECT_SALES.getSlot() && GuiIcon.MANAGE_SHOP_COLLECT_SALES.getItemStack().equals(item)) {
Player player = getPlayer();
double d = 0;
int count = 0;
for (PlayerShop playerShop : shops) {
if (!ShopUtil.canManageShop(player, playerShop))
continue;
if (playerShop.getType().equals(ShopType.BUY))
continue;
double balance = playerShop.getBalance();
if (balance == 0)
continue;
playerShop.removeBalance(balance);
EconomyUtils.addFunds(player, balance);
d += balance;
count++;
}
TagResolver placeholders = TagResolver.resolver(
Placeholder.parsed("balance", String.valueOf(d)),
Placeholder.parsed("count", String.valueOf(count))
);
player.sendRichMessage(count > 0 ? "<green>You have collected <balance> from <count> shops." : "<red>No shops to be collected from.", placeholders);
return;
}
if (slot >= 45)
return;
Player player = getPlayer();
if (player == null)
return;
int index = pageIndex * 45 + slot;
if (index >= shops.size())
return;
PlayerShop playerShop = shops.get(index);
if (playerShop == null) return;
if (!ShopUtil.canManageShop(player, playerShop))
return;
ShopManagementGui shopManagementGui = new ShopManagementGui(player.getUniqueId(), playerShop);
shopManagementGui.setLastGui(this);
shopManagementGui.open();
}
}

View File

@ -1,44 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopTransaction;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.UUID;
public class ListTransactionsGui extends AbstractGui {
private final List<ShopTransaction> transactions;
public ListTransactionsGui(UUID uuid, PlayerShop shop) {
super(uuid);
this.transactions = shop.getTransactions();
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_LIST_TRANSACTIONS_TITLE));
makeMenuBar();
initInvContents();
}
@Override
void initInvContents() {
super.initInvContents();
int startIndex = pageIndex * 45;
ItemStack item;
for (int i = startIndex; i < transactions.size(); i++) {
item = ShopUtil.getShopTransactionItem(transactions.get(i));
if (!addItem(item)) {
addNextPageItem();
break;
}
}
if (pageIndex > 0) {
addPrevPageItem();
}
}
}

View File

@ -1,59 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.shop.PlayerSettings;
import com.alttd.playershops.utils.Util;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.UUID;
public class PlayerSettingsGui extends AbstractGui {
public PlayerSettingsGui(UUID uuid) {
super(uuid);
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_PLAYER_SETTINGS_TITLE));
makeMenuBar();
initInvContents();
}
@Override
void initInvContents() {
super.initInvContents();
int startIndex = pageIndex * 45;
ItemStack item;
for(Object2BooleanMap.Entry<PlayerSettings.Option> entry : PlayerShops.getInstance().getShopHandler().getPlayerSettings(uuid).optionsMap.object2BooleanEntrySet()) {
item = entry.getBooleanValue() ? GuiIcon.SETTING_ON.getItemStack() : GuiIcon.SETTING_OFF.getItemStack();
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta.hasDisplayName()) {
itemMeta.displayName(
itemMeta.displayName().replaceText(
TextReplacementConfig.builder()
.match("<setting>")
.replacement(entry.getKey().toString().toLowerCase().replace("_", " ")).build()));
}
item.setItemMeta(itemMeta);
if (!addItem(item)) {
addNextPageItem();
break;
}
}
if (pageIndex > 0) {
addPrevPageItem();
}
}
@Override
public void onClick(int slot, ItemStack item) {
super.onClick(slot, item);
if (slot >= 45)
return;
}
}

View File

@ -1,142 +0,0 @@
package com.alttd.playershops.gui;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.conversation.ConversationManager;
import com.alttd.playershops.conversation.ConversationType;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopType;
import com.alttd.playershops.utils.EconomyUtils;
import com.alttd.playershops.utils.ShopUtil;
import com.alttd.playershops.utils.Util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ShopManagementGui extends AbstractGui {
PlayerShop shop;
public ShopManagementGui(UUID uuid, PlayerShop shop) {
super(uuid);
this.inventory = Bukkit.createInventory(this, INV_SIZE, Util.parseMiniMessage(MessageConfig.GUI_MANAGE_TITLE));
this.shop = shop;
makeMenuBar();
initInvContents();
}
@Override
void initInvContents() {
super.initInvContents();
ItemStack shopIcon = GuiIcon.MANAGE_SHOP.getItemStack();
ItemMeta meta = shopIcon.getItemMeta();
List<Component> lore = new ArrayList<>();
TagResolver placeholders = TagResolver.resolver(
Placeholder.unparsed("balance", shop.getBalance() + ""),
Placeholder.unparsed("price", shop.getPrice() + ""),
Placeholder.unparsed("amount", shop.getAmount() + ""),
Placeholder.unparsed("shoptype", shop.getType().toString()),
Placeholder.component("itemname", ShopUtil.itemNameComponent(shop.getItemStack()))
);
lore.add(Util.parseMiniMessage("Balance: <balance>", placeholders));
lore.add(Util.parseMiniMessage("item: <itemname>", placeholders));
lore.add(Util.parseMiniMessage("amount: <amount>", placeholders));
lore.add(Util.parseMiniMessage("Type: <shoptype>", placeholders));
lore.add(Util.parseMiniMessage("Price: <price>", placeholders));
meta.lore(lore);
shopIcon.setItemMeta(meta);
inventory.setItem(GuiIcon.MANAGE_SHOP.getSlot(), shopIcon);
inventory.setItem(GuiIcon.MANAGE_SHOP_BALANCE_ADD.getSlot(), GuiIcon.MANAGE_SHOP_BALANCE_ADD.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_BALANCE_REMOVE.getSlot(), GuiIcon.MANAGE_SHOP_BALANCE_REMOVE.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_COLLECT_SALE.getSlot(), GuiIcon.MANAGE_SHOP_COLLECT_SALE.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_SALES.getSlot(), GuiIcon.MANAGE_SHOP_SALES.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_ITEM.getSlot(), GuiIcon.MANAGE_SHOP_ITEM.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_TYPE.getSlot(), GuiIcon.MANAGE_SHOP_TYPE.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_AMOUNT.getSlot(), GuiIcon.MANAGE_SHOP_AMOUNT.getItemStack());
inventory.setItem(GuiIcon.MANAGE_SHOP_PRICE.getSlot(), GuiIcon.MANAGE_SHOP_PRICE.getItemStack());
}
@Override
void makeMenuBar() {
super.makeMenuBar();
}
@Override
public void onClick(int slot, ItemStack item) {
super.onClick(slot, item);
if (slot == GuiIcon.MANAGE_SHOP_COLLECT_SALE.getSlot() && GuiIcon.MANAGE_SHOP_COLLECT_SALE.getItemStack().equals(item)) {
Player player = getPlayer();
double d = 0;
int count = 0;
if (!ShopUtil.canManageShop(player, this.shop))
return;
if (this.shop.getType().equals(ShopType.BUY))
return;
double balance = this.shop.getBalance();
if (balance == 0)
return;
this.shop.removeBalance(balance);
EconomyUtils.addFunds(player, balance);
d += balance;
count++;
TagResolver placeholders = TagResolver.resolver(
Placeholder.parsed("balance", String.valueOf(d)),
Placeholder.parsed("count", String.valueOf(count))
);
player.sendRichMessage("<green>You have collected <balance> from <count> shops.", placeholders);
return;
}
if (slot >= 45)
return;
if (slot == GuiIcon.MANAGE_SHOP.getSlot() && GuiIcon.MANAGE_SHOP.getItemStack().equals(item)) {
} else if (slot == GuiIcon.MANAGE_SHOP_BALANCE_ADD.getSlot() && GuiIcon.MANAGE_SHOP_BALANCE_ADD.getItemStack().equals(item)) {
if (EconomyUtils.getFunds(getPlayer()) > 0) {
openChangePrompt(ConversationType.ADD_BALANCE);
} else {
getPlayer().sendRichMessage("<red>You do not have money to add to this shop");
}
} else if (slot == GuiIcon.MANAGE_SHOP_BALANCE_REMOVE.getSlot() && GuiIcon.MANAGE_SHOP_BALANCE_REMOVE.getItemStack().equals(item)) {
if (shop.getBalance() > 0) {
openChangePrompt(ConversationType.WITHDRAW_BALANCE);
} else {
getPlayer().sendRichMessage("<red>You can't withdraw money from this shop");
}
} else if (slot == GuiIcon.MANAGE_SHOP_SALES.getSlot() && GuiIcon.MANAGE_SHOP_SALES.getItemStack().equals(item)) {
ListTransactionsGui listTransactionsGui = new ListTransactionsGui(uuid, shop);
listTransactionsGui.setLastGui(this);
listTransactionsGui.open();
} else if (slot == GuiIcon.MANAGE_SHOP_ITEM.getSlot() && GuiIcon.MANAGE_SHOP_ITEM.getItemStack().equals(item)) {
openChangePrompt(ConversationType.CHANGE_ITEM);
} else if (slot == GuiIcon.MANAGE_SHOP_TYPE.getSlot() && GuiIcon.MANAGE_SHOP_TYPE.getItemStack().equals(item)) {
openChangePrompt(ConversationType.CHANGE_TYPE);
} else if (slot == GuiIcon.MANAGE_SHOP_AMOUNT.getSlot() && GuiIcon.MANAGE_SHOP_AMOUNT.getItemStack().equals(item)) {
openChangePrompt(ConversationType.CHANGE_AMOUNT);
} else if (slot == GuiIcon.MANAGE_SHOP_PRICE.getSlot() && GuiIcon.MANAGE_SHOP_PRICE.getItemStack().equals(item)) {
openChangePrompt(ConversationType.CHANGE_PRICE);
}
}
private void openChangePrompt(ConversationType conversationType) {
Player player = getPlayer();
player.closeInventory();
new ConversationManager(PlayerShops.getInstance(), player, conversationType, shop);
}
}

View File

@ -6,14 +6,18 @@ import com.alttd.playershops.shop.PlayerSettings;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.storage.database.DatabaseHelper;
import com.alttd.playershops.utils.Logger;
import com.alttd.playershops.utils.Pair;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.DoubleChest;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
@ -178,4 +182,47 @@ public class ShopHandler {
return playerSettings.computeIfAbsent(uuid, PlayerSettings::loadFromFile);
}
public void transferShops(CommandSender sender, OfflinePlayer oldOwner, OfflinePlayer newOwner) {
if (!oldOwner.hasPlayedBefore()) {
sender.sendRichMessage("<red>" + oldOwner.getName() + " has not joined this server before.");
return;
}
if (!newOwner.hasPlayedBefore()) {
sender.sendRichMessage("<red>" + newOwner.getName() + " has not joined this server before.");
return;
}
List<PlayerShop> playerShops = PlayerShops.getInstance().getShopHandler().getShops(oldOwner.getUniqueId());
sender.sendRichMessage("<red>Starting the transfer process now, this might lag the server.");
for (PlayerShop playerShop : playerShops) {
playerShop.setOwner(newOwner);
}
if (newOwner.isOnline() && newOwner.getPlayer() != null) {
newOwner.getPlayer().sendRichMessage(playerShops.size() + " have been transferred to you.");
}
sender.sendRichMessage(playerShops.size() + " from " + oldOwner.getName() + " have been transferred to " + newOwner.getName() + ".");
}
/**
* On demand calculation of shops per material
*
* @return TreeMap containing a list of PlayerShops per material.
*/
public TreeMap<Material, List<PlayerShop>> shopByMaterial() {
// TODO -- make this a cache. update cache on change, do not calc dynamicly
TreeMap<Material, List<PlayerShop>> shopByMaterialTreeSet = new TreeMap<>();
for (PlayerShop shop : getShops()) {
if (!shop.isInitialized()) {
continue;
}
Material material = shop.getItemStack().getType();
List<PlayerShop> shopList = shopByMaterialTreeSet.computeIfAbsent(material, v -> new ArrayList<>());
shopList.add(shop);
}
return shopByMaterialTreeSet;
}
}

View File

@ -1,44 +0,0 @@
package com.alttd.playershops.listener;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.gui.AbstractGui;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
public class InventoryListener extends EventListener {
private final PlayerShops plugin;
public InventoryListener(PlayerShops plugin) {
this.plugin = plugin;
this.register(this.plugin);
}
@EventHandler
public void onInventoryClickEvent(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player))
return;
if (!(event.getView().getTopInventory().getHolder() instanceof AbstractGui gui))
return;
if ((event.getView().getBottomInventory().equals(event.getClickedInventory())))
return;
if (event.getClick() == ClickType.NUMBER_KEY) {
event.setCancelled(true);
return;
}
ItemStack clicked = event.getCurrentItem();
if (!(clicked != null && clicked.getType() != Material.AIR))
return;
event.setCancelled(true);
gui.onClick(event.getRawSlot(), clicked);
}
}

View File

@ -2,7 +2,7 @@ package com.alttd.playershops.listener;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.config.MessageConfig;
import com.alttd.playershops.gui.ShopManagementGui;
import com.alttd.playershops.dialog.ManageShopDialog;
import com.alttd.playershops.handler.ShopHandler;
import com.alttd.playershops.hook.WorldGuardHook;
import com.alttd.playershops.shop.PlayerShop;
@ -92,8 +92,10 @@ public class TransactionListener extends EventListener {
event.setCancelled(true);
return;
}
ShopManagementGui gui = new ShopManagementGui(player.getUniqueId(), playerShop);
gui.open();
// ShopManagementGui gui = new ShopManagementGui(player.getUniqueId(), playerShop);
// gui.open();
ManageShopDialog manageShopDialog = new ManageShopDialog(player.getUniqueId(), playerShop);
manageShopDialog.open();
event.setCancelled(true);
return;
}

View File

@ -12,7 +12,12 @@ import java.util.UUID;
public class PlayerSettings {
public enum Option {
SALE_OWNER_NOTIFICATIONS, SALE_USER_NOTIFICATIONS,
STOCK_NOTIFICATIONS, DISCORD_STOCK_NOTIFICATIONS
STOCK_NOTIFICATIONS, DISCORD_STOCK_NOTIFICATIONS;
@Override
public String toString() {
return this.name().toLowerCase().replace("_", " ");
}
}
private final UUID uuid;

View File

@ -3,18 +3,25 @@ package com.alttd.playershops.shop;
import com.alttd.playershops.PlayerShops;
import com.alttd.playershops.events.*;
import com.alttd.playershops.utils.*;
import com.alttd.playershops.utils.sprite.MaterialSprites;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
@ -30,8 +37,8 @@ public class PlayerShop {
private final Location signLocation;
private final Location shopLocation;
private String server;
private double price;
private int amount;
private double price = 0;
private int amount = 1;
private double balance;
private ItemStack itemStack;
@Setter
@ -109,7 +116,7 @@ public class PlayerShop {
}
public boolean matches(ItemStack item) {
return ShopUtil.matches(getItemStack(), item);
return ItemMatcher.matches(getItemStack(), item);
}
public void remove(ItemStack item, int amount) {
@ -134,7 +141,7 @@ public class PlayerShop {
}
}
public void setOwner(Player player) {
public void setOwner(OfflinePlayer player) {
ownerUUID = player.getUniqueId();
ownerName = player.getName();
update();
@ -171,14 +178,15 @@ public class PlayerShop {
public void run() {
if (!(signLocation.getBlock().getState() instanceof Sign signBlock)) return;
MiniMessage miniMessage = MiniMessage.miniMessage();
Component itemComponent = getItemStack() == null ? ShopUtil.trimmedItemNameComponent(getItemStack()): MaterialSprites.get(getItemStack().getType()).append(Component.space()).append(ShopUtil.trimmedItemNameComponent(getItemStack()));
TagResolver tagResolver = TagResolver.resolver(
Placeholder.unparsed("ownername", getOwnerName()),
Placeholder.unparsed("price", trimPrice(String.valueOf(ShopUtil.round(getPrice())))),
Placeholder.unparsed("amount", String.valueOf(getAmount())),
Placeholder.component("itemname", ShopUtil.trimmedItemNameComponent(getItemStack()))
Placeholder.component("itemname", itemComponent)
);
for (int i = 0; i < 4; i++) {
signBlock.line(i, miniMessage.deserialize(signLines.get(i), tagResolver));
signBlock.getSide(Side.FRONT).line(i, miniMessage.deserialize(signLines.get(i), tagResolver));
}
signBlock.update(true);
}
@ -360,8 +368,12 @@ public class PlayerShop {
if (Util.callCancellableEvent(shopItemChangeEvent))
return; // cancelled by another plugin, does this need logging?
this.itemStack = itemStack;
if (itemStack == null) {
this.itemStack = null;
} else {
this.itemStack = itemStack.clone();
this.itemStack.setAmount(this.amount != 0 ? this.amount : 1);
}
setDirty(true);
update();
}
@ -451,4 +463,25 @@ public class PlayerShop {
+ " balance :" + this.balance
+ " lastTransaction :" + this.lastTransaction;
}
public Component getHoverComponent(Player player) {
Component component = ShopUtil.itemNameComponent(getItemStack())
.append(Component.newline())
.append(Component.newline())
.append(Component.text("ShopType: " + type))
.append(Component.newline())
.append(Component.text("Price: " + price))
.append(Component.newline())
.append(Component.text("ShopType: " + amount))
.append(Component.newline())
.append(Component.text("World: " + shopLocation.getWorld().getName()))
.append(Component.newline())
.append(Component.text("Coordinates: " + shopLocation.getBlockX() + ", " + shopLocation.getBlockY() + ", " + shopLocation.getBlockZ()))
;
if (getOwnerUUID().equals(player.getUniqueId())) {
component = component.append(Component.newline()).append(Component.text("Balance: " + balance));
}
return component;
}
}

View File

@ -65,4 +65,37 @@ public class EconomyUtils {
return bd.doubleValue();
}
public static Double parseValue(String input) {
Double value = null;
if (input == null || input.isEmpty()) {
return null;
}
try {
value = Double.parseDouble(input);
} catch (NumberFormatException ex) {
Double multiplier = multiplierValue(input.substring(input.length() - 1));
if (multiplier == null) {
multiplier = 1.0;
}
try {
value = Double.parseDouble(input.substring(0, input.length() - 1));
} catch (NumberFormatException ignored) {}
if (value != null) {
value = value * multiplier;
}
}
return value;
}
private static Double multiplierValue(String input) {
Double value = null;
switch(input.toLowerCase()) {
case "k" -> value = 1000.0;
case "m" -> value = 1000000.0;
}
return value;
}
}

View File

@ -25,7 +25,7 @@ public class InventoryUtils {
for (ItemStack iStack : inv.getContents()) {
if (iStack == null)
continue;
if (ShopUtil.matches(item, iStack)) {
if (ItemMatcher.matches(item, iStack)) {
items += iStack.getAmount();
}
}
@ -46,7 +46,7 @@ public class InventoryUtils {
int amount = itemStack.getAmount();
for (ItemStack stack : contents) {
if (stack != null) {
if (ShopUtil.matches(stack, itemStack)) {
if (ItemMatcher.matches(stack, itemStack)) {
if (stack.getAmount() > amount) {
stack.setAmount(stack.getAmount() - amount);
inventory.setContents(contents);

View File

@ -0,0 +1,335 @@
package com.alttd.playershops.utils;
import org.bukkit.attribute.Attribute;
import org.bukkit.block.BlockState;
import org.bukkit.block.ShulkerBox;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.map.MapView;
import org.bukkit.potion.PotionEffect;
import java.util.*;
public class ItemMatcher {
private final static List<Matcher> matcherList = new ArrayList<>();
static {
// DisplayName
matcherList.add(
(meta1, meta2) ->
Objects.equals(meta1.displayName(), meta2.displayName())
);
// Damage
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof Damageable damageable1 && meta2 instanceof Damageable damageable2) {
return damageable1.getDamage() == damageable2.getDamage();
}
return true;
}
);
// Enchants
matcherList.add(
(meta1, meta2) -> {
if (meta1.hasEnchants() != meta2.hasEnchants()) {
return false;
}
if (meta1.hasEnchants()) {
final Map<Enchantment, Integer> enchants1 = meta1.getEnchants();
final Map<Enchantment, Integer> enchants2 = meta2.getEnchants();
return listMatches(enchants1.entrySet(), enchants2.entrySet());
}
return true;
}
);
// Potions
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof PotionMeta potion1 && meta2 instanceof PotionMeta potion2) {
if (potion1.hasColor() != potion2.hasColor()) {
return false;
}
if (potion1.hasColor() && !Objects.equals(potion1.getColor(), potion2.getColor())) {
return false;
}
if (potion1.hasCustomEffects() != potion2.hasCustomEffects()) {
return false;
}
if (potion1.hasCustomEffects() && !Arrays.deepEquals(potion1.getCustomEffects().toArray(), potion2.getCustomEffects().toArray())) {
return false;
}
if (potion1.getBasePotionType() != null && potion2.getBasePotionType() != null) {
List<PotionEffect> effects1 = new ArrayList<>();
List<PotionEffect> effects2 = new ArrayList<>();
if (potion1.getBasePotionType() != null) {
effects1.addAll(potion1.getBasePotionType().getPotionEffects());
}
if (potion1.hasCustomEffects()) {
effects1.addAll(potion1.getCustomEffects());
}
if (potion2.getBasePotionType() != null) {
effects2.addAll(potion2.getBasePotionType().getPotionEffects());
}
if (potion2.hasCustomEffects()) {
effects2.addAll(potion2.getCustomEffects());
}
return listMatches(effects1, effects2);
}
}
return true;
}
);
// Attributes
matcherList.add(
(meta1, meta2) -> {
if (meta1.hasAttributeModifiers() != meta2.hasAttributeModifiers()) {
return false;
}
if (meta1.hasAttributeModifiers() && meta2.hasAttributeModifiers()) {
final Set<Attribute> set1 = Objects.requireNonNull(meta1.getAttributeModifiers()).keySet();
final Set<Attribute> set2 = Objects.requireNonNull(meta2.getAttributeModifiers()).keySet();
for(final Attribute att : set1) {
if(!set2.contains(att)) {
return false;
} else if(!meta1.getAttributeModifiers().get(att).equals(meta2.getAttributeModifiers().get(att))) {
return false;
}
}
}
return true;
}
);
// Itemflags
matcherList.add(
(meta1, meta2) -> {
return Arrays.deepEquals(meta1.getItemFlags().toArray(), meta2.getItemFlags().toArray());
}
);
// Books
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof BookMeta bookMeta1 && meta2 instanceof BookMeta bookMeta2) {
if (bookMeta1.hasTitle() != bookMeta2.hasTitle()) {
return false;
}
if (bookMeta1.hasTitle() && !Objects.equals(bookMeta1.getTitle(), bookMeta2.getTitle())) {
return false;
}
if (bookMeta1.hasPages() != bookMeta2.hasPages()) {
return false;
}
if (bookMeta1.hasPages() && !bookMeta1.getPages().equals(bookMeta2.getPages())) {
return false;
}
if (bookMeta1.hasAuthor() != bookMeta2.hasAuthor()) {
return false;
}
if (bookMeta1.hasAuthor() && !Objects.equals(bookMeta1.getAuthor(), bookMeta2.getAuthor())) {
return false;
}
if (bookMeta1.hasGeneration() != bookMeta2.hasGeneration()) {
return false;
}
return !bookMeta1.hasGeneration() || Objects.equals(bookMeta1.getGeneration(), bookMeta2.getGeneration());
}
return true;
}
);
// Fireworks
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof FireworkMeta fireworkMeta1 && meta2 instanceof FireworkMeta fireworkMeta2) {
if (fireworkMeta1.hasEffects() != fireworkMeta2.hasEffects()) {
return false;
}
if (!fireworkMeta1.getEffects().equals(fireworkMeta2.getEffects())) {
return false;
}
return fireworkMeta1.getPower() == fireworkMeta2.getPower();
}
return true;
}
);
// Banners
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof BannerMeta bannerMeta1 && meta2 instanceof BannerMeta bannerMeta2) {
if (bannerMeta1.numberOfPatterns() != bannerMeta2.numberOfPatterns()) {
return false;
}
return new HashSet<>(bannerMeta1.getPatterns()).containsAll(bannerMeta2.getPatterns());
}
return true;
}
);
// Skulls/heads
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof SkullMeta skullMeta1 && meta2 instanceof SkullMeta skullMeta2) {
return Objects.equals(skullMeta1.getOwningPlayer(), skullMeta2.getOwningPlayer());
}
return true;
}
);
// Bundles
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof BundleMeta bundleMeta1 && meta2 instanceof BundleMeta bundleMeta2) {
if (bundleMeta1.hasItems() != bundleMeta2.hasItems()) {
return false;
}
if (bundleMeta1.hasItems()) {
return listMatches(bundleMeta1.getItems(), bundleMeta2.getItems());
}
}
return true;
}
);
// Maps
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof MapMeta mapMeta1 && meta2 instanceof MapMeta mapMeta2) {
if (mapMeta1.hasMapView() != mapMeta2.hasMapView()) {
return false;
}
// TODO -- do we need checks on color?
// mapMeta1.getColor()
MapView mapView1 = mapMeta1.getMapView();
MapView mapView2 = mapMeta2.getMapView();
if (mapView1 == null || mapView2 == null)
return false;
if (mapView1.getId() == mapView2.getId())
return true;
}
return true;
}
);
// Leather
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof LeatherArmorMeta leatherArmorMeta1 && meta2 instanceof LeatherArmorMeta leatherArmorMeta2) {
return leatherArmorMeta1.getColor().equals(leatherArmorMeta2.getColor());
}
return true;
}
);
// Tropical fishes
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof TropicalFishBucketMeta tropicalFishBucketMeta1 && meta2 instanceof TropicalFishBucketMeta tropicalFishBucketMeta2) {
if (tropicalFishBucketMeta1.hasVariant() != tropicalFishBucketMeta2.hasVariant()) {
return false;
}
return !tropicalFishBucketMeta1.hasVariant()
|| (tropicalFishBucketMeta1.getPattern() == tropicalFishBucketMeta2.getPattern()
&& tropicalFishBucketMeta1.getBodyColor().equals(tropicalFishBucketMeta2.getBodyColor())
&& tropicalFishBucketMeta1.getPatternColor().equals(tropicalFishBucketMeta2.getPatternColor()));
}
return true;
}
);
// Axolotls
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof AxolotlBucketMeta axolotlBucketMeta1 && meta2 instanceof AxolotlBucketMeta axolotlBucketMeta2) {
return axolotlBucketMeta1.getVariant() == axolotlBucketMeta2.getVariant();
}
return true;
}
);
// Shulkerboxes
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof BlockStateMeta blockStateMeta1 && meta2 instanceof BlockStateMeta blockStateMeta2) {
// extra heavy - Banners, Shulkerboxes, beehive and more?
BlockState blockState1 = blockStateMeta1.getBlockState();
BlockState blockState2 = blockStateMeta2.getBlockState();
if (blockState1 instanceof ShulkerBox shulkerBox1 && blockState2 instanceof ShulkerBox shulkerBox2) {
if (shulkerBox1.getColor() != shulkerBox2.getColor())
return false; // not the same color
// Do we need all of the above checks inside the shulker?
if (Arrays.equals(shulkerBox1.getInventory().getContents(), shulkerBox2.getInventory().getContents()))
return true; // same content
}
}
return true;
}
);
// Suspicious stews
matcherList.add(
(meta1, meta2) -> {
if (meta1 instanceof SuspiciousStewMeta suspiciousStewMeta1 && meta2 instanceof SuspiciousStewMeta suspiciousStewMeta2) {
if (suspiciousStewMeta1.hasCustomEffects() != suspiciousStewMeta2.hasCustomEffects()) {
return false;
}
if (suspiciousStewMeta1.hasCustomEffects()) {
return listMatches(suspiciousStewMeta1.getCustomEffects(), suspiciousStewMeta2.getCustomEffects());
}
}
return true;
}
);
}
public static boolean matches(ItemStack item1, ItemStack item2) {
if (item1 == null && item2 == null) {
return true;
}
if (item1 == null || item2 == null) {
return true;
}
if (!item1.getType().equals(item2.getType())) {
return false;
}
if(item1.isSimilar(item2)) {
return true;
}
if (item1.hasItemMeta() && item2.hasItemMeta()) {
return metaMatches(item1, item2);
}
return !item1.hasItemMeta() && !item1.hasItemMeta();
}
protected static boolean metaMatches(ItemStack item1, ItemStack item2) {
ItemMeta meta1 = item1.getItemMeta();
ItemMeta meta2 = item2.getItemMeta();
if (meta1 != null && meta2 != null) {
for (Matcher matcher : matcherList) {
boolean result = matcher.match(meta1, meta2);
if (!result) {
return false;
}
}
return true;
}
return meta1 == null && meta2 == null;
}
private static boolean listMatches(Collection<?> list1, Collection<?> list2) {
return list1.containsAll(list2) && list2.containsAll(list1);
}
public static interface Matcher {
boolean match(ItemMeta meta1, ItemMeta meta2);
}
}

View File

@ -0,0 +1,5 @@
package com.alttd.playershops.utils;
public record Pair<X, Y>(X x, Y y) {
}

View File

@ -2,20 +2,17 @@ package com.alttd.playershops.utils;
import com.alttd.playershops.shop.PlayerShop;
import com.alttd.playershops.shop.ShopTransaction;
import com.alttd.playershops.utils.sprite.MaterialSprites;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.*;
import org.bukkit.block.BlockState;
import org.bukkit.block.ShulkerBox;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.map.MapView;
import java.util.*;
@ -37,167 +34,6 @@ public class ShopUtil {
return (loc.getWorld().isChunkLoaded(x, z));
}
/**
* Compares two items to each other. Returns true if they match.
*
* @param stack1
* The first item stack
* @param stack2
* The second item stack
* @return true if the itemstacks match. (Material, durability, enchants, name)
*/
public static boolean matches(ItemStack stack1, ItemStack stack2) {
if (stack1 == stack2)
return true; // Referring to the same thing, or both are null.
if (stack1 == null || stack2 == null)
return false; // One of them is null (Can't be both, see above)
if (stack1.getType() != stack2.getType())
return false; // Not the same material
if (stack1.getDurability() != stack2.getDurability())
return false; // Not the same durability
if (!stack1.getEnchantments().equals(stack2.getEnchantments()))
return false; // They have the same enchants
if (!stack1.getItemMeta().equals(stack2.getItemMeta()))
return false; // They have the same enchants
if (stack1.getItemMeta().hasDisplayName() || stack2.getItemMeta().hasDisplayName()) {
if (stack1.getItemMeta().hasDisplayName() && stack2.getItemMeta().hasDisplayName()) {
if (!stack1.getItemMeta().getDisplayName().equals(stack2.getItemMeta().getDisplayName())) {
return false; // items have different display name
}
} else {
return false; // one of the item stacks have a display name
}
}
try {
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
boolean book1 = stack1.getItemMeta() instanceof EnchantmentStorageMeta;
boolean book2 = stack2.getItemMeta() instanceof EnchantmentStorageMeta;
if (book1 != book2)
return false;// One has enchantment meta, the other does not.
if (book1 == true) { // They are the same here (both true or both
// false). So if one is true, the other is
// true.
Map<Enchantment, Integer> ench1 = ((EnchantmentStorageMeta) stack1.getItemMeta()).getStoredEnchants();
Map<Enchantment, Integer> ench2 = ((EnchantmentStorageMeta) stack2.getItemMeta()).getStoredEnchants();
if (!ench1.equals(ench2))
return false; // Enchants aren't the same.
}
} catch (ClassNotFoundException e) {
}
return matches2(stack1, stack2);
}
public static boolean matches2(ItemStack stack1, ItemStack stack2) {
if (stack1 == stack2)
return true; // Referring to the same thing, or both are null.
if (stack1 == null || stack2 == null)
return false; // One of them is null (Can't be both, see above)
if (stack1.getType() != stack2.getType())
return false; // Not the same material
if ((!stack1.hasItemMeta() && !stack2.hasItemMeta()))
return true; // Only one of the items has item meta
if (stack1.hasItemMeta() && stack2.hasItemMeta()) {
ItemMeta itemMeta1 = stack1.getItemMeta();
ItemMeta itemMeta2 = stack2.getItemMeta();
if ((itemMeta1.hasDisplayName() != itemMeta2.hasDisplayName()))
return false; // Only one has a display name
if (!itemMeta1.getDisplayName().equals(itemMeta2.getDisplayName()))
return false; // items have different display name
// This is where the heavy checks are :/
try {
Class.forName("org.bukkit.inventory.meta.EnchantmentStorageMeta");
boolean book1 = itemMeta1 instanceof EnchantmentStorageMeta;
boolean book2 = itemMeta2 instanceof EnchantmentStorageMeta;
if (book1 != book2)
return false;// One has enchantment meta, the other does not.
if (book1 == true) { // They are the same here (both true or both false). So if one is true, the other is true.
Map<Enchantment, Integer> ench1 = ((EnchantmentStorageMeta) itemMeta1).getStoredEnchants();
Map<Enchantment, Integer> ench2 = ((EnchantmentStorageMeta) itemMeta2).getStoredEnchants();
if (!ench1.equals(ench2))
return false; // Enchants aren't the same.
}
} catch (ClassNotFoundException ignored) {
}
if (itemMeta1 instanceof Damageable damageable1 && itemMeta2 instanceof Damageable damageable2)
if (damageable1.getDamage() != damageable2.getDamage())
return false; // Not the same durability
// We need this check now because mapart stores data in the map NBT
if (itemMeta1 instanceof MapMeta mapMeta1 && itemMeta2 instanceof MapMeta mapMeta2) {
MapView mapView1 = mapMeta1.getMapView();
MapView mapView2 = mapMeta2.getMapView();
if (mapView1 == null || mapView2 == null)
return false; // at least one is null
if (mapView1.getId() == mapView2.getId())
return true; // ID does not match
}
if (itemMeta1 instanceof TropicalFishBucketMeta tropicalFishBucketMeta1 && itemMeta2 instanceof TropicalFishBucketMeta tropicalFishBucketMeta2) {
if (tropicalFishBucketMeta1.getBodyColor() != tropicalFishBucketMeta2.getBodyColor())
return false;
if (tropicalFishBucketMeta1.getPattern() != tropicalFishBucketMeta2.getPattern())
return false;
if (tropicalFishBucketMeta1.getPatternColor() != tropicalFishBucketMeta2.getPatternColor())
return false;
}
if (itemMeta1 instanceof AxolotlBucketMeta axolotlBucketMeta1 && itemMeta2 instanceof AxolotlBucketMeta axolotlBucketMeta2) {
if (axolotlBucketMeta1.getVariant() != axolotlBucketMeta2.getVariant())
return false;
}
if (itemMeta1 instanceof BlockStateMeta blockStateMeta1 && itemMeta2 instanceof BlockStateMeta blockStateMeta2) {
// extra heavy - Banners, Shulkerboxes, beehive and more?
BlockState blockState1 = blockStateMeta1.getBlockState();
BlockState blockState2 = blockStateMeta2.getBlockState();
if (blockState1 instanceof ShulkerBox shulkerBox1 && blockState2 instanceof ShulkerBox shulkerBox2) {
if (shulkerBox1.getColor() != shulkerBox2.getColor())
return false; // not the same color
// Do we need all of the above checks inside the shulker?
if (Arrays.equals(shulkerBox1.getInventory().getContents(), shulkerBox2.getInventory().getContents()))
return true; // same content
}
}
// TODO
/* if (itemMeta1 instanceof BannerMeta bannerMeta1 && itemMeta2 instanceof BannerMeta bannerMeta2) {
if (bannerMeta1.numberOfPatterns() != bannerMeta2.numberOfPatterns())
return false;
if (!bannerMeta1.getPatterns().equals(bannerMeta2.getPatterns()))
return false;
}
if (itemMeta1 instanceof BookMeta bookMeta1 && itemMeta2 instanceof BookMeta bookMeta2) {
// Todo Books
}
if (itemMeta1 instanceof PotionMeta potionMeta1 && itemMeta2 instanceof PotionMeta potionMeta2) {
}*/
}
// if (!stack1.getEnchantments().equals(stack2.getEnchantments()))
// return false; // They have the same enchants
// if (!stack1.getItemMeta().equals(stack2.getItemMeta()))
// return false; // They have the same enchants
return Arrays.equals(stack1.serializeAsBytes(), stack2.serializeAsBytes());
}
/**
* Returns the number of items that can be given to the inventory safely.
*
@ -216,7 +52,7 @@ public class ShopUtil {
for (ItemStack iStack : contents) {
if (iStack == null || iStack.getType() == Material.AIR) {
space += item.getMaxStackSize();
} else if (matches(item, iStack)) {
} else if (ItemMatcher.matches(item, iStack)) {
space += item.getMaxStackSize() - iStack.getAmount();
}
}
@ -329,7 +165,7 @@ public class ShopUtil {
Component component;
MiniMessage miniMessage = MiniMessage.miniMessage();
if (item == null || item.getType().equals(Material.AIR))
return miniMessage.deserialize("NONE");
return miniMessage.deserialize("No item");
boolean dname = item.hasItemMeta() && item.getItemMeta().hasDisplayName();
if (dname) {
component = item.getItemMeta().displayName();
@ -366,4 +202,18 @@ public class ShopUtil {
public static double round(double price) {
return (double) Math.round(price * 100) / 100;
}
public static Component spriteComponent(ItemStack itemStack) {
Component component = Component.empty();
if (itemStack == null || itemStack.getType() == Material.AIR) return component;
component = MaterialSprites.get(itemStack.getType()).append(Component.space()).append(
itemStack.displayName().hoverEvent(itemStack.asHoverEvent()));
return component;
}
public static Component spriteComponent(Material material) {
return MaterialSprites.get(material).append(Component.space()).append(Component.text(material.key().value()));
}
}

File diff suppressed because it is too large Load Diff