diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a3fd9288..b5c2f85c 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,6 +1,6 @@ object Versions { - const val SPIGOT_API = "1.19.4-R0.1-SNAPSHOT" + const val PAPER_API = "1.20.4-R0.1-SNAPSHOT" const val OKAERI_CONFIGS = "5.0.5" const val LITE_COMMANDS = "3.10.5" diff --git a/buildSrc/src/main/kotlin/economy-repositories.gradle.kts b/buildSrc/src/main/kotlin/economy-repositories.gradle.kts index 5e501f5f..6ae90508 100644 --- a/buildSrc/src/main/kotlin/economy-repositories.gradle.kts +++ b/buildSrc/src/main/kotlin/economy-repositories.gradle.kts @@ -3,7 +3,7 @@ plugins { } repositories { - mavenCentral() + maven("https://maven-central.storage-download.googleapis.com/maven2") maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.panda-lang.org/releases/") @@ -13,4 +13,4 @@ repositories { maven("https://jitpack.io") maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") -} \ No newline at end of file +} diff --git a/eternaleconomy-api/build.gradle.kts b/eternaleconomy-api/build.gradle.kts index 73f32897..fe91090c 100644 --- a/eternaleconomy-api/build.gradle.kts +++ b/eternaleconomy-api/build.gradle.kts @@ -1,12 +1,11 @@ plugins { `economy-java` `economy-repositories` - // `economy-checkstyle` `economy-publish` } dependencies { - compileOnly("org.spigotmc:spigot-api:${Versions.SPIGOT_API}") + compileOnly("io.papermc.paper:paper-api:${Versions.PAPER_API}") api("org.jetbrains:annotations:${Versions.JETBRAINS_ANNOTATIONS}") } diff --git a/eternaleconomy-core/build.gradle.kts b/eternaleconomy-core/build.gradle.kts index 09f83224..275ce346 100644 --- a/eternaleconomy-core/build.gradle.kts +++ b/eternaleconomy-core/build.gradle.kts @@ -1,54 +1,61 @@ import net.minecrell.pluginyml.bukkit.BukkitPluginDescription +import net.minecrell.pluginyml.paper.PaperPluginDescription plugins { `economy-java` `economy-repositories` -// `economy-checkstyle` - id("net.minecrell.plugin-yml.bukkit") + id("net.minecrell.plugin-yml.paper") id("com.gradleup.shadow") id("xyz.jpenilla.run-paper") id("me.champeau.jmh") } + +repositories { + maven { + name = "papermc" + url = uri("https://repo.papermc.io/repository/maven-public/") + } +} + dependencies { // api module implementation(project(":eternaleconomy-api")) - // spigot-api - compileOnly("org.spigotmc:spigot-api:${Versions.SPIGOT_API}") + // paper-api + compileOnly("io.papermc.paper:paper-api:${Versions.PAPER_API}") // eternalcode commons - implementation("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}") - implementation("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}") - implementation("com.eternalcode:eternalcode-commons-shared:${Versions.ETERNALCODE_COMMONS}") - implementation("com.eternalcode:eternalcode-commons-folia:${Versions.ETERNALCODE_COMMONS}") - - bukkitLibrary("org.mariadb.jdbc:mariadb-java-client:${Versions.MARIA_DB}") - bukkitLibrary("org.postgresql:postgresql:${Versions.POSTGRESQL}") - bukkitLibrary("com.h2database:h2:${Versions.H2}") - bukkitLibrary("com.j256.ormlite:ormlite-core:${Versions.ORMLITE}") - bukkitLibrary("com.j256.ormlite:ormlite-jdbc:${Versions.ORMLITE}") - bukkitLibrary("com.zaxxer:HikariCP:${Versions.HIKARI_CP}") - - implementation("dev.rollczi:litecommands-bukkit:${Versions.LITE_COMMANDS}") - implementation("dev.rollczi:litecommands-adventure:${Versions.LITE_COMMANDS}") - implementation("dev.rollczi:litecommands-jakarta:${Versions.LITE_COMMANDS}") + paperLibrary("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}") + paperLibrary("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}") + paperLibrary("com.eternalcode:eternalcode-commons-shared:${Versions.ETERNALCODE_COMMONS}") + paperLibrary("com.eternalcode:eternalcode-commons-folia:${Versions.ETERNALCODE_COMMONS}") + + paperLibrary("org.mariadb.jdbc:mariadb-java-client:${Versions.MARIA_DB}") + paperLibrary("org.postgresql:postgresql:${Versions.POSTGRESQL}") + paperLibrary("com.h2database:h2:${Versions.H2}") + paperLibrary("com.j256.ormlite:ormlite-core:${Versions.ORMLITE}") + paperLibrary("com.j256.ormlite:ormlite-jdbc:${Versions.ORMLITE}") + paperLibrary("com.zaxxer:HikariCP:${Versions.HIKARI_CP}") + + paperLibrary("dev.rollczi:litecommands-bukkit:${Versions.LITE_COMMANDS}") + paperLibrary("dev.rollczi:litecommands-adventure:${Versions.LITE_COMMANDS}") + paperLibrary("dev.rollczi:litecommands-jakarta:${Versions.LITE_COMMANDS}") // multification - implementation("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}") - implementation("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}") - - // kyori - implementation("net.kyori:adventure-platform-bukkit:${Versions.ADVENTURE_PLATFORM_BUKKIT}") - implementation("net.kyori:adventure-text-minimessage:${Versions.ADVENTURE_API}") + paperLibrary("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}") + paperLibrary("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}") // vault compileOnly("com.github.MilkBowl:VaultAPI:${Versions.VAULT_API}") // okaeri configs - implementation("eu.okaeri:okaeri-configs-yaml-snakeyaml:${Versions.OKAERI_CONFIGS}") - implementation("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS}") + paperLibrary("eu.okaeri:okaeri-configs-yaml-snakeyaml:${Versions.OKAERI_CONFIGS}") + paperLibrary("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS}") + paperLibrary("eu.okaeri:okaeri-configs-serdes-bukkit:${Versions.OKAERI_CONFIGS}") + + paperLibrary("com.github.cryptomorin:XSeries:13.5.1") compileOnly("me.clip:placeholderapi:${Versions.PLACEHOLDER_API}") @@ -66,9 +73,10 @@ tasks.test { useJUnitPlatform() } -bukkit { +paper { main = "com.eternalcode.economy.EconomyBukkitPlugin" - apiVersion = "1.13" + loader = "com.eternalcode.economy.EconomyBukkitLoader" + apiVersion = "1.19" prefix = "EternalEconomy" author = "EternalCodeTeam" name = "EternalEconomy" @@ -79,14 +87,26 @@ bukkit { load = BukkitPluginDescription.PluginLoadOrder.STARTUP version = "${project.version}" - depend = listOf("Vault") - softDepend = listOf("PlaceholderAPI") + serverDependencies { + register("Vault") { + required = true + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + } + register("PlaceholderAPI") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + } + } foliaSupported = true + generateLibrariesJson = true } tasks.runServer { minecraftVersion("1.21.8") + downloadPlugins { + url("https://github.com/MilkBowl/Vault/releases/download/1.7.3/Vault.jar") + } } tasks.shadowJar { @@ -97,14 +117,8 @@ tasks.shadowJar { "org/jetbrains/annotations/**" ) - val prefix = "com.eternalcode.economy.libs" - listOf( - "dev.rollczi", - "eu.okaeri", - "panda", - "org.yaml", - "net.kyori", - "com.eternalcode.commons", - "net.jodah", - ).forEach { relocate(it, prefix) } +// val prefix = "com.eternalcode.economy.libs" +// listOf( +// +// ).forEach { relocate(it, prefix) } } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitLoader.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitLoader.java new file mode 100644 index 00000000..1249bd22 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitLoader.java @@ -0,0 +1,55 @@ +package com.eternalcode.economy; + +import com.google.gson.Gson; +import io.papermc.paper.plugin.loader.PluginClasspathBuilder; +import io.papermc.paper.plugin.loader.PluginLoader; +import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.repository.RemoteRepository; +import org.jetbrains.annotations.NotNull; + +public class EconomyBukkitLoader implements PluginLoader { + + public static final Gson GSON = new Gson(); + public static final String LIBRARIES_JSON_PATH = "/paper-libraries.json"; + + @Override + public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) { + MavenLibraryResolver resolver = new MavenLibraryResolver(); + + PluginLibraries pluginLibraries = this.load(); + + pluginLibraries.asDependencies().forEach(resolver::addDependency); + pluginLibraries.asRepositories().forEach(resolver::addRepository); + + classpathBuilder.addLibrary(resolver); + } + + public PluginLibraries load() { + try (InputStream in = getClass().getResourceAsStream(LIBRARIES_JSON_PATH)) { + return GSON.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), PluginLibraries.class); + } + catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + private record PluginLibraries(Map repositories, List dependencies) { + public Stream asDependencies() { + return this.dependencies.stream().map(d -> new Dependency(new DefaultArtifact(d), null)); + } + + public Stream asRepositories() { + return this.repositories.entrySet().stream() + .map(e -> new RemoteRepository.Builder(e.getKey(), "default", e.getValue()).build()); + } + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitPlugin.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitPlugin.java index 705ee90e..a2bd63d6 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitPlugin.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyBukkitPlugin.java @@ -11,34 +11,39 @@ import com.eternalcode.economy.account.database.AccountRepository; import com.eternalcode.economy.account.database.AccountRepositoryImpl; import com.eternalcode.economy.bridge.BridgeManager; -import com.eternalcode.economy.command.impl.admin.AdminAddCommand; -import com.eternalcode.economy.command.impl.admin.AdminBalanceCommand; -import com.eternalcode.economy.command.impl.admin.AdminRemoveCommand; -import com.eternalcode.economy.command.impl.admin.AdminResetCommand; -import com.eternalcode.economy.command.impl.admin.AdminSetCommand; import com.eternalcode.economy.command.argument.AccountArgument; import com.eternalcode.economy.command.context.AccountContext; import com.eternalcode.economy.command.cooldown.CommandCooldownEditor; import com.eternalcode.economy.command.cooldown.CommandCooldownMessage; import com.eternalcode.economy.command.handler.InvalidUsageHandlerImpl; import com.eternalcode.economy.command.handler.MissingPermissionHandlerImpl; -import com.eternalcode.economy.command.message.InvalidBigDecimalMessage; import com.eternalcode.economy.command.impl.MoneyBalanceCommand; import com.eternalcode.economy.command.impl.MoneyTransferCommand; -import com.eternalcode.economy.database.DatabaseManager; -import com.eternalcode.economy.leaderboard.LeaderboardCommand; +import com.eternalcode.economy.command.impl.admin.AdminAddCommand; +import com.eternalcode.economy.command.impl.admin.AdminBalanceCommand; +import com.eternalcode.economy.command.impl.admin.AdminRemoveCommand; +import com.eternalcode.economy.command.impl.admin.AdminResetCommand; +import com.eternalcode.economy.command.impl.admin.AdminSetCommand; +import com.eternalcode.economy.command.message.InvalidBigDecimalMessage; import com.eternalcode.economy.command.validator.notsender.NotSender; import com.eternalcode.economy.command.validator.notsender.NotSenderValidator; import com.eternalcode.economy.config.ConfigService; import com.eternalcode.economy.config.implementation.CommandsConfig; import com.eternalcode.economy.config.implementation.PluginConfig; import com.eternalcode.economy.config.implementation.messages.MessageConfig; +import com.eternalcode.economy.database.DatabaseManager; import com.eternalcode.economy.format.DecimalFormatter; import com.eternalcode.economy.format.DecimalFormatterImpl; +import com.eternalcode.economy.leaderboard.LeaderboardCommand; import com.eternalcode.economy.multification.NoticeBroadcastHandler; import com.eternalcode.economy.multification.NoticeHandler; import com.eternalcode.economy.multification.NoticeService; import com.eternalcode.economy.vault.VaultEconomyProvider; +import com.eternalcode.economy.withdraw.WithdrawCommand; +import com.eternalcode.economy.withdraw.WithdrawItemServiceImpl; +import com.eternalcode.economy.withdraw.WithdrawService; +import com.eternalcode.economy.withdraw.controller.WithdrawAnvilController; +import com.eternalcode.economy.withdraw.controller.WithdrawController; import com.eternalcode.multification.notice.Notice; import com.eternalcode.multification.notice.NoticeBroadcast; import com.google.common.base.Stopwatch; @@ -50,8 +55,6 @@ import java.io.File; import java.math.BigDecimal; import java.time.Duration; -import net.kyori.adventure.platform.AudienceProvider; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.minimessage.MiniMessage; import net.milkbowl.vault.economy.Economy; import org.bukkit.Server; @@ -64,7 +67,6 @@ public class EconomyBukkitPlugin extends JavaPlugin { private static final String PLUGIN_STARTED = "EternalEconomy has been enabled in %dms."; - private AudienceProvider audienceProvider; private DatabaseManager databaseManager; private LiteCommands liteCommands; @@ -74,7 +76,6 @@ public void onEnable() { Stopwatch started = Stopwatch.createStarted(); Server server = this.getServer(); - this.audienceProvider = BukkitAudiences.create(this); MiniMessage miniMessage = MiniMessage.builder() .postProcessor(new AdventureUrlPostProcessor()) .postProcessor(new AdventureLegacyColorPostProcessor()) @@ -86,9 +87,10 @@ public void onEnable() { ConfigService configService = new ConfigService(); MessageConfig messageConfig = configService.create(MessageConfig.class, new File(dataFolder, "messages.yml")); PluginConfig pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, "config.yml")); - CommandsConfig commandsConfig = configService.create(CommandsConfig.class, new File(dataFolder, "commands.yml")); + CommandsConfig commandsConfig = + configService.create(CommandsConfig.class, new File(dataFolder, "commands.yml")); - NoticeService noticeService = new NoticeService(messageConfig, this.audienceProvider, miniMessage); + NoticeService noticeService = new NoticeService(messageConfig, miniMessage); Scheduler scheduler = EconomySchedulerAdapter.getAdaptiveScheduler(this); @@ -101,13 +103,21 @@ public void onEnable() { DecimalFormatter decimalFormatter = new DecimalFormatterImpl(pluginConfig); AccountPaymentService accountPaymentService = new AccountPaymentService(accountManager, pluginConfig); + WithdrawItemServiceImpl + withdrawItemServiceImpl = new WithdrawItemServiceImpl(this, pluginConfig, decimalFormatter, + miniMessage); + WithdrawService withdrawService = new WithdrawService( + server, noticeService, decimalFormatter, + withdrawItemServiceImpl, accountPaymentService, accountManager); + VaultEconomyProvider vaultEconomyProvider = new VaultEconomyProvider(this, decimalFormatter, accountPaymentService, accountManager); server.getServicesManager().register(Economy.class, vaultEconomyProvider, this, ServicePriority.Highest); this.liteCommands = LiteBukkitFactory.builder("eternaleconomy", this, server) - .extension(new LiteJakartaExtension<>(), settings -> settings - .violationMessage(Positive.class, BigDecimal.class, new InvalidBigDecimalMessage<>(noticeService)) + .extension( + new LiteJakartaExtension<>(), settings -> settings + .violationMessage(Positive.class, BigDecimal.class, new InvalidBigDecimalMessage<>(noticeService)) ) .annotations(extension -> extension.validator( @@ -119,10 +129,11 @@ public void onEnable() { .invalidUsage(new InvalidUsageHandlerImpl(noticeService)) .message(LiteMessages.COMMAND_COOLDOWN, new CommandCooldownMessage(noticeService, commandsConfig)) - .message(LiteMessages.INVALID_NUMBER, (invocation, amount) -> noticeService.create() - .notice(messageConfig.positiveNumberRequired) - .placeholder("{AMOUNT}", amount) - .viewer(invocation.sender())) + .message( + LiteMessages.INVALID_NUMBER, (invocation, amount) -> noticeService.create() + .notice(messageConfig.positiveNumberRequired) + .placeholder("{AMOUNT}", amount) + .viewer(invocation.sender())) .editorGlobal(new CommandCooldownEditor(commandsConfig)) .commands( @@ -131,6 +142,7 @@ public void onEnable() { new AdminSetCommand(accountPaymentService, decimalFormatter, noticeService), new AdminResetCommand(accountPaymentService, noticeService), new AdminBalanceCommand(noticeService, decimalFormatter), + new WithdrawCommand(withdrawService, noticeService, decimalFormatter), new MoneyBalanceCommand(noticeService, decimalFormatter), new MoneyTransferCommand(accountPaymentService, decimalFormatter, noticeService, pluginConfig), new EconomyReloadCommand(configService, noticeService), @@ -147,8 +159,11 @@ public void onEnable() { server.getPluginManager().registerEvents(new AccountController(accountManager), this); + server.getPluginManager().registerEvents(new WithdrawController(withdrawService, withdrawItemServiceImpl), this); + server.getPluginManager().registerEvents(new WithdrawAnvilController(withdrawItemServiceImpl, noticeService), this); + BridgeManager bridgeManager = new BridgeManager( - this.getDescription(), + this.getPluginMeta(), accountManager, decimalFormatter, server, @@ -163,10 +178,6 @@ public void onEnable() { @Override public void onDisable() { - if (this.audienceProvider != null) { - this.audienceProvider.close(); - } - if (this.liteCommands != null) { this.liteCommands.unregister(); } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyPermissionConstant.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyPermissionConstant.java index f8e3b747..96330d2f 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyPermissionConstant.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/EconomyPermissionConstant.java @@ -8,9 +8,11 @@ public class EconomyPermissionConstant { public static final String ADMIN_REMOVE_PERMISSION = "eternaleconomy.admin.remove"; public static final String ADMIN_SET_PERMISSION = "eternaleconomy.admin.set"; public static final String ADMIN_RELOAD_PERMISSION = "eternaleconomy.admin.reload"; + public static final String ADMIN_ITEM_PERMISSION = "eternaleconomy.admin.item"; public static final String PLAYER_BALANCE_PERMISSION = "eternaleconomy.player.balance"; public static final String PLAYER_BALANCE_OTHER_PERMISSION = "eternaleconomy.player.balance.other"; public static final String PLAYER_PAY_PERMISSION = "eternaleconomy.player.pay"; public static final String PLAYER_BALANCE_TOP_PERMISSION = "eternaleconomy.player.balance.top"; + public static final String PLAYER_WITHDRAW_PERMISSION = "eternaleconomy.player.withdraw"; } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/BridgeManager.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/BridgeManager.java index b424583f..9e1d5f54 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/BridgeManager.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/BridgeManager.java @@ -3,6 +3,7 @@ import com.eternalcode.economy.account.AccountManager; import com.eternalcode.economy.bridge.placeholderapi.PlaceholderEconomyExpansion; import com.eternalcode.economy.format.DecimalFormatter; +import io.papermc.paper.plugin.configuration.PluginMeta; import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.Server; @@ -12,7 +13,7 @@ public class BridgeManager { - private final PluginDescriptionFile pluginDescriptionFile; + private final PluginMeta pluginMeta; private final AccountManager accountManager; private final DecimalFormatter decimalFormatter; @@ -22,14 +23,14 @@ public class BridgeManager { private final Logger logger; public BridgeManager( - PluginDescriptionFile pluginDescriptionFile, + PluginMeta pluginMeta, AccountManager accountManager, DecimalFormatter decimalFormatter, Server server, Plugin plugin, Logger logger ) { - this.pluginDescriptionFile = pluginDescriptionFile; + this.pluginMeta = pluginMeta; this.accountManager = accountManager; this.decimalFormatter = decimalFormatter; this.server = server; @@ -44,7 +45,7 @@ public void init() { Bukkit.getScheduler().runTask(this.plugin, () -> { this.setupBridge("PlaceholderAPI", () -> { PlaceholderEconomyExpansion placeholderEconomyExpansion = new PlaceholderEconomyExpansion( - this.pluginDescriptionFile, + this.pluginMeta, this.accountManager, this.decimalFormatter ); diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/placeholderapi/PlaceholderEconomyExpansion.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/placeholderapi/PlaceholderEconomyExpansion.java index 31eac73e..b74e4817 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/placeholderapi/PlaceholderEconomyExpansion.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/bridge/placeholderapi/PlaceholderEconomyExpansion.java @@ -4,42 +4,42 @@ import com.eternalcode.economy.account.AccountManager; import com.eternalcode.economy.bridge.BridgeInitializer; import com.eternalcode.economy.format.DecimalFormatter; +import io.papermc.paper.plugin.configuration.PluginMeta; import java.util.UUID; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; -import org.bukkit.plugin.PluginDescriptionFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class PlaceholderEconomyExpansion extends PlaceholderExpansion implements BridgeInitializer { - private final PluginDescriptionFile pluginDescriptionFile; + private final PluginMeta pluginMeta; private final AccountManager accountManager; private final DecimalFormatter decimalFormatter; public PlaceholderEconomyExpansion( - PluginDescriptionFile pluginDescriptionFile, + PluginMeta pluginMeta, AccountManager accountManager, DecimalFormatter decimalFormatter ) { - this.pluginDescriptionFile = pluginDescriptionFile; + this.pluginMeta = pluginMeta; this.accountManager = accountManager; this.decimalFormatter = decimalFormatter; } @Override public @NotNull String getIdentifier() { - return this.pluginDescriptionFile.getName().toLowerCase(); + return this.pluginMeta.getName().toLowerCase(); } @Override public @NotNull String getAuthor() { - return this.pluginDescriptionFile.getAuthors().get(0); + return this.pluginMeta.getAuthors().get(0); } @Override public @NotNull String getVersion() { - return this.pluginDescriptionFile.getVersion(); + return this.pluginMeta.getVersion(); } @Override diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/cooldown/CommandCooldownMessage.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/cooldown/CommandCooldownMessage.java index 9dcd914c..6a55f010 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/cooldown/CommandCooldownMessage.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/cooldown/CommandCooldownMessage.java @@ -23,7 +23,7 @@ public CommandCooldownMessage(NoticeService noticeService, CommandsConfig comman @Override public Object get(Invocation invocation, CooldownState cooldownState) { - CommandCooldownConfig cooldown = commandsConfig.cooldowns.get(cooldownState.getCooldownContext().getKey()); + CommandCooldownConfig cooldown = commandsConfig.cooldowns.get(cooldownState.getKey()); if (cooldown == null) { return LiteMessages.COMMAND_COOLDOWN.getDefaultMessage(cooldownState); diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/MoneyTransferCommand.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/MoneyTransferCommand.java index a1175b83..989fb2d6 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/MoneyTransferCommand.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/MoneyTransferCommand.java @@ -8,6 +8,7 @@ import com.eternalcode.economy.format.DecimalFormatter; import com.eternalcode.economy.multification.NoticeService; import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.argument.Key; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminRemoveCommand.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminRemoveCommand.java index bfb314c1..e849afac 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminRemoveCommand.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminRemoveCommand.java @@ -6,6 +6,7 @@ import com.eternalcode.economy.format.DecimalFormatter; import com.eternalcode.economy.multification.NoticeService; import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.argument.Key; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminSetCommand.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminSetCommand.java index bd85ab91..588e3746 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminSetCommand.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/command/impl/admin/AdminSetCommand.java @@ -6,6 +6,7 @@ import com.eternalcode.economy.format.DecimalFormatter; import com.eternalcode.economy.multification.NoticeService; import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.argument.Key; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/ConfigService.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/ConfigService.java index 640df63c..3efd7f7e 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/ConfigService.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/ConfigService.java @@ -7,6 +7,7 @@ import eu.okaeri.configs.ConfigManager; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.serdes.commons.SerdesCommons; +import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit; import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer; import java.io.File; import java.util.HashSet; @@ -30,7 +31,7 @@ public T create(Class config, File file) { NoticeResolverRegistry noticeRegistry = NoticeResolverDefaults.createRegistry() .registerResolver(new SoundBukkitResolver()); configFile - .withConfigurer(yamlConfigurer, new SerdesCommons()) + .withConfigurer(yamlConfigurer, new SerdesCommons(), new SerdesBukkit()) .withConfigurer(yamlConfigurer, new MultificationSerdesPack(noticeRegistry)) .withBindFile(file) .withRemoveOrphans(true) diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/PluginConfig.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/PluginConfig.java index 18afcf4c..0bb435bb 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/PluginConfig.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/PluginConfig.java @@ -1,9 +1,12 @@ package com.eternalcode.economy.config.implementation; +import com.eternalcode.economy.config.item.ConfigItem; import com.eternalcode.economy.database.DatabaseConfig; import com.eternalcode.economy.format.DecimalUnit; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; +import org.bukkit.Material; + import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @@ -29,6 +32,9 @@ public class PluginConfig extends OkaeriConfig { @Comment("Should leaderboard command show player's position in the leaderboard") public boolean showLeaderboardPosition = true; + @Comment("Currency item settings") + public WithdrawItem withdraw = new WithdrawItem(); + public static class Units extends OkaeriConfig { public List format = Arrays.asList( @@ -40,4 +46,14 @@ public static class Units extends OkaeriConfig { new DecimalUnit(1_000_000_000_000_000_000L, 'e') ); } + + public static class WithdrawItem extends OkaeriConfig { + public ConfigItem item = ConfigItem.builder() + .withName("Check worth {VALUE}$") + .withLore(List.of("Right click to redeem")) + .withMaterial(Material.PAPER) + .withTexture(0) + .withGlow(true) + .build(); + } } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageAdminSubSection.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageAdminSubSection.java index f7c581dd..7805414d 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageAdminSubSection.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageAdminSubSection.java @@ -8,23 +8,13 @@ public class MessageAdminSubSection extends OkaeriConfig { public Notice insufficientFunds = - Notice.chat("ECONOMY " - + "Player {PLAYER} has insufficient funds, they are missing {MISSING_BALANCE}."); + Notice.chat("ECONOMY Player {PLAYER} has insufficient funds, they are missing {MISSING_BALANCE}."); - public Notice added = Notice.chat("ECONOMY " - + "Added {AMOUNT} to " - + "{PLAYER}."); - public Notice removed = Notice.chat("ECONOMY " - + " Removed {AMOUNT} from " - + "{PLAYER}."); - public Notice set = Notice.chat("ECONOMY " - + "Set {PLAYER}'s balance to " - + "{AMOUNT}."); - public Notice reset = Notice.chat("ECONOMY " - + "Reset {PLAYER}'s balance."); - public Notice balance = Notice.chat("ECONOMY " - + " {PLAYER}'s balance is " - + "{BALANCE}."); + public Notice added = Notice.chat("ECONOMY Added {AMOUNT} to {PLAYER}."); + public Notice removed = Notice.chat("ECONOMY Removed {AMOUNT} from {PLAYER}."); + public Notice set = Notice.chat("ECONOMY Set {PLAYER}'s balance to {AMOUNT}."); + public Notice reset = Notice.chat("ECONOMY Reset {PLAYER}'s balance."); + public Notice balance = Notice.chat("ECONOMY {PLAYER}'s balance is {BALANCE}."); } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageConfig.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageConfig.java index b44fdd85..0d1ce57c 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageConfig.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessageConfig.java @@ -1,5 +1,6 @@ package com.eternalcode.economy.config.implementation.messages; +import com.eternalcode.economy.withdraw.WithdrawMessageConfig; import com.eternalcode.multification.notice.Notice; import eu.okaeri.configs.OkaeriConfig; @@ -21,6 +22,11 @@ public class MessageConfig extends OkaeriConfig { public Notice missingPermission = Notice.chat( "ECONOMY Missing permission: {PERMISSION}."); + public Notice invalidMoney = + Notice.chat("ECONOMY Invalid money value format! Use: 1000, 1k, 1.5k, 1m, etc."); + public Notice incorrectMoneyArgument = Notice.chat("ECONOMY Price must be greater than or equal 0.1!"); + public MessageAdminSubSection admin = new MessageAdminSubSection(); public MessagesPlayerSubSection player = new MessagesPlayerSubSection(); + public WithdrawMessageConfig withdraw = new WithdrawMessageConfig(); } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessagesPlayerSubSection.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessagesPlayerSubSection.java index 42654ec9..9d3f293a 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessagesPlayerSubSection.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/implementation/messages/MessagesPlayerSubSection.java @@ -6,37 +6,23 @@ public class MessagesPlayerSubSection extends OkaeriConfig { - public Notice added = Notice.chat("ECONOMY " - + "Added {AMOUNT} to your account."); - public Notice removed = Notice.chat("ECONOMY " - + " Removed {AMOUNT} from your account."); - public Notice set = Notice.chat("ECONOMY " - + "Set your balance to {AMOUNT}."); - public Notice reset = Notice.chat("ECONOMY " - + "Resetted your balance."); - public Notice balance = Notice.chat("ECONOMY " - + " Your balance is {BALANCE}."); + public Notice added = Notice.chat("ECONOMY Added {AMOUNT} to your account."); + public Notice removed = Notice.chat("ECONOMY Removed {AMOUNT} from your account."); + public Notice set = Notice.chat("ECONOMY Set your balance to {AMOUNT}."); + public Notice reset = Notice.chat("ECONOMY Resetted your balance."); + public Notice balance = Notice.chat("ECONOMY Your balance is {BALANCE}."); public Notice balanceOther = - Notice.chat("ECONOMY " - + " {PLAYER}'s balance is {BALANCE}."); - public Notice insufficientBalance = Notice.chat("ECONOMY " - + " Insufficient funds," - + " you are missing {MISSING_BALANCE}."); - public Notice transferSuccess = Notice.chat("ECONOMY Successfully transferred {AMOUNT} to " - + "{PLAYER}."); - public Notice transferReceived = Notice.chat("ECONOMY " - + " Received {AMOUNT} from " - + "{PLAYER}."); - public Notice transferLimit = Notice.chat("ECONOMY " - + " Transaction limit is {LIMIT}."); + Notice.chat("ECONOMY {PLAYER}'s balance is {BALANCE}."); + public Notice insufficientBalance = Notice.chat("ECONOMY Insufficient funds, you are missing {MISSING_BALANCE}."); + public Notice transferSuccess = Notice.chat("ECONOMY Successfully transferred {AMOUNT} to {PLAYER}."); + public Notice transferReceived = Notice.chat("ECONOMY Received {AMOUNT} from {PLAYER}."); + public Notice transferLimit = Notice.chat("ECONOMY Transaction limit is {LIMIT}."); @Comment({ "Use {PAGE} placeholder to show the current page number", "Use {TOTAL_PAGES} placeholder to show the total number of pages" }) - public Notice leaderboardHeader = Notice.chat(" Balance leaderboard (Page " - + "{PAGE}/{TOTAL_PAGES}): "); + public Notice leaderboardHeader = Notice.chat(" Balance leaderboard (Page {PAGE}/{TOTAL_PAGES}): "); @Comment({ "Leaderboard entry notice, only displayed if showLeaderboardPosition is set to true in the config.yml", @@ -45,8 +31,7 @@ public class MessagesPlayerSubSection extends OkaeriConfig { "Use {BALANCE} placeholder to show the player's formatted balance", "Use {BALANCE_RAW} placeholder to show the player's raw balance" }) - public Notice leaderboardEntry = Notice.chat(" #{POSITION} {PLAYER} -" - + " {BALANCE}"); + public Notice leaderboardEntry = Notice.chat(" #{POSITION} {PLAYER} - {BALANCE}"); @Comment({ "Leaderboard position notice, only displayed if showLeaderboardPosition is set to true in the config.yml", @@ -65,6 +50,5 @@ public class MessagesPlayerSubSection extends OkaeriConfig { .build(); @Comment("Leaderboard is empty notice") - public Notice leaderboardEmpty = Notice.chat("ECONOMY " - + " Leaderboard is empty :("); + public Notice leaderboardEmpty = Notice.chat("ECONOMY Leaderboard is empty :("); } diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/item/ConfigItem.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/item/ConfigItem.java new file mode 100644 index 00000000..b79e4d49 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/config/item/ConfigItem.java @@ -0,0 +1,94 @@ +package com.eternalcode.economy.config.item; + +import eu.okaeri.configs.OkaeriConfig; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import org.bukkit.Material; + +public class ConfigItem extends OkaeriConfig { + + private String name; + private List lore; + private Material material; + private Integer texture; + private boolean glow; + + public ConfigItem(String name, List lore, Material material, Integer texture, boolean glow) { + this.name = name; + this.lore = lore != null ? new ArrayList<>(lore) : new ArrayList<>(); + this.material = material; + this.texture = texture; + this.glow = glow; + } + + public ConfigItem() { + this.name = "&6Item"; + this.lore = new ArrayList<>(Collections.singletonList("&7This is an item")); + this.material = Material.PLAYER_HEAD; + this.texture = null; + this.glow = false; + } + + public static Builder builder() { + return new Builder(); + } + + public String name() { + return this.name; + } + + public List lore() { + return Collections.unmodifiableList(this.lore); + } + + public Material material() { + return this.material; + } + + public Integer texture() { + return this.texture; + } + + public boolean glow() { + return this.glow; + } + + public static class Builder { + private String name = "&6Item"; + private List lore = new ArrayList<>(Collections.singletonList("&7This is an item")); + private Material material = Material.PLAYER_HEAD; + private Integer texture = null; + private boolean glow = false; + + public Builder withName(String name) { + this.name = name; + return this; + } + + public Builder withLore(List lore) { + this.lore = lore != null ? new ArrayList<>(lore) : new ArrayList<>(); + return this; + } + + public Builder withMaterial(Material material) { + this.material = material; + return this; + } + + public Builder withTexture(Integer texture) { + this.texture = texture; + return this; + } + + public Builder withGlow(boolean glow) { + this.glow = glow; + return this; + } + + public ConfigItem build() { + return new ConfigItem(this.name, this.lore, this.material, this.texture, this.glow); + } + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/multification/NoticeService.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/multification/NoticeService.java index 8d62a952..b9a855d5 100644 --- a/eternaleconomy-core/src/main/java/com/eternalcode/economy/multification/NoticeService.java +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/multification/NoticeService.java @@ -4,23 +4,20 @@ import com.eternalcode.multification.adventure.AudienceConverter; import com.eternalcode.multification.bukkit.BukkitMultification; import com.eternalcode.multification.translation.TranslationProvider; -import net.kyori.adventure.platform.AudienceProvider; +import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; public class NoticeService extends BukkitMultification { private final MessageConfig messageConfig; - private final AudienceProvider audienceProvider; private final MiniMessage miniMessage; - public NoticeService(MessageConfig messageConfig, AudienceProvider audienceProvider, MiniMessage miniMessage) { + public NoticeService(MessageConfig messageConfig, MiniMessage miniMessage) { this.messageConfig = messageConfig; - this.audienceProvider = audienceProvider; this.miniMessage = miniMessage; } @@ -36,12 +33,6 @@ public NoticeService(MessageConfig messageConfig, AudienceProvider audienceProvi @Override protected @NotNull AudienceConverter audienceConverter() { - return commandSender -> { - if (commandSender instanceof Player player) { - return this.audienceProvider.player(player.getUniqueId()); - } - - return this.audienceProvider.console(); - }; + return Audience::audience; } -} \ No newline at end of file +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawCommand.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawCommand.java new file mode 100644 index 00000000..2ba646c3 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawCommand.java @@ -0,0 +1,51 @@ +package com.eternalcode.economy.withdraw; + +import com.eternalcode.economy.EconomyPermissionConstant; +import com.eternalcode.economy.account.Account; +import com.eternalcode.economy.format.DecimalFormatter; +import com.eternalcode.economy.multification.NoticeService; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.argument.Key; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.permission.Permission; +import jakarta.validation.constraints.Positive; +import java.math.BigDecimal; + +@Command(name = "withdraw", aliases = {"paycheck", "check"}) +@Permission(EconomyPermissionConstant.PLAYER_WITHDRAW_PERMISSION) +public class WithdrawCommand { + + private final WithdrawService withdrawService; + private final NoticeService noticeService; + private final DecimalFormatter decimalFormatter; + + public WithdrawCommand( + WithdrawService withdrawService, + NoticeService noticeService, + DecimalFormatter decimalFormatter + ) { + this.withdrawService = withdrawService; + this.noticeService = noticeService; + this.decimalFormatter = decimalFormatter; + } + + @Execute + void execute(@Context Account account, @Arg("amount") @Positive BigDecimal value) { + BigDecimal balance = account.balance(); + BigDecimal subtract = balance.subtract(value); + + if (subtract.compareTo(BigDecimal.ZERO) < 0) { + this.noticeService.create() + .notice(messageConfig -> messageConfig.player.insufficientBalance) + .placeholder("{MISSING_BALANCE}", this.decimalFormatter.format(subtract.abs())) + .player(account.uuid()) + .send(); + + return; + } + + this.withdrawService.addBanknote(account.uuid(), value); + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemService.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemService.java new file mode 100644 index 00000000..8d64cee8 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemService.java @@ -0,0 +1,14 @@ +package com.eternalcode.economy.withdraw; + +import java.math.BigDecimal; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public interface WithdrawItemService { + + BigDecimal getValue(ItemStack itemStack); + + boolean isBanknote(ItemStack itemStack); + + ItemStack createBanknote(BigDecimal value); +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemServiceImpl.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemServiceImpl.java new file mode 100644 index 00000000..94450437 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawItemServiceImpl.java @@ -0,0 +1,141 @@ +package com.eternalcode.economy.withdraw; + +import com.cryptomorin.xseries.XEnchantment; +import com.eternalcode.economy.config.implementation.PluginConfig; +import com.eternalcode.economy.config.item.ConfigItem; +import com.eternalcode.economy.format.DecimalFormatter; +import java.math.BigDecimal; +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.NamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.Plugin; + +public class WithdrawItemServiceImpl implements WithdrawItemService { + + private static final String VALUE_PLACEHOLDER = "{VALUE}"; + private static final String WITHDRAW_VALUE_KEY = "withdraw_value"; + + private final PluginConfig pluginConfig; + private final DecimalFormatter moneyFormatter; + private final MiniMessage miniMessage; + private final NamespacedKey banknoteValueKey; + + public WithdrawItemServiceImpl( + Plugin plugin, + PluginConfig pluginConfig, + DecimalFormatter moneyFormatter, + MiniMessage miniMessage + ) { + this.banknoteValueKey = new NamespacedKey(plugin, WITHDRAW_VALUE_KEY); + this.pluginConfig = pluginConfig; + this.moneyFormatter = moneyFormatter; + this.miniMessage = miniMessage; + } + + @Override + public ItemStack createBanknote(BigDecimal value) { + if (value.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Banknote value must be positive, got: " + value); + } + + ItemStack banknoteItem = this.createBaseBanknoteItem(value); + return this.attachBanknoteValue(banknoteItem, value); + } + + @Override + public boolean isBanknote(ItemStack itemStack) { + if (itemStack == null) { + return false; + } + + if (!itemStack.hasItemMeta()) { + return false; + } + + ItemMeta itemMeta = itemStack.getItemMeta(); + return itemMeta.getPersistentDataContainer().has(this.banknoteValueKey, PersistentDataType.STRING); + } + + @Override + public BigDecimal getValue(ItemStack itemStack) { + if (itemStack == null) { + return BigDecimal.ZERO; + } + + if (!itemStack.hasItemMeta()) { + return BigDecimal.ZERO; + } + + ItemMeta itemMeta = itemStack.getItemMeta(); + String storedValue = itemMeta.getPersistentDataContainer() + .get(this.banknoteValueKey, PersistentDataType.STRING); + + if (storedValue == null) { + return BigDecimal.ZERO; + } + + try { + return new BigDecimal(storedValue); + } + catch (NumberFormatException exception) { + return BigDecimal.ZERO; + } + } + + private ItemStack createBaseBanknoteItem(BigDecimal value) { + ConfigItem configItem = this.pluginConfig.withdraw.item; + String formattedValue = this.moneyFormatter.format(value); + + ItemStack banknoteItem = new ItemStack(configItem.material()); + + banknoteItem.editMeta(meta -> { + if (configItem.texture() != null) { + meta.setCustomModelData(configItem.texture()); + } + + Component displayName = this.miniMessage.deserialize( + configItem.name().replace(VALUE_PLACEHOLDER, formattedValue), + TagResolver.empty() + ).decoration(TextDecoration.ITALIC, false); + meta.displayName(displayName); + + List loreComponents = configItem.lore().stream() + .map(line -> this.miniMessage.deserialize( + line.replace(VALUE_PLACEHOLDER, formattedValue), + TagResolver.empty() + ).decoration(TextDecoration.ITALIC, false)) + .toList(); + meta.lore(loreComponents); + }); + + if (configItem.glow()) { + banknoteItem.editMeta(meta -> { + Enchantment enchantment = XEnchantment.UNBREAKING.get(); // for version compatibility + meta.addEnchant(enchantment, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + }); + } + + return banknoteItem; + } + + private ItemStack attachBanknoteValue(ItemStack banknoteItem, BigDecimal value) { + ItemStack taggedItem = banknoteItem.clone(); + taggedItem.editMeta(meta -> + meta.getPersistentDataContainer().set( + this.banknoteValueKey, + PersistentDataType.STRING, + value.toPlainString() + ) + ); + return taggedItem; + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawMessageConfig.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawMessageConfig.java new file mode 100644 index 00000000..11f6adef --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawMessageConfig.java @@ -0,0 +1,34 @@ +package com.eternalcode.economy.withdraw; + +import com.eternalcode.multification.notice.Notice; +import eu.okaeri.configs.OkaeriConfig; + +public class WithdrawMessageConfig extends OkaeriConfig { + public Notice noItemInHand = Notice.chat( + "ECONOMY You must hold an item to create a banknote!" + ); + + public Notice noBanknoteInHand = Notice.chat( + "ECONOMY You must hold a banknote in your hand to redeem it!" + ); + + public Notice itemSetSuccess = Notice.chat( + "ECONOMY You have set the banknote item to {ITEM}!" + ); + + public Notice banknoteWithdrawn = Notice.chat( + "ECONOMY You have withdrawn a banknote worth {VALUE}!" + ); + + public Notice banknoteRedeemed = Notice.chat( + "ECONOMY You have redeemed your banknote worth {VALUE}!" + ); + + public Notice invalidInteraction = Notice.chat( + "ECONOMY You cannot use the banknote item in an anvil or crafting table!" + ); + + public Notice noInventorySpace = Notice.chat( + "ECONOMY You do not have enough space in your inventory to withdraw a banknote!" + ); +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawService.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawService.java new file mode 100644 index 00000000..a3c00cd1 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/WithdrawService.java @@ -0,0 +1,110 @@ +package com.eternalcode.economy.withdraw; + +import com.eternalcode.economy.account.Account; +import com.eternalcode.economy.account.AccountManager; +import com.eternalcode.economy.account.AccountPaymentService; +import com.eternalcode.economy.format.DecimalFormatter; +import com.eternalcode.economy.multification.NoticeService; +import java.math.BigDecimal; +import java.util.Objects; +import java.util.UUID; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class WithdrawService { + + private static final int MINIMUM_AMOUNT = 1; + + private final Server server; + private final NoticeService noticeService; + private final WithdrawItemService withdrawItemService; + private final DecimalFormatter decimalFormatter; + private final AccountPaymentService accountPaymentService; + private final AccountManager accountManager; + + public WithdrawService( + Server server, + NoticeService noticeService, + DecimalFormatter decimalFormatter, + WithdrawItemService withdrawItemService, + AccountPaymentService accountPaymentService, + AccountManager accountManager + ) { + this.server = server; + this.noticeService = noticeService; + this.decimalFormatter = decimalFormatter; + this.withdrawItemService = withdrawItemService; + this.accountPaymentService = accountPaymentService; + this.accountManager = accountManager; + } + + public void addBanknote(UUID uuid, BigDecimal value) { + if (value.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Banknote value must be positive, got: " + value); + } + + Player player = this.server.getPlayer(uuid); + if (player == null) { + return; + } + + if (player.getInventory().firstEmpty() == -1) { + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.noInventorySpace) + .player(player.getUniqueId()) + .send(); + return; + } + + ItemStack banknote = this.withdrawItemService.createBanknote(value); + player.getInventory().addItem(banknote); + + Account account = this.accountManager.getAccount(player.getUniqueId()); + this.accountPaymentService.removeBalance(account, value); + + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.banknoteWithdrawn) + .placeholder("{VALUE}", this.decimalFormatter.format(value)) + .player(player.getUniqueId()) + .send(); + } + + public void redeem(Player player, ItemStack item, BigDecimal value, int amount) { + if (amount < MINIMUM_AMOUNT) { + throw new IllegalArgumentException("Amount must be at least " + MINIMUM_AMOUNT + ", got: " + amount); + } + + if (value.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Banknote value must be positive, got: " + value); + } + + if (item.getType() == Material.AIR) { + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.noBanknoteInHand) + .player(player.getUniqueId()) + .send(); + return; + } + + if (item.getAmount() < amount) { + throw new IllegalArgumentException( + "Cannot redeem " + amount + " items when only " + item.getAmount() + " are available" + ); + } + + item.setAmount(item.getAmount() - amount); + + BigDecimal totalValue = value.multiply(BigDecimal.valueOf(amount)); + + Account account = this.accountManager.getAccount(player.getUniqueId()); + this.accountPaymentService.addBalance(account, totalValue); + + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.banknoteRedeemed) + .placeholder("{VALUE}", this.decimalFormatter.format(totalValue)) + .player(player.getUniqueId()) + .send(); + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawAnvilController.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawAnvilController.java new file mode 100644 index 00000000..90bb4663 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawAnvilController.java @@ -0,0 +1,94 @@ +package com.eternalcode.economy.withdraw.controller; + +import com.eternalcode.economy.multification.NoticeService; +import com.eternalcode.economy.withdraw.WithdrawItemService; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class WithdrawAnvilController implements Listener { + + private final WithdrawItemService withdrawItemService; + private final NoticeService noticeService; + + public WithdrawAnvilController(WithdrawItemService withdrawItemService, NoticeService noticeService) { + this.withdrawItemService = withdrawItemService; + this.noticeService = noticeService; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onAnvilClick(InventoryClickEvent event) { + Inventory topInventory = event.getInventory(); + + if (this.isRestrictedInventory(topInventory)) { + return; + } + + ItemStack clickedItem = event.getCurrentItem(); + ItemStack cursorItem = event.getCursor(); + + if (this.isBanknoteInteraction(clickedItem, cursorItem, event.getAction())) { + event.setCancelled(true); + event.getView().close(); + + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.invalidInteraction) + .viewer(event.getWhoClicked()) + .send(); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onAnvilDrag(InventoryDragEvent event) { + Inventory inventory = event.getInventory(); + + if (this.isRestrictedInventory(inventory)) { + return; + } + + ItemStack draggedItem = event.getOldCursor(); + if (draggedItem == null) { + return; + } + + boolean isDraggingToTopInventory = event.getRawSlots().stream() + .anyMatch(slot -> slot < inventory.getSize()); + + if (isDraggingToTopInventory && this.withdrawItemService.isBanknote(draggedItem)) { + event.setCancelled(true); + event.getView().close(); + + if (event.getWhoClicked() instanceof Player) { + this.noticeService.create() + .notice(messageConfig -> messageConfig.withdraw.invalidInteraction) + .viewer(event.getWhoClicked()) + .send(); + } + } + } + + private boolean isRestrictedInventory(Inventory inventory) { + InventoryType type = inventory.getType(); + return type != InventoryType.ANVIL && type != InventoryType.CRAFTING && type != InventoryType.WORKBENCH; + } + + private boolean isBanknoteInteraction(ItemStack clickedItem, ItemStack cursorItem, InventoryAction action) { + if (action == InventoryAction.MOVE_TO_OTHER_INVENTORY && this.withdrawItemService.isBanknote(clickedItem)) { + return true; + } + + if (action == InventoryAction.PLACE_ALL || action == InventoryAction.PLACE_ONE + || action == InventoryAction.PLACE_SOME) { + return this.withdrawItemService.isBanknote(cursorItem); + } + + return false; + } +} diff --git a/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawController.java b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawController.java new file mode 100644 index 00000000..ee8a54e7 --- /dev/null +++ b/eternaleconomy-core/src/main/java/com/eternalcode/economy/withdraw/controller/WithdrawController.java @@ -0,0 +1,57 @@ +package com.eternalcode.economy.withdraw.controller; + +import com.eternalcode.economy.withdraw.WithdrawItemService; +import com.eternalcode.economy.withdraw.WithdrawService; +import java.math.BigDecimal; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +public class WithdrawController implements Listener { + + private final WithdrawService withdrawService; + private final WithdrawItemService withdrawItemService; + + public WithdrawController(WithdrawService withdrawService, WithdrawItemService withdrawItemService) { + this.withdrawService = withdrawService; + this.withdrawItemService = withdrawItemService; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false) + public void onItemUse(PlayerInteractEvent event) { + Action action = event.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; + } + + Player player = event.getPlayer(); + EquipmentSlot hand = event.getHand(); + if (hand == null) { + return; + } + + ItemStack item = hand == EquipmentSlot.HAND + ? player.getInventory().getItemInMainHand() + : player.getInventory().getItemInOffHand(); + + if (item == null || item.getType() == Material.AIR) { + return; + } + + BigDecimal value = this.withdrawItemService.getValue(item); + if (value == null || value.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + + event.setCancelled(true); + + int itemAmount = player.isSneaking() ? item.getAmount() : 1; + this.withdrawService.redeem(player, item, value, itemAmount); + } +}