diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..4288da4 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,94 @@ +plugins { + java + id("java-library") + id("maven-publish") + id("idea") +} + +repositories { + mavenLocal() + maven { + url = uri("https://repo.papermc.io/repository/maven-public/") + } + + maven { + url = uri("https://jitpack.io") + } + + maven { + url = uri("https://ci.ender.zone/plugin/repository/everything/") + } + + maven { + url = uri("https://repo.codemc.io/repository/maven-public/") + } + + maven { + url = uri("https://repo.maven.apache.org/maven2/") + } +} + +dependencies { + compileOnly("de.tr7zw:item-nbt-api-plugin:2.15.1") + compileOnly("dev.jorel:commandapi-bukkit-core:10.0.1") + compileOnly(libs.io.papermc.paper.paper.api) + compileOnly(libs.com.github.milkbowl.vaultapi) + compileOnly(libs.net.ess3.essentialsx) + compileOnly(libs.com.github.brcdev.minecraft.shopgui.api) +} + +group = "com.shmkane" +version = "2.0.0" +description = "sellstick" + +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 + +tasks.withType() { + options.encoding = "UTF-8" +} +tasks.withType() { + options.encoding = "UTF-8" +} + +tasks.processResources { + val props = mapOf("version" to project.version) + inputs.properties(props) + filteringCharset = "UTF-8" + filesMatching("plugin.yml") { + expand(props) + } +} + +tasks.register("copyJarToServer") { + dependsOn(tasks.build) + from(layout.buildDirectory.file("libs/${rootProject.name}-${version}.jar")) + into("/data/BudgieNet/PAPER_1_21_4/plugins/") +} + +tasks.register("restartPaper") { + dependsOn(tasks.named("copyJarToServer")) + commandLine("bash", "-c", "/data/BudgieNet/PAPER_1_21_4/reload_sellstick.sh") + isIgnoreExitValue = true +} + +tasks.named("build") { + finalizedBy("copyJarToServer") + finalizedBy("restartPaper") +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +publishing { + publications.create("maven") { + from(components["java"]) + } +} + +configurations.configureEach { + resolutionStrategy.capabilitiesResolution.withCapability("com.destroystokyo.paper:paper-mojangapi") { + select("net.budgie.papercrane:papercrane-api:1.21.4-R0.1-SNAPSHOT") + } +} \ No newline at end of file diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml deleted file mode 100644 index ec14da6..0000000 --- a/dependency-reduced-pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - 4.0.0 - com.shmkane - sellstick - 1.1.1 - - - - true - src/main/resources - - - - - maven-jar-plugin - 2.3.1 - - /data/BudgieNet/PAPER_21/plugins - - - - maven-compiler-plugin - - 8 - 8 - - - - maven-shade-plugin - 3.5.0 - - - shade - package - - shade - - - - - - - de.tr7zw.changeme.nbtapi - com.shmkane.sellstick.nbtapi - - - - - - - - - papermc - https://repo.papermc.io/repository/maven-public/ - - - jitpack.io - https://jitpack.io - - - ess-repo - https://ci.ender.zone/plugin/repository/everything/ - - - codemc-repo - https://repo.codemc.io/repository/maven-public/ - - - - - io.papermc.paper - paper-api - 1.21.5-R0.1-SNAPSHOT - provided - - - com.github.MilkBowl - VaultAPI - 1.7 - provided - - - net.ess3 - EssentialsX - 2.16.1 - provided - - - com.github.brcdev-minecraft - shopgui-api - 3.0.0 - provided - - - diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..377538c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.configuration-cache=true + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..3481636 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,18 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +com-github-brcdev-minecraft-shopgui-api = "3.0.0" +com-github-milkbowl-vaultapi = "1.7" +de-tr7zw-item-nbt-api = "2.15.0" +dev-jorel-commandapi-bukkit-core = "10.1.0" +io-papermc-paper-paper-api = "1.21.5-R0.1-SNAPSHOT" +net-ess3-essentialsx = "2.16.1" + +[libraries] +com-github-brcdev-minecraft-shopgui-api = { module = "com.github.brcdev-minecraft:shopgui-api", version.ref = "com-github-brcdev-minecraft-shopgui-api" } +com-github-milkbowl-vaultapi = { module = "com.github.MilkBowl:VaultAPI", version.ref = "com-github-milkbowl-vaultapi" } +de-tr7zw-item-nbt-api = { module = "de.tr7zw:item-nbt-api", version.ref = "de-tr7zw-item-nbt-api" } +dev-jorel-commandapi-bukkit-core = { module = "dev.jorel:commandapi-bukkit-core", version.ref = "dev-jorel-commandapi-bukkit-core" } +io-papermc-paper-paper-api = { module = "io.papermc.paper:paper-api", version.ref = "io-papermc-paper-paper-api" } +net-ess3-essentialsx = { module = "net.ess3:EssentialsX", version.ref = "net-ess3-essentialsx" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1b33c55 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d4081da --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..23d15a9 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 3ba7b09..0000000 --- a/pom.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - 4.0.0 - - com.shmkane - sellstick - 2.0.0 - - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.3.1 - - ${project.basedir}/target - - - - org.apache.maven.plugins - maven-compiler-plugin - - 8 - 8 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.5.0 - - - shade - package - - shade - - - - - - - de.tr7zw.changeme.nbtapi - com.shmkane.sellstick.nbtapi - - - - - - - - - - papermc - https://repo.papermc.io/repository/maven-public/ - - - jitpack.io - https://jitpack.io - - - ess-repo - https://ci.ender.zone/plugin/repository/everything/ - - - codemc-repo - https://repo.codemc.io/repository/maven-public/ - default - - - - - - - io.papermc.paper - paper-api - 1.21.5-R0.1-SNAPSHOT - provided - - - com.github.MilkBowl - VaultAPI - 1.7 - provided - - - net.ess3 - EssentialsX - 2.16.1 - provided - - - com.github.brcdev-minecraft - shopgui-api - 3.0.0 - provided - - - de.tr7zw - item-nbt-api - 2.15.0 - - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..445656c --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,5 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +rootProject.name = "sellstick" diff --git a/src/main/java/com/shmkane/sellstick/SellStick.java b/src/main/java/com/shmkane/sellstick/SellStick.java index 2d3caf0..3a2bb81 100644 --- a/src/main/java/com/shmkane/sellstick/SellStick.java +++ b/src/main/java/com/shmkane/sellstick/SellStick.java @@ -1,10 +1,14 @@ package com.shmkane.sellstick; +import com.earth2me.essentials.IEssentials; +import com.shmkane.sellstick.commands.AdminCommands; +import com.shmkane.sellstick.commands.CommandManager; import com.shmkane.sellstick.configs.PriceConfig; import com.shmkane.sellstick.configs.SellstickConfig; import com.shmkane.sellstick.events.PlayerListener; import com.shmkane.sellstick.utilities.ChatUtils; +import dev.jorel.commandapi.CommandAPI; import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; @@ -23,6 +27,7 @@ public class SellStick extends JavaPlugin { private static Economy econ = null; public boolean ShopGUIEnabled, EssentialsEnabled, CommandAPIEnabled = false; + private static IEssentials ess; SellstickConfig sellstickConfig; PriceConfig priceConfig; @@ -53,6 +58,10 @@ public void onEnable() { //Load Variables, Listeners and Commands loadVariables(); loadClasses(); + + // Instantiate commands + CommandAPI.onEnable(); + new CommandManager(); } // Reload plugin (only configurations && variables) @@ -75,8 +84,6 @@ public void loadVariables() { public void loadClasses() { // Register Listeners Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); - // Register Commands - getCommand("sellstick").setExecutor(new SellStickCommand()); // Create config classes sellstickConfig = new SellstickConfig("config", getDataFolder()); priceConfig = new PriceConfig("prices", getDataFolder()); @@ -96,6 +103,7 @@ private boolean setupEconomy() { if (rsp == null) { return false; } + ess = (IEssentials) SellStick.getInstance().getServer().getPluginManager().getPlugin("Essentials"); econ = rsp.getProvider(); return true; } @@ -104,11 +112,11 @@ public Economy getEcon() { return SellStick.econ; } - public static SellStick getInstance() { - return plugin; + public IEssentials getEssentials() { + return SellStick.ess; } - public int getMaxAmount() { - return sellstickConfig.getMaxAmount(); + public static SellStick getInstance() { + return plugin; } } diff --git a/src/main/java/com/shmkane/sellstick/SellStickCommand.java b/src/main/java/com/shmkane/sellstick/SellStickCommand.java deleted file mode 100644 index 8a88dbb..0000000 --- a/src/main/java/com/shmkane/sellstick/SellStickCommand.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.shmkane.sellstick; - -import com.shmkane.sellstick.configs.SellstickConfig; -import com.shmkane.sellstick.utilities.ChatUtils; -import com.shmkane.sellstick.utilities.CommandUtils; -import com.shmkane.sellstick.utilities.ConvertUtils; -import com.shmkane.sellstick.utilities.ItemUtils; -import com.shmkane.sellstick.utilities.EventUtils; -import com.shmkane.sellstick.utilities.MergeUtils; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabExecutor; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; - -public class SellStickCommand implements TabExecutor { - - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { - List commands = new ArrayList<>(); - - if (args.length == 1) { - if (sender.hasPermission("sellstick.give")) { - commands.add("give"); - } - if (sender.hasPermission("sellstick.reload")) { - commands.add("reload"); - } - if (sender.hasPermission("sellstick.convert")) { - commands.add("convert"); - } - if (sender.hasPermission("sellstick.merge")) { - commands.add("merge"); - } - if (sender.hasPermission("sellstick.toggle")) { - commands.add("toggle"); - } - } else if (args.length == 2) { - for(Player player : SellStick.getInstance().getServer().getOnlinePlayers()){ - commands.add(player.getName()); - } - } else if (args.length == 3) { - commands.add("1"); - } else if (args.length == 4) { - commands.add("i"); - commands.add("1"); - commands.add("2"); - commands.add("3"); - commands.add("5"); - commands.add("10"); - } - return commands; - } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { - - if (args.length == 0) { - ChatUtils.sendCommandNotProperMessage(sender); - return true; - } - - String subCommand = args[0].toLowerCase(); - - // Reload Command - if (subCommand.equals("reload") && sender.hasPermission("sellstick.reload")) { - try { - SellStick.getInstance().reload(); - return true; - } catch (Exception ex) { - ChatUtils.sendMsg(sender, "Something went wrong! Check console for error", true); - ChatUtils.log(Level.SEVERE, ex.getMessage()); - return false; - } - } - - // Convert Command - if (subCommand.equals("convert") && sender.hasPermission("sellstick.convert")) { - if (!(sender instanceof Player)) { - sender.sendMessage("Only players can use this command."); - return false; - } - ConvertUtils.convertSellStick((Player) sender); - return true; - } - - // Merge Command - if (subCommand.equals("merge") && sender.hasPermission("sellstick.merge")) { - // Get max amount of uses for a new sellstick - int maxAmount = SellStick.getInstance().getMaxAmount(); - - // Get player - Player player = (Player) sender; - - // Get all sellsticks in player inventory - ItemStack[] sellsticks = MergeUtils.searchInventory(player); - - // Check if player has any sellsticks - if (sellsticks.length == 0) { - ChatUtils.sendMsg(player, "You have no sellsticks in your inventory!", true); - return false; - } - - // Check if player has at least 2 sellsticks - if (sellsticks.length == 1) { - ChatUtils.sendMsg(player, "You need at least 2 sellsticks to merge!", true); - return false; - } - - // Sort sellsticks by their uses - ItemStack[] sortedSellsticks = MergeUtils.sortSellsticksByUses(sellsticks); - - // Sum the uses of all sellsticks - int usesSum = MergeUtils.sumSellStickUses(sortedSellsticks, maxAmount); - - // Check if sellsticks exceed max cap. - int totalUsesBeforeMerge = 0; - for (ItemStack sellstick : sortedSellsticks) { - totalUsesBeforeMerge += ItemUtils.getUses(sellstick); - } - - if (totalUsesBeforeMerge == usesSum) { - // Remove all sellsticks from player inventory - MergeUtils.removeSortedSellsticks(player, sortedSellsticks, maxAmount); - - // Give a new sellstick with a number of uses equalling usesSum - CommandUtils.giveSellStick(player, usesSum); - - ChatUtils.sendMsg(player, "All sellsticks merged successfully!", true); - - return true; - } else { - ChatUtils.sendMsg(player, "Sellsticks exceed the maximum allowed merged uses.", true); - return false; - } - } - - // Toggle Command - if (subCommand.equals("toggle") && sender.hasPermission("sellstick.toggle")) { - if (!(sender instanceof Player)) { - sender.sendMessage("Only players can use this command."); - return false; - } - - Player player = (Player) sender; - UUID playerUUID = player.getUniqueId(); - EventUtils.togglePlayerPreference(playerUUID); - - boolean newPreference = EventUtils.getPlayerPreference(playerUUID); - String message = newPreference ? "Sell messages will now be sent in chat." - : "Sell messages will now be sent in the action bar."; - ChatUtils.sendMsg(player, message, true); - - return true; - } - - // Give Command - else if (subCommand.equals("give") && sender.hasPermission("sellstick.give")) { - - if (args.length < 4) { - ChatUtils.sendMsg(sender, "Not enough arguments!", true); - return false; - } - - Player target = SellStick.getInstance().getServer().getPlayer(args[1]); - if (target == null) { - ChatUtils.sendMsg(sender, "Player not found", true); - return false; - } - - // Check if the argument is an integer - int numSticks; - try { - numSticks = Integer.parseInt(args[2]); - } catch (NumberFormatException ex) { - ChatUtils.sendMsg(sender, "Not a number: " + args[2], true); - return false; - } - - // Check if the stick is infinite - String argUses = args[3].toLowerCase(); - int uses; - - if (argUses.equals("i") || argUses.equals("infinite")) { - uses = Integer.MAX_VALUE; - } else { // Parse the argument is a number - try { - uses = Integer.parseInt(args[3]); - } catch (NumberFormatException ex) { - ChatUtils.sendMsg(sender, "Must be a number or 'i': ", true); - return false; - } - } - - // Give sell sticks - for (int i = 0; i < numSticks; i++) { - // TODO: Check if inventory is full or has enough slots?? - CommandUtils.giveSellStick(target, uses); - } - - ChatUtils.sendMsg(target, SellstickConfig.receiveMessage.replace("%amount%", numSticks + ""), true); - ChatUtils.sendMsg(sender, SellstickConfig.giveMessage.replace("%player%", target.getName()).replace("%amount%", numSticks + ""), true); - - return true; - } else { - ChatUtils.sendMsg(sender, SellstickConfig.noPerm, true); - } - return false; - } -} \ No newline at end of file diff --git a/src/main/java/com/shmkane/sellstick/commands/AdminCommands.java b/src/main/java/com/shmkane/sellstick/commands/AdminCommands.java new file mode 100644 index 0000000..e429c62 --- /dev/null +++ b/src/main/java/com/shmkane/sellstick/commands/AdminCommands.java @@ -0,0 +1,72 @@ +package com.shmkane.sellstick.commands; + +import com.shmkane.sellstick.SellStick; +import com.shmkane.sellstick.configs.SellstickConfig; +import com.shmkane.sellstick.utilities.ChatUtils; +import com.shmkane.sellstick.stick.StickHandler; +import dev.jorel.commandapi.executors.CommandArguments; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.logging.Level; + +public class AdminCommands { + + /** + * Converts an old SellStick held by a specified target player into the newer format. + */ + public void convert(CommandSender sender, CommandArguments args) { + final Player target = (Player) args.get("target"); + + if (target == null) return; + + // Try convert all items in inventory + for (ItemStack item : target.getInventory().getStorageContents()) { + if (item == null || item.isEmpty()) continue; + + ItemStack newItem = StickHandler.convertOldSellStick(item); + + if (newItem == null) continue; + + target.getInventory().remove(item); + target.getInventory().addItem(newItem); + } + } + + /** + * Reloads the SellStick plugin, reloading its configurations and variables. + */ + public void reload(CommandSender sender, CommandArguments arguments) { + try { + SellStick.getInstance().reload(); + ChatUtils.sendMsg(sender, "SellStick plugin reloaded successfully!"); + } catch (Exception ex) { + ChatUtils.sendMsg(sender, "Something went wrong! Check console for errors!"); + ChatUtils.log(Level.SEVERE, ex.getMessage()); + } + } + + /** + * Provides the ability to give a player a specified number of "sell sticks" with a defined number of uses. + */ + public void give(CommandSender sender, CommandArguments args) { + final Player target = (Player) args.get("target"); + final Integer numSticks = (Integer) args.get("amount"); + Integer uses = (Integer) args.get("uses"); + + if (target == null || numSticks == null || uses == null) return; + + // Give sell sticks + for (int i = 0; i < numSticks; i++) { + StickHandler.giveSellStickToPlayer(target, uses); + } + + ChatUtils.sendMsg(target, SellstickConfig.receiveMessage + .replace("%player%", target.getName()) + .replace("%amount%", numSticks.toString())); + ChatUtils.sendMsg(sender, SellstickConfig.giveMessage + .replace("%player%", target.getName()) + .replace("%amount%", numSticks.toString())); + } +} \ No newline at end of file diff --git a/src/main/java/com/shmkane/sellstick/commands/CommandManager.java b/src/main/java/com/shmkane/sellstick/commands/CommandManager.java new file mode 100644 index 0000000..b233359 --- /dev/null +++ b/src/main/java/com/shmkane/sellstick/commands/CommandManager.java @@ -0,0 +1,59 @@ +package com.shmkane.sellstick.commands; + +import com.shmkane.sellstick.SellStick; +import dev.jorel.commandapi.CommandAPICommand; +import dev.jorel.commandapi.arguments.*; +import org.bukkit.Bukkit; +import org.bukkit.entity.*; + +public class CommandManager { + + public CommandManager() { + + String cmd = "sellstick"; + + AdminCommands adminCommands = new AdminCommands(); + PlayerCommands playerCommands = new PlayerCommands(); + + new CommandAPICommand(cmd) + .withPermission(cmd) + .withSubcommand(new CommandAPICommand("reload") + .withPermission(cmd + ".reload") + .executes(adminCommands::reload)) + + .withSubcommand(new CommandAPICommand("convert") + .withPermission(cmd + ".convert") + .withArguments(new PlayerArgument("target") + .replaceSafeSuggestions(SafeSuggestions.suggest(info -> + Bukkit.getOnlinePlayers().toArray(new Player[0])))) + .executes(adminCommands::convert)) + + .withSubcommand(new CommandAPICommand("give") + .withPermission(cmd + ".give") + .withArguments(new PlayerArgument("target") + .replaceSafeSuggestions(SafeSuggestions.suggest(info -> + Bukkit.getOnlinePlayers().toArray(new Player[0])))) + .withArguments(new IntegerArgument("amount")) + .withArguments(new IntegerArgument("uses")) + .executes(adminCommands::give)) + + .withSubcommand(new CommandAPICommand("toggle") + .withPermission(cmd + ".toggle") + .executes(playerCommands::toggle)) + + .withSubcommand(new CommandAPICommand("merge") + .withPermission(cmd + ".merge") + .executes(playerCommands::merge)) + + .withSubcommand(new CommandAPICommand("give") + .withPermission(cmd + ".give") + .withArguments(new PlayerArgument("target") + .replaceSafeSuggestions(SafeSuggestions.suggest(info -> + Bukkit.getOnlinePlayers().toArray(new Player[0])))) + .withArguments(new IntegerArgument("amount")) + .withArguments(new IntegerArgument("uses")) + .executes(adminCommands::give)) + + .register(SellStick.getInstance()); + } +} diff --git a/src/main/java/com/shmkane/sellstick/commands/PlayerCommands.java b/src/main/java/com/shmkane/sellstick/commands/PlayerCommands.java new file mode 100644 index 0000000..f136076 --- /dev/null +++ b/src/main/java/com/shmkane/sellstick/commands/PlayerCommands.java @@ -0,0 +1,88 @@ +package com.shmkane.sellstick.commands; + +import com.shmkane.sellstick.configs.SellstickConfig; +import com.shmkane.sellstick.stick.StickHandler; +import com.shmkane.sellstick.utilities.ChatUtils; +import com.shmkane.sellstick.utilities.EventUtils; +import com.shmkane.sellstick.utilities.MergeUtils; +import dev.jorel.commandapi.executors.CommandArguments; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.UUID; + +public class PlayerCommands { + + /** + * Toggles the player's preference for receiving sell messages in chat or the action bar. + * If the preference is enabled, messages will be sent in chat; otherwise, they will be sent in the action bar. + */ + public void toggle(CommandSender sender, CommandArguments arguments) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return; + } + + UUID playerUUID = player.getUniqueId(); + EventUtils.togglePlayerPreference(playerUUID); + + boolean newPreference = EventUtils.getPlayerPreference(playerUUID); + String message = newPreference ? "Sell messages will now be sent in chat." + : "Sell messages will now be sent in the action bar."; + ChatUtils.sendMsg(player, message, true); + + } + + /** + * Merges all sellsticks in the inventory of the command sender into a single sellstick, combining their + * uses, provided that the total number of uses does not exceed the maximum allowed value. + */ + public void merge(CommandSender sender, CommandArguments args) { + + if (!(sender instanceof Player player)) return; + + // Get max amount of uses for a new sellstick + int maxAmount = SellstickConfig.maxAmount; + + // Get all sellsticks in player inventory + ItemStack[] sellsticks = MergeUtils.searchInventory(player); + + // Check if player has any sellsticks + if (sellsticks.length == 0) { + ChatUtils.sendMsg(player, "You have no sellsticks in your inventory!", true); + return; + } + + // Check if player has at least 2 sellsticks + if (sellsticks.length == 1) { + ChatUtils.sendMsg(player, "You need at least 2 sellsticks to merge!", true); + return; + } + + // Sort sellsticks by their uses + ItemStack[] sortedSellsticks = MergeUtils.sortSellSticksByUses(sellsticks); + + // Sum the uses of all sellsticks + int usesSum = MergeUtils.sumSellStickUses(sortedSellsticks, maxAmount); + + // Check if sellsticks exceed max cap. + int totalUsesBeforeMerge = 0; + for (ItemStack sellstick : sortedSellsticks) { + totalUsesBeforeMerge += StickHandler.getUses(sellstick); + } + + if (totalUsesBeforeMerge == usesSum) { + // Remove all sellsticks from player inventory + MergeUtils.removeSortedSellSticks(player, sortedSellsticks, maxAmount); + + // Give a new sellstick with a number of uses equalling usesSum + StickHandler.giveSellStickToPlayer(player, usesSum); + + ChatUtils.sendMsg(player, "All sellsticks merged successfully!", true); + + } else { + ChatUtils.sendMsg(player, "Sellsticks exceed the maximum allowed merged uses.", true); + } + } +} diff --git a/src/main/java/com/shmkane/sellstick/configs/Config.java b/src/main/java/com/shmkane/sellstick/configs/Config.java index 19db9be..cb45fca 100644 --- a/src/main/java/com/shmkane/sellstick/configs/Config.java +++ b/src/main/java/com/shmkane/sellstick/configs/Config.java @@ -22,7 +22,13 @@ public abstract class Config { setup(dataFolder); } - // Setup Main Configuration + /** + * Sets up the configuration file within the specified directory. If the directory or + * configuration file does not exist, it is created. Loads values from the configuration file + * after ensuring its existence. + * + * @param dir The directory in which the configuration file should be located or created. + */ public void setup(File dir) { if (dir.exists() || dir.mkdirs()) { conf = new File(dir + File.separator + configFilename); @@ -40,8 +46,7 @@ public void setup(File dir) { } @ForOverride - void loadValues(FileConfiguration config) { - } + void loadValues(FileConfiguration config) {} public static FileConfiguration getConfig() { return YamlConfiguration.loadConfiguration(conf); diff --git a/src/main/java/com/shmkane/sellstick/configs/PriceConfig.java b/src/main/java/com/shmkane/sellstick/configs/PriceConfig.java index f95355c..a763e42 100644 --- a/src/main/java/com/shmkane/sellstick/configs/PriceConfig.java +++ b/src/main/java/com/shmkane/sellstick/configs/PriceConfig.java @@ -1,7 +1,14 @@ package com.shmkane.sellstick.configs; import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import com.shmkane.sellstick.utilities.ChatUtils; +import jdk.jfr.Configuration; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; // Handles Prices.YML @@ -11,12 +18,30 @@ public PriceConfig(String configName, File dataFolder) { super(configName, dataFolder); } + public static Map prices = new HashMap<>(); + @Override void loadValues(FileConfiguration config) { - config.set("prices.SULPHUR", 1.02); - config.set("prices.RED_ROSE", 0.76); - config.set("prices.LEATHER", 2.13); - config.set("prices.COOKED_BEEF", 0.01); - config.set("prices.BONE", 5.00); + + ConfigurationSection pricesSection = config.getConfigurationSection("prices"); + + if (pricesSection == null) { + ChatUtils.log(Level.WARNING, "Prices section was not found in prices.yml."); + return; + } + + // Parse values into map + for (Map.Entry entry : pricesSection.getValues(false).entrySet()) { + Material material = Material.getMaterial(entry.getKey()); + if (material == null) { + ChatUtils.log(Level.WARNING, "Material " + entry.getKey() + " was not found in prices.yml."); + continue; + } + try { + prices.put(material, Float.parseFloat(entry.getValue().toString())); + } catch (NumberFormatException e) { + ChatUtils.log(Level.WARNING, "Price for " + entry.getKey() + " was not a valid number in prices.yml."); + } + } } } diff --git a/src/main/java/com/shmkane/sellstick/configs/SellstickConfig.java b/src/main/java/com/shmkane/sellstick/configs/SellstickConfig.java index 9638d37..c869f17 100644 --- a/src/main/java/com/shmkane/sellstick/configs/SellstickConfig.java +++ b/src/main/java/com/shmkane/sellstick/configs/SellstickConfig.java @@ -2,23 +2,29 @@ import com.shmkane.sellstick.SellStick; import com.shmkane.sellstick.utilities.ChatUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; import java.io.File; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; // Handles config.YML public class SellstickConfig extends Config { - public static List lore; - public static String displayName, PriceInterface, receiveMessage, giveMessage, nonSellingRelated, brokenStick, - nothingWorth, territoryMessage, noPerm, sellMessage, prefix, infiniteLore, finiteLore, holdOneMessage; - public static boolean sound, glow; + public static String PriceInterface, receiveMessage, giveMessage, nonSellingRelated, brokenStick, + nothingWorth, territoryMessage, noPerm, sellMessage, prefix, holdOneMessage; + public static boolean sound, glow, particles; public static Material material; public static int maxAmount; - static PriceSource priceSource; + public static PriceSource priceSource; + public static List loreFinite = new ArrayList<>(), loreInfinite = new ArrayList<>(); + public static Component displayName; + public static int loreLine; public SellstickConfig(String configName, File dataFolder) { super(configName, dataFolder); @@ -29,16 +35,27 @@ public SellstickConfig(String configName, File dataFolder) { void loadValues(FileConfiguration config) { // Price Interface Configuration PriceInterface = tryGetString(conf, "PriceSource", "PricesYML"); + priceSource = setPriceSource(PriceInterface); // Item Configuration - displayName = tryGetString(conf, "Item.DisplayName", "SellStick"); material = tryGetMaterial(conf, "Item.Material", Material.STICK); - lore = config.getStringList("Item.StickLore"); - finiteLore = tryGetString(conf, "Item.FiniteLore", "%remaining% remaining uses"); - infiniteLore = tryGetString(conf, "Item.InfiniteLore", "Infinite uses!"); - glow = Boolean.parseBoolean(tryGetString(conf, "Item.Glow", String.valueOf(true))); - maxAmount = Integer.parseInt(tryGetString(conf, "Item.MaxAmount", "2000")); - sound = Boolean.parseBoolean(tryGetString(conf, "Item.UseSound", String.valueOf(true))); + glow = config.getBoolean("Item.Glow", true); + maxAmount = config.getInt("Item.MaxAmount", 2000); + sound = config.getBoolean("Item.UseSound", true); + particles = config.getBoolean("Item.UseParticles", true); + + // Name / Lore + displayName = MiniMessage.miniMessage().deserialize(tryGetString(conf, "Item.DisplayName", "SellStick")); + // Infinite lore + config.getStringList("Item.InfiniteLore").forEach(line -> loreInfinite.add(MiniMessage.miniMessage().deserialize(line))); + // Finite lore + List finiteLore = config.getStringList("Item.FiniteLore"); + for (String line : finiteLore) { loreFinite.add(MiniMessage.miniMessage().deserialize(line)); } + // Lore line + for (int i = 0; i < loreFinite.size(); i++) { + if (loreFinite.get(i).toString().contains("%remaining%")) loreLine = i; + } + // Messages holdOneMessage = tryGetString(conf, "Messages.OnlyHoldOne", "Please use 1 sell stick at a time!"); prefix = tryGetString(conf, "Messages.PluginPrefix", "[SellStick] "); @@ -53,23 +70,12 @@ void loadValues(FileConfiguration config) { "Oak''s words echoed... There''s a time and place for everything but not now! (Right click a chest!)"); receiveMessage = tryGetString(conf, "Messages.ReceiveMessage", "You gave %player% %amount% SellSticks!"); giveMessage = tryGetString(conf, "Messages.GiveMessage", "You''ve received %amount% SellSticks!"); - - priceSource = setPriceSource(PriceInterface); - } - - public int getMaxAmount() { - return maxAmount; - } - - public static PriceSource getPriceSource() { - return priceSource; } private PriceSource setPriceSource(String priceString) { if (priceString != null) { if (priceString.equalsIgnoreCase("ShopGUI") && SellStick.getInstance().ShopGUIEnabled) { return PriceSource.SHOPGUI; - } if (priceString.equalsIgnoreCase("Essentials") && SellStick.getInstance().EssentialsEnabled) { return PriceSource.ESSWORTH; diff --git a/src/main/java/com/shmkane/sellstick/events/PlayerListener.java b/src/main/java/com/shmkane/sellstick/events/PlayerListener.java index 8342111..3016d87 100644 --- a/src/main/java/com/shmkane/sellstick/events/PlayerListener.java +++ b/src/main/java/com/shmkane/sellstick/events/PlayerListener.java @@ -1,9 +1,14 @@ package com.shmkane.sellstick.events; +import com.shmkane.sellstick.SellStick; import com.shmkane.sellstick.configs.SellstickConfig; +import com.shmkane.sellstick.stick.StickHandler; import com.shmkane.sellstick.utilities.*; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.Particle; import org.bukkit.Sound; -import org.bukkit.block.Block; +import org.bukkit.block.*; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -12,95 +17,120 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Objects; + +import static com.shmkane.sellstick.utilities.ChatUtils.log; public class PlayerListener implements Listener { - @EventHandler(priority = EventPriority.MONITOR) // Checks if other plugins are using the event + @EventHandler(priority = EventPriority.HIGHEST) public void onSellstickUse(PlayerInteractEvent event) { - Player player = event.getPlayer(); - Block block = event.getClickedBlock(); - ItemStack sellStick = player.getInventory().getItemInMainHand(); + // Execute quick checks + if (!(event.getAction() == Action.RIGHT_CLICK_BLOCK)) return; + if (event.getClickedBlock() == null) return; + if (event.getMaterial() != SellstickConfig.material) return; + if (event.getItem() == null || !event.getItem().hasItemMeta()) return; - // Player preference for sell message - boolean sendInChat = EventUtils.getPlayerPreference(player.getUniqueId()); - - if (!(event.getAction() == Action.RIGHT_CLICK_BLOCK)) return; // Must right-click - if (sellStick.getItemMeta() == null || block == null) return; // Return if empty item + // Possibly a sellstick - continue - // Convert old sellticks - ItemMeta meta = sellStick.getItemMeta(); - if (meta != null && meta.hasDisplayName()) { - String name = meta.displayName() != null ? meta.displayName().toString() : ""; - if (name.startsWith("§e✦ §e§lSellStick") || name.startsWith("§6§lSellStick")) { - ConvertUtils.convertSellStick(player); - return; - } - } + if (!(event.getClickedBlock().getState() instanceof Container container)) return; - // Replace unstackable sellstick with stackable one - if (ConvertUtils.makeSellStickStackable(player, sellStick)) return; + Player player = event.getPlayer(); + ItemStack sellStick = player.getInventory().getItemInMainHand(); - if (event.getPlayer().isSneaking()) return; // Check Player is not sneaking - if (sellStick.getType().isAir()) return; // Check if Item is air - if (!ItemUtils.matchSellStickUUID(sellStick)) return; // Check if Item has UUID of SellStick - if (!EventUtils.didClickSellStickBlock(block)) return; // Check if clicked block is a container + // Convert old sellstick + ItemStack newStick = StickHandler.convertOldSellStick(sellStick); + if (newStick != null) { + player.getInventory().setItemInMainHand(newStick); + ChatUtils.sendMsg(player, "Your old sell stick has been updated.", true); + event.setCancelled(true); + return; + } // Check if another plugin is cancelling the event - if (event.useInteractedBlock() == Event.Result.DENY){ + if (event.useInteractedBlock() == Event.Result.DENY) { ChatUtils.sendMsg(player, SellstickConfig.territoryMessage, true); return; } event.setCancelled(true); // Cancel opening the chest - confirmed player is using a sellstick - // Check sellstick material - if (!ItemUtils.matchSellStickMaterial(sellStick)) { - // Replace the item if the material does not match - player.getInventory().removeItem(sellStick); - CommandUtils.giveSellStick(player, ItemUtils.getUses(sellStick)); - return; - } - // Checks if Player has the permission to use a SellStick + // Check permission if (!player.hasPermission("sellstick.use")) { ChatUtils.sendMsg(player, SellstickConfig.noPerm, true); return; } + // Check if player is only holding 1 stick if (sellStick.getAmount() != 1) { ChatUtils.sendMsg(player, SellstickConfig.holdOneMessage, true); return; } - // Get total value of container - double total = EventUtils.calculateContainerWorth(event); + // Player preference for sell message + boolean sendInChat = EventUtils.getPlayerPreference(player.getUniqueId()); // Nothing worth selling - if (total <= 0) { + if (EventUtils.getContainerWorth(container, false) <= 0d) { if (sendInChat) { - ChatUtils.sendMsg(player, SellstickConfig.nothingWorth, true); + ChatUtils.sendMsg(player, SellstickConfig.nothingWorth); } else { ChatUtils.sendActionBar(player, SellstickConfig.nothingWorth); } - event.setCancelled(true); return; } - // Sell the items - if (!EventUtils.saleEvent(player, sellStick, total)) { + // Remove items + double total = EventUtils.getContainerWorth(container, true); + + // Subtract use + if (!StickHandler.isInfinite(sellStick)) StickHandler.subtractUses(sellStick); + + double multiplier = EventUtils.getPlayersMultiplier(player); + Economy econ = SellStick.getInstance().getEcon(); + int uses = StickHandler.getUses(sellStick); + + // Add funds to player + EconomyResponse response = econ.depositPlayer(player, total * multiplier); + if (!response.transactionSuccess()) { + ChatUtils.sendMsg(player, String.format("An error occurred: " + SellstickConfig.prefix, response.errorMessage), true); + return; + } + + // Send message to player + String[] send = SellstickConfig.sellMessage.split("\\\\n"); + for (String msg : send) { if (sendInChat) { - ChatUtils.sendMsg(player, SellstickConfig.nothingWorth, true); + ChatUtils.sendMsg(player, msg + .replace("%uses%", String.valueOf(uses)) + .replace("%balance%", econ.format(response.balance)) + .replace("%price%", econ.format(response.amount)), true); } else { - ChatUtils.sendActionBar(player, SellstickConfig.nothingWorth); + ChatUtils.sendActionBar(player, msg + .replace("%uses%", String.valueOf(uses)) + .replace("%balance%", econ.format(response.balance)) + .replace("%price%", econ.format(response.amount))); } - event.setCancelled(true); - return; } + // Send to log file + String coords = Math.round(player.getLocation().x()) + " " + Math.round(player.getLocation().y()) + " " + Math.round(player.getLocation().z()); + ChatUtils.writeLog(player.getUniqueId() + " sold $" + Math.round(response.amount) + " ($" + Math.round(response.balance) + ") at " + coords + " in " + player.getWorld().getName()); + // Play sound if (SellstickConfig.sound) { - assert event.getInteractionPoint() != null; - player.playSound(event.getInteractionPoint(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 0.5f); + player.playSound(Objects.requireNonNull(event.getInteractionPoint()), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 0.5f); + } + // Particles + if (SellstickConfig.particles) { + player.getWorld().spawnParticle(Particle.HAPPY_VILLAGER, Objects.requireNonNull(event.getInteractionPoint()), 5, 0.2, 0.2, 0.2, 0); + } + // Remove broken stick + if (uses <= 0) { + player.getInventory().removeItem(sellStick); + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f); + ChatUtils.sendMsg(player, SellstickConfig.brokenStick, true); } } } diff --git a/src/main/java/com/shmkane/sellstick/stick/StickHandler.java b/src/main/java/com/shmkane/sellstick/stick/StickHandler.java new file mode 100644 index 0000000..b00381e --- /dev/null +++ b/src/main/java/com/shmkane/sellstick/stick/StickHandler.java @@ -0,0 +1,245 @@ +package com.shmkane.sellstick.stick; + +import com.shmkane.sellstick.configs.SellstickConfig; +import com.shmkane.sellstick.utilities.ChatUtils; +import de.tr7zw.nbtapi.NBT; +import de.tr7zw.nbtapi.iface.ReadableNBT; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StickHandler { + + /** + * Creates a SellStick item with a specified number of uses. + * + * @param uses The number of uses the SellStick should have. + * @return The created {@code ItemStack} representing a SellStick, or {@code null} if the item + * could not be created due to an invalid configuration. + */ + @NotNull + public static ItemStack createSellStick(int uses) { + ItemStack itemStack; + + itemStack = new ItemStack(SellstickConfig.material); + + ItemMeta itemMeta = itemStack.getItemMeta(); + + // Set display name + itemMeta.displayName(SellstickConfig.displayName); + + // Set lore + itemMeta.lore((uses == Integer.MAX_VALUE) ? SellstickConfig.loreInfinite : SellstickConfig.loreFinite); + + // Add glow if required + if (SellstickConfig.glow) { + itemStack.addUnsafeEnchantment(Enchantment.FORTUNE, 1); + itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itemMeta.setEnchantmentGlintOverride(true); + } + + // Apply meta to item stack + itemStack.setItemMeta(itemMeta); + + // Set NBT, uses and lore + StickHandler.setUses(itemStack, uses); + + return itemStack; + } + + /** + * Gives the specified player a SellStick with a defined number of uses. + * The SellStick will be added to the player's inventory or dropped at their location + * if the inventory is full. + * + * @param target The player who will receive the SellStick. + * @param uses The number of uses the SellStick will have. + */ + public static void giveSellStickToPlayer(Player target, int uses) { + + ItemStack sellStick = createSellStick(uses); + + // Check if target inventory is full + if (target.getInventory().firstEmpty() == -1) { + Location l = target.getLocation(); + // Drop item on player + target.getWorld().dropItem(l, sellStick); + ChatUtils.sendMsg(target, "&cInventory full! Sellstick was dropped on the ground!"); + + String locationString = l.getWorld().getName() + " [" + Math.round(l.x()) + " " + Math.round(l.y()) + " " + Math.round(l.z()) + "]" ; + ChatUtils.log("Gave " + target.displayName() + " a sellstick but inv was full. Item dropped at " + locationString); + } else { + // Add to inventory + target.getInventory().addItem(sellStick); + } + } + + /** + * Check if a Sellstick is infinite + * @param itemStack The sellstick to check. + * @return true if infinite, false if not. + */ + public static boolean isInfinite(ItemStack itemStack) { + + ReadableNBT nbtItemStack = NBT.readNbt(itemStack); + + return nbtItemStack.getBoolean("Infinite"); + } + + /** + * Retrieves the remaining uses of the given ItemStack from its NBT data. + * + * @param itemStack The ItemStack whose remaining uses will be retrieved. + * @return The number of uses remaining for the ItemStack, as stored in its NBT data. + */ + public static int getUses(ItemStack itemStack) { + ReadableNBT nbtItemStack = NBT.readNbt(itemStack); + return nbtItemStack.getInteger("UsesRemaining"); + } + + /** + * Sets the remaining uses of the given ItemStack by updating its NBT data and lore. + * When the uses are set to the maximum integer value, the item is considered infinite. + * This method modifies the item's metadata and updates its lore accordingly. + * + * @param itemStack The ItemStack to modify by setting its remaining uses. + * @param uses The number of remaining uses to set for the ItemStack. If this value is + * {@code Integer.MAX_VALUE}, the item will be marked as infinite. + */ + public static void setUses(ItemStack itemStack, int uses) { + // NBT + NBT.modify(itemStack, nbt -> { + nbt.setInteger("UsesRemaining", uses); + nbt.setBoolean("Infinite", (uses == Integer.MAX_VALUE)); + }); + + // Update lore + updateRemainingLore(itemStack, uses); + } + + /** + * Decreases the remaining uses of the provided ItemStack by 1. + * Updates the item's NBT data and lore accordingly to the new uses. + * + * @param itemStack The ItemStack whose remaining uses will be reduced. + */ + public static void subtractUses(ItemStack itemStack) { + + // Skip if infinite + if (isInfinite(itemStack)) return; + + // Subtract use + int uses = getUses(itemStack) - 1; + NBT.modify(itemStack, nbt -> { + nbt.setInteger("UsesRemaining", uses); + }); + + // Update lore + updateRemainingLore(itemStack, uses); + } + + /** + * Check if a valid sellstick based on if it has Uses and Infinite NBT tags. + * @param itemStack The itemstack to check. + * @return true if sellstick, false if not. + */ + public static boolean hasSellStickNBT(ItemStack itemStack) { + ReadableNBT nbtItemStack = NBT.readNbt(itemStack); + return (nbtItemStack.hasTag("UsesRemaining") && nbtItemStack.hasTag("Infinite")); + } + + /** + * Check if a sellstick is old by: + * - Must be a stick + * - Must have an enchant + * - Must contain sellstick in name + * - Must not match current name + * - Must not match current lore + * + * @param itemStack The ItemStack to be checked. + * @return {@code true} if the provided ItemStack is an old SellStick; {@code false} otherwise. + */ + public static boolean isOldSellStick(ItemStack itemStack) { + if (itemStack == null) return false; + // Must be stick + if (itemStack.getType() != Material.STICK) return false; + // Must have an enchant + if (itemStack.getEnchantments().isEmpty()) return false; + // Must have sellstick in the name + if (!PlainTextComponentSerializer.plainText().serialize(itemStack.displayName()).toLowerCase().contains("sellstick")) return false; + // Must not be current + ChatUtils.log("Checking if " + (itemStack.displayName() == SellstickConfig.displayName)); + if (itemStack.displayName() == SellstickConfig.displayName) return false; + if (itemStack.getItemMeta().lore() == SellstickConfig.loreFinite) return false; + + return true; + } + + /** + * Converts an old SellStick item into the current version if it matches the requirements + * for being considered an old SellStick. + * + * @param itemStack The ItemStack to be converted. This should be validated before being + * passed to ensure it is a potential old SellStick. + * @return The converted SellStick ItemStack, or {@code null} if the provided ItemStack + * is not an old SellStick. + */ + public static ItemStack convertOldSellStick(ItemStack itemStack) { + if (!isOldSellStick(itemStack)) return null; + List lore = itemStack.getItemMeta().lore(); + + int uses = 0; + int amount = itemStack.getAmount(); + + for (Component line : lore) { + String text = PlainTextComponentSerializer.plainText().serialize(line).toLowerCase(); + if (text.contains("infinite")) { + uses = Integer.MAX_VALUE; + break; + } + ChatUtils.log("Checking for uses: " + text); + Pattern pattern = Pattern.compile("\\d+"); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + ChatUtils.log("Found uses: " + matcher.group()); + uses = Integer.parseInt(matcher.group()); + break; + } + } + + ItemStack newStick = StickHandler.createSellStick(uses); + newStick.setAmount(amount); + return newStick; + } + + /** + * Used to update the remaining uses on the items lore most efficiently. + * Must be an up-to-date config sellstick. + * @param itemStack The itemstack to update the lore for. + */ + public static void updateRemainingLore(ItemStack itemStack, int uses) { + + String newStrLine = MiniMessage.miniMessage().serialize(SellstickConfig.loreFinite.get(SellstickConfig.loreLine)) + .replace("%remaining%", String.valueOf(uses)); + Component newLine = MiniMessage.miniMessage().deserialize(newStrLine); + + ItemMeta itemMeta = itemStack.getItemMeta(); + List lore = itemMeta.lore(); + lore.set(SellstickConfig.loreLine, newLine); + itemMeta.lore(lore); + itemStack.setItemMeta(itemMeta); + } + +} diff --git a/src/main/java/com/shmkane/sellstick/utilities/ChatUtils.java b/src/main/java/com/shmkane/sellstick/utilities/ChatUtils.java index e247367..8a1b6ea 100644 --- a/src/main/java/com/shmkane/sellstick/utilities/ChatUtils.java +++ b/src/main/java/com/shmkane/sellstick/utilities/ChatUtils.java @@ -3,44 +3,101 @@ import com.shmkane.sellstick.configs.SellstickConfig; import com.shmkane.sellstick.SellStick; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; import java.util.logging.Level; public class ChatUtils { - - // Send Messages - public static void sendMsg(CommandSender sender, String string, boolean showPrefix) { - Component msg = MiniMessage.miniMessage() - .deserialize((showPrefix) ? SellstickConfig.prefix.concat(string) : string); - if (sender instanceof ConsoleCommandSender) - log(Level.INFO, string); - else if (sender instanceof Player) - sender.sendMessage(msg); + // Send player messages + public static void sendMsg(CommandSender sender, String message) { + sendMsg(sender, message, true); + } + public static void sendMsg(CommandSender sender, String message, boolean showPrefix) { sendMsg(sender, toTextComp(message), showPrefix); } + public static void sendMsg(CommandSender sender, TextComponent message, boolean showPrefix) { + if (showPrefix) message = toTextComp(SellstickConfig.prefix).append(message); + sendMsg(sender, message); + } + public static void sendMsg(CommandSender sender, TextComponent message) { + if (sender instanceof Player) { + sender.sendMessage(message); + return; + } + log(Level.INFO, message); } - + // Send Action Bar Messages public static void sendActionBar(CommandSender sender, String string) { Component msg = MiniMessage.miniMessage().deserialize(string); sender.sendActionBar(msg); } - public static void sendCommandNotProperMessage(CommandSender sender) { - if (sender.hasPermission("sellstick.give")) { - ChatUtils.sendMsg(sender, NamedTextColor.GREEN + "/SellStick give (/infinite)", true); - } - if (sender.hasPermission("sellstick.reload")) { - ChatUtils.sendMsg(sender, NamedTextColor.GREEN + "/SellStick reload", true); - } + // Server console messages + public static void error(String message) { log(Level.SEVERE, message); } + public static void error(TextComponent message) { log(Level.SEVERE, message); } + public static void log(String message) { log(Level.INFO, message); } + public static void log(TextComponent message) { log(Level.INFO, message); } + public static void log(Level level, TextComponent message) { log(level, PlainTextComponentSerializer.plainText().serializeOrNull(message)); } + public static void log(Level level, String message) { + if (message == null || message.isBlank()) return; + SellStick.getInstance().getLogger().log(level, message); } - // Server Logger - public static void log(Level level, String string) { - SellStick.getInstance().getLogger().log(level, string); + /** + * Converts a string to TextComponent and parses & for colour, # for hex. + * @param string The string to parse. + * @return The formated TextComponent. + */ + static TextComponent toTextComp(String string) { + if (string.contains("&")) + return LegacyComponentSerializer.builder().character('&').hexCharacter('#').build().deserialize(string); + else + return (TextComponent) MiniMessage.miniMessage().deserialize(string); + } + + /** + * Writes a log entry to a daily log file in the plugin's log folder. + * + * @param message The log message to be written to the file. + */ + public static void writeLog(String message) { + try { + // Get date + time + Date calender = Calendar.getInstance().getTime(); + String time = new SimpleDateFormat("dd-MM-yy HH:mm:ss").format(calender); + String date = time.substring(0, 8); + + // Get / create log folder + File logFolder = new File(SellStick.getInstance().getDataFolder() + File.separator + "logs"); + if (!logFolder.exists()) logFolder.mkdir(); + + // Get / create log file + File saveTo = new File(logFolder,date + ".log"); + if (!saveTo.exists()) saveTo.createNewFile(); + + // Write log to file + FileWriter fw = new FileWriter(saveTo, true); + PrintWriter pw = new PrintWriter(fw); + pw.println(time + " | " + message); + pw.close(); + + } catch (IOException e) { + ChatUtils.log(Level.SEVERE, e.toString()); + e.printStackTrace(); + } } } diff --git a/src/main/java/com/shmkane/sellstick/utilities/CommandUtils.java b/src/main/java/com/shmkane/sellstick/utilities/CommandUtils.java deleted file mode 100644 index c3f22f0..0000000 --- a/src/main/java/com/shmkane/sellstick/utilities/CommandUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.shmkane.sellstick.utilities; - -import com.shmkane.sellstick.configs.SellstickConfig; -import net.kyori.adventure.text.minimessage.MiniMessage; - -// import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import java.util.logging.Level; - -public class CommandUtils { - - public static void giveSellStick(Player target, int uses) { - - ItemStack itemStack; - - try { - itemStack = new ItemStack(SellstickConfig.material); - } catch (Exception ex) { - ChatUtils.log(Level.SEVERE, SellstickConfig.prefix + " - Invalid item set in config. Please read the links I put in the config to fix this."); - return; - } - - ItemMeta itemMeta = itemStack.getItemMeta(); - - // Set display name - itemMeta.displayName(MiniMessage.miniMessage().deserialize(SellstickConfig.displayName)); - - // Apply meta to item stack - itemStack.setItemMeta(itemMeta); - - // Add glow if required - if (SellstickConfig.glow) { - ItemUtils.glow(itemStack); - } - - // Set NBT, uses and lore - ItemUtils.setSellStick(itemStack); - ItemStack finalItem = ItemUtils.setUses(itemStack, uses); - - // Add to inventory - target.getInventory().addItem(finalItem); - } -} diff --git a/src/main/java/com/shmkane/sellstick/utilities/ConvertUtils.java b/src/main/java/com/shmkane/sellstick/utilities/ConvertUtils.java deleted file mode 100644 index 3612b85..0000000 --- a/src/main/java/com/shmkane/sellstick/utilities/ConvertUtils.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.shmkane.sellstick.utilities; - -import com.shmkane.sellstick.SellStick; -import de.tr7zw.changeme.nbtapi.NBT; -import de.tr7zw.changeme.nbtapi.iface.ReadableNBT; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ConvertUtils { - - public static boolean makeSellStickStackable(Player player, ItemStack sellStick) { - if (sellStick == null || sellStick.isEmpty()) return false; - ReadableNBT nbt = NBT.readNbt(sellStick); - if (!nbt.hasTag("RandomSSUUID")) return false; - if (!nbt.hasTag("UsesRemaining")) return false; - if (!nbt.hasTag("Infinite")) return false; - - int uses = ItemUtils.getUses(sellStick); - int amount = sellStick.getAmount(); - - player.getInventory().removeItem(sellStick); - for (int i = 0; i < amount; i++) { - CommandUtils.giveSellStick(player, uses); - } - ChatUtils.sendMsg(player, "Your sellstick has been replaced with a stackable version!", true); - return true; - } - - public static void convertSellStick (Player player) { - if (player.getInventory().getItemInMainHand().isEmpty() || player.getInventory().getItemInMainHand().getType().isAir()) { - return; - } - - ItemStack sellstick = player.getInventory().getItemInMainHand(); - String nbt = sellstick.getItemMeta().toString(); - - if (nbt.contains("UNSPECIFIC_META:{meta-type=UNSPECIFIC, display-name={\"text\":\"SellStick\"")) { - ChatUtils.sendMsg(player, "This is not an old SellStick! ", true); - return; - } - - if (!nbt.contains("✦") && !nbt.contains("SellStick")) { - ChatUtils.sendMsg(player, "This is not an old SellStick! ", true); - Bukkit.getLogger().info(MiniMessage.miniMessage().serialize(sellstick.getItemMeta().displayName())); - return; - } - - if (sellstick.getAmount() != 1) { - ChatUtils.sendMsg(player, "You cannot convert more than one SellStick at a time!", true); - return; - } - - if (sellstick.getItemMeta() == null) { - return; - } - - ItemMeta itemMeta = sellstick.getItemMeta(); - List sellStickLore = itemMeta.lore(); - - // if (sellStickLore == null || sellStickLore.size() < 3) { - // ChatUtils.sendMsg(player, "SellStick does not have enough lore lines!", true); - // return; - // } - - String numberLine = MiniMessage.miniMessage().serialize(sellStickLore.get(2)); - - if (numberLine.contains("Infinite")) { - player.getInventory().removeItem(sellstick); - Bukkit.getServer().getScheduler().runTaskLater(SellStick.getInstance(), () -> { - // Your code to be executed after 5 seconds goes here - // For example, you can send a delayed message to the player - CommandUtils.giveSellStick(player, Integer.MAX_VALUE); - }, 10L); - ChatUtils.sendMsg(player, "Replaced old Sellstick!", true); - return; - } - - // Use a regular expression to extract the number from the lore line - Matcher matcher = Pattern.compile("\\d+").matcher(numberLine); - - if (!matcher.find()) { - ChatUtils.sendMsg(player,"No number found in the lore line.", true); - return; - } - - try { - int uses = Integer.parseInt(matcher.group()); - // Now 'uses' contains the extracted number from the lore line - player.getInventory().removeItem(sellstick); - Bukkit.getServer().getScheduler().runTaskLater(SellStick.getInstance(), () -> { - // Your code to be executed after 5 seconds goes here - // For example, you can send a delayed message to the player - CommandUtils.giveSellStick(player, uses); - }, 10L); - ChatUtils.sendMsg(player, "Replaced old Sellstick!", true); - } catch (NumberFormatException e) { - ChatUtils.sendMsg(player, "Unable to parse the number from the lore line.", true); - } - } -} diff --git a/src/main/java/com/shmkane/sellstick/utilities/EventUtils.java b/src/main/java/com/shmkane/sellstick/utilities/EventUtils.java index 4075bfa..4756854 100644 --- a/src/main/java/com/shmkane/sellstick/utilities/EventUtils.java +++ b/src/main/java/com/shmkane/sellstick/utilities/EventUtils.java @@ -5,205 +5,103 @@ import com.shmkane.sellstick.configs.SellstickConfig; import com.shmkane.sellstick.SellStick; import net.brcdev.shopgui.ShopGuiPlusApi; -import net.milkbowl.vault.economy.Economy; -import net.milkbowl.vault.economy.EconomyResponse; -import org.bukkit.block.Barrel; -import org.bukkit.block.Block; -import org.bukkit.block.Chest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.block.Container; import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.permissions.PermissionAttachmentInfo; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; import java.math.BigDecimal; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.UUID; -import java.util.logging.Level; public class EventUtils { private static final HashMap playerPreferences = new HashMap<>(); - // Get the player's preference for receiving sell messages (true for chat, false for action bar) + /** + * Get the player's preference for receiving sell messages (true for chat, false for action bar) + * @param playerUUID The players UUID. + * @return true if the player wants chat, false if action bar + */ public static boolean getPlayerPreference(UUID playerUUID) { return playerPreferences.getOrDefault(playerUUID, true); // Default to true (chat) } - // Toggle the player's preference for receiving sell messages + /** + * Toggle the player's preference for receiving sell messages + * @param playerUUID The players UUID. + */ public static void togglePlayerPreference(UUID playerUUID) { boolean currentPreference = getPlayerPreference(playerUUID); playerPreferences.put(playerUUID, !currentPreference); } - public static double calculateContainerWorth(PlayerInteractEvent event) { - - InventoryHolder container = (InventoryHolder) Objects.requireNonNull(event.getClickedBlock()).getState(); + /** + * Calculates the total worth of items inside a container using various pricing sources, + * removes those items from the container, and returns the calculated total value. + * + * @param container The container whose contents will be evaluated for their worth. + * @return The total value of all sellable items in the container. + */ + public static double getContainerWorth(Container container, boolean removeItems) { ItemStack[] containerContents = container.getInventory().getContents(); double total = 0; - SellstickConfig.PriceSource priceSource = SellstickConfig.getPriceSource(); - for (ItemStack itemstack : containerContents) { - - // Check if ItemStack is null if (itemstack == null) continue; - if (itemstack.getItemMeta().hasDisplayName()) continue; - // Reset each variable on each itemstack double price = 0; - double slotPrice; - switch (priceSource) { + switch (SellstickConfig.priceSource) { case PRICESYML: - ConfigurationSection pricesSection = PriceConfig.getConfig().getConfigurationSection("prices"); - - // Initialize a map to store prices - assert pricesSection != null; - Map prices = pricesSection.getValues(false); - // Check Price of ItemStack - for (Map.Entry entry : prices.entrySet()) { - if (itemstack.getType().toString().equalsIgnoreCase(entry.getKey())) { - price = Double.parseDouble(entry.getValue().toString()); - } - } + // Get price from config + Float configPrice = PriceConfig.prices.get(itemstack.getType()); + if (configPrice == null || configPrice <= 0f) continue; + price = configPrice.doubleValue(); break; case SHOPGUI: - price = ShopGuiPlusApi.getItemStackPriceSell(itemstack); - if (price < 0) { - price = 0; - } + price = ShopGuiPlusApi.getItemStackPriceSell(itemstack); + if (price <= 0d) continue; break; case ESSWORTH: - IEssentials ess = (IEssentials) SellStick.getInstance().getServer().getPluginManager().getPlugin("Essentials"); - assert ess != null; + IEssentials ess = SellStick.getInstance().getEssentials(); BigDecimal essPrice = ess.getWorth().getPrice(ess, itemstack); - // Check for null before getting double value - if (essPrice != null) { - price = essPrice.doubleValue(); - } else { - // Handle the case where the price is null (log an error, throw an exception, etc.) - // For now, setting price to 0 if it's null - price = 0; - } + if (essPrice == null || essPrice.doubleValue() <= 0d) continue; + + price = essPrice.doubleValue(); break; } int amount = itemstack.getAmount(); - // ShopGUI already implements amount within the API - if (priceSource == SellstickConfig.PriceSource.SHOPGUI) { - amount = 1; - } + // ShopGUI includes amount in price already + if (SellstickConfig.priceSource == SellstickConfig.PriceSource.SHOPGUI) { amount = 1; } - slotPrice = price * amount; + if (removeItems) container.getInventory().remove(itemstack); - if (slotPrice > 0) { - container.getInventory().remove(itemstack); - } - total += slotPrice; + total += price * amount; } return total; } - // Checks if clicked block is on a chest, barrel or Shulker Box with a SellStick - @Deprecated - public static boolean didClickContainerWithSellStick(PlayerInteractEvent event) { - ItemStack playerHand = event.getPlayer().getInventory().getItemInMainHand(); - - if (ItemUtils.isSellStick(playerHand)) return false; - - Block block = event.getClickedBlock(); - return (block instanceof Chest || block instanceof Barrel || block instanceof ShulkerBox); - } - - // Checks if clicked block is on a chest, barrel or shulker box - public static boolean didClickSellStickBlock(Block block) { - return (block.getState() instanceof Chest || block.getState() instanceof Barrel || block.getState() instanceof ShulkerBox); - } - - // Handles the SellStick in SaleEvent and PostSaleEvent - (Originally Made by MrGhetto) - public static boolean saleEvent(Player player, ItemStack sellStick, double total) { - // Player preference for sell message - boolean sendInChat = getPlayerPreference(player.getUniqueId()); - - // Subtract use - if (!ItemUtils.isInfinite(sellStick)) ItemUtils.subtractUses(sellStick); - - double multiplier = setMultiplier(player); // Get sell multiplier - Economy econ = SellStick.getInstance().getEcon(); // Get economy - int uses = ItemUtils.getUses(sellStick); // Get uses - - // Add funds to player - EconomyResponse response = econ.depositPlayer(player, total * multiplier); - - // Catch error - if (!response.transactionSuccess()) { - ChatUtils.sendMsg(player, String.format("An error occurred: " + SellstickConfig.prefix, response.errorMessage), true); - return false; - } - - // Send message to player - String[] send = SellstickConfig.sellMessage.split("\\\\n"); - - for (String msg : send) { - if (sendInChat) { - ChatUtils.sendMsg(player, msg - .replace("%uses%", String.valueOf(uses)) - .replace("%balance%", econ.format(response.balance)) - .replace("%price%", econ.format(response.amount)), true); - } else { - ChatUtils.sendActionBar(player, msg - .replace("%uses%", String.valueOf(uses)) - .replace("%balance%", econ.format(response.balance)) - .replace("%price%", econ.format(response.amount))); - } - } - - // Build log message - String coords = Math.round(player.getLocation().x()) + " " + Math.round(player.getLocation().y()) + " " + Math.round(player.getLocation().z()); - String usesLeft = (uses == Integer.MAX_VALUE) ? "i" : String.valueOf(uses); - writeLog(player.getName() + " (" + player.getUniqueId() + ") sold $" + Math.round(response.amount) + " ($" + Math.round(response.balance) + ") at " + coords + " in " + player.getWorld().getName() + " with " + usesLeft + " uses left"); - - if (uses <= 0) { - player.getInventory().removeItem(sellStick); - - if (sendInChat) { - ChatUtils.sendMsg(player, SellstickConfig.brokenStick, true); - } else { - ChatUtils.sendActionBar(player, SellstickConfig.brokenStick); - } - } - - return true; - } - - static double setMultiplier(Player player) { - /* - * Permissions based multiplier check. If user doesn't have access to - * sellstick.multiplier.x permission Multiplier defaults to 1 as seen below. - */ - double multiplier = 1; + /** + * Get the multiplier from the players permission node sellstick.mutiplier.x. + * If the player does not have a multiplier, defaults to 1. + * + * @param player The player to check the multiplier for. + * @return The multiplier as a double. + */ + public static double getPlayersMultiplier(Player player) { + double multiplier = 1d; for (PermissionAttachmentInfo perm : player.getEffectivePermissions()) { if (perm.getPermission().startsWith("sellstick.multiplier")) { @@ -216,31 +114,4 @@ static double setMultiplier(Player player) { } return multiplier; } - - static void writeLog(String message) { - try { - // Get date + time - Date calender = Calendar.getInstance().getTime(); - String time = new SimpleDateFormat("dd-MM-yy HH:mm:ss").format(calender); - String date = time.substring(0, 8); - - // Get / create log folder - File logFolder = new File(SellStick.getInstance().getDataFolder() + File.separator + "logs"); - if (!logFolder.exists()) logFolder.mkdir(); - - // Get / create log file - File saveTo = new File(logFolder,date + ".log"); - if (!saveTo.exists()) saveTo.createNewFile(); - - // Write log to file - FileWriter fw = new FileWriter(saveTo, true); - PrintWriter pw = new PrintWriter(fw); - pw.println(time + " | " + message); - pw.close(); - - } catch (IOException e) { - ChatUtils.log(Level.SEVERE, e.toString()); - e.printStackTrace(); - } - } } diff --git a/src/main/java/com/shmkane/sellstick/utilities/ItemUtils.java b/src/main/java/com/shmkane/sellstick/utilities/ItemUtils.java deleted file mode 100644 index 1ee891d..0000000 --- a/src/main/java/com/shmkane/sellstick/utilities/ItemUtils.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.shmkane.sellstick.utilities; - -import com.shmkane.sellstick.configs.SellstickConfig; -import de.tr7zw.changeme.nbtapi.NBT; -import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; -import de.tr7zw.changeme.nbtapi.iface.ReadableNBT; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemFlag; -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 ItemUtils { - - private static final UUID uuid = UUID.fromString("c5faa888-4b14-11ee-be56-0242ac120002"); - - // Make an ItemStack Glow - public static ItemStack glow(ItemStack itemStack) { - itemStack.addUnsafeEnchantment(Enchantment.FORTUNE, 1); - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); - itemStack.setItemMeta(itemMeta); - return itemStack; - } - - // Check if an ItemStack is infinite - public static boolean isInfinite(ItemStack itemStack) { - - ReadableNBT nbtItemStack = NBT.readNbt(itemStack); - - return nbtItemStack.getBoolean("Infinite"); - } - - // Set an Itemstack with a NBT Tag of Infinite with a state - public static ReadWriteNBT setInfinite(ItemStack itemStack) { - - ReadWriteNBT nbtItemStack = NBT.itemStackToNBT(itemStack); - - nbtItemStack.setBoolean("Infinite", true); - nbtItemStack.setInteger("UsesRemaining", Integer.MAX_VALUE); - - return nbtItemStack; - } - - // Get uses Remaining from a SellStick - public static int getUses(ItemStack itemStack) { - ReadableNBT nbtItemStack = NBT.readNbt(itemStack); - return nbtItemStack.getInteger("UsesRemaining"); - } - - // Set uses to a SellStick - public static ItemStack setUses(ItemStack itemStack, int uses) { - - // Update ItemMeta - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.lore(setLoreList(uses)); - itemStack.setItemMeta(itemMeta); - - // NBT - NBT.modify(itemStack, nbt -> { - nbt.setInteger("UsesRemaining", uses); - nbt.setBoolean("Infinite", (uses == Integer.MAX_VALUE)); - }); - - return itemStack; - } - - // Subtract a use from a SellStick - public static ItemStack subtractUses(ItemStack itemStack) { - - // Skip if infinite - if (isInfinite(itemStack)) return itemStack; - - // Subtract use - int newUses = getUses(itemStack) - 1; - NBT.modify(itemStack, nbt -> { - nbt.setInteger("UsesRemaining", newUses); - }); - - // Update Uses on Lore - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.lore(setLoreList(newUses)); - itemStack.setItemMeta(itemMeta); - return itemStack; - - } - - public static ItemStack setSellStick(ItemStack itemStack) { - - NBT.modify(itemStack, nbt -> { - nbt.setString("SellStickUUID", uuid.toString()); - - //nbt.setString("RandomSSUUID", UUID.randomUUID().toString()); // Make it non stackable - }); - - return itemStack; - } - - @Deprecated - public static boolean isSellStick(ItemStack itemStack) { - boolean matchUUID = matchSellStickUUID(itemStack); - boolean matchMaterial = matchSellStickMaterial(itemStack); - - return (matchUUID && matchMaterial); - } - - public static boolean matchSellStickUUID(ItemStack itemStack) { - ReadableNBT nbtItemStack = NBT.readNbt(itemStack); - return nbtItemStack.getString("SellStickUUID").equals(uuid.toString()); - } - - public static boolean matchSellStickMaterial(ItemStack itemStack) { - return itemStack.getType().equals(SellstickConfig.material); - } - - public static List setLoreList(int uses){ - List loreList = new ArrayList<>(); - for(String loreLine : SellstickConfig.lore) { - loreList.add(MiniMessage.miniMessage().deserialize(loreLine)); - } - if(uses == Integer.MAX_VALUE){ - loreList.add(MiniMessage.miniMessage().deserialize(SellstickConfig.infiniteLore)); - } else { - loreList.add(MiniMessage.miniMessage().deserialize(SellstickConfig.finiteLore.replace("%remaining%", String.valueOf(uses)))); - } - return loreList; - } - -} diff --git a/src/main/java/com/shmkane/sellstick/utilities/MergeUtils.java b/src/main/java/com/shmkane/sellstick/utilities/MergeUtils.java index 17bc6e8..def891c 100644 --- a/src/main/java/com/shmkane/sellstick/utilities/MergeUtils.java +++ b/src/main/java/com/shmkane/sellstick/utilities/MergeUtils.java @@ -1,5 +1,6 @@ package com.shmkane.sellstick.utilities; +import com.shmkane.sellstick.stick.StickHandler; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -9,6 +10,7 @@ import java.util.List; public class MergeUtils { + // Search through player inventory to get all sellsticks public static ItemStack[] searchInventory(Player player) { // Get inventory @@ -18,7 +20,7 @@ public static ItemStack[] searchInventory(Player player) { // Search through inventory to find all sellsticks and store in sellsticks list for (ItemStack item : inventory.getContents()) { - if (item != null && ItemUtils.matchSellStickUUID(item)) { + if (item != null && StickHandler.hasSellStickNBT(item)) { int amount = item.getAmount(); // Account for multiple sellsticks in same slot @@ -36,10 +38,10 @@ public static ItemStack[] searchInventory(Player player) { } // Sort sellsticks by their uses - public static ItemStack[] sortSellsticksByUses(ItemStack[] sellsticks) { + public static ItemStack[] sortSellSticksByUses(ItemStack[] sellsticks) { List sortedSellsticks = new ArrayList<>(List.of(sellsticks)); - sortedSellsticks.sort(Comparator.comparingInt(ItemUtils::getUses)); + sortedSellsticks.sort(Comparator.comparingInt(StickHandler::getUses)); return sortedSellsticks.toArray(new ItemStack[0]); } @@ -50,7 +52,7 @@ public static int sumSellStickUses(ItemStack[] sortedSellsticks, int maxAmount) // Sum the uses of all sellsticks in sellsticks array for (ItemStack sellstick : sortedSellsticks) { - int uses = ItemUtils.getUses(sellstick); + int uses = StickHandler.getUses(sellstick); if (usesSum + uses <= maxAmount) { usesSum += uses; } else { @@ -62,14 +64,14 @@ public static int sumSellStickUses(ItemStack[] sortedSellsticks, int maxAmount) } // Remove all sorted sellsticks from player inventory - public static void removeSortedSellsticks(Player player, ItemStack[] sortedSellsticks, int maxAmount) { + public static void removeSortedSellSticks(Player player, ItemStack[] sortedSellsticks, int maxAmount) { // Get inventory Inventory inventory = player.getInventory(); int usesSum = 0; // Remove sellsticks which are less than maxAmount for (ItemStack sellstick : sortedSellsticks) { - int uses = ItemUtils.getUses(sellstick); + int uses = StickHandler.getUses(sellstick); if (usesSum + uses <= maxAmount) { usesSum += uses; // Sum the uses before removing the sellstick diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8101a4f..5ebcebd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -3,50 +3,52 @@ PriceSource: PricesYML Item: - DisplayName: ✦ SellStick + DisplayName: ✦ SellStick # Set the Material of the SellStick # https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html for Materials Material: STICK - StickLore: - - Right click on a chest to sell items inside! Glow: true # Dependent on if item is infinite - # Use \n for new lines - FiniteLore: %remaining% remaining uses - InfiniteLore: Infinite uses! + FiniteLore: + - Right click on a chest to sell items inside! + - %remaining% remaining uses + InfiniteLore: + - Right click on a chest to sell items inside! + - Infinite uses! MaxAmount: 2000 UseSound: true + UseParticles: true Messages: PluginPrefix: '[SellStick] ' - + # When they sell the items what message should be displayed # Placeholders: # %price% - for how much they gained # %balance% - to show new balance SellMessage: You sold items for %price% and now have %balance% - + # No Permission Message NoPermissionMessage: Sorry, you don't have permission for this! - + # No Territory Access InvalidTerritoryMessage: You can't use sell stick here! - + # Empty Chest NotWorthMessage: Nothing worth selling inside - + # SellStick Break Message BrokenStick: Your sellstick broke! (Ran out of uses) - + # Unrelated Action NonSellingRelated: Right click a chest! - + # The message to send to the Admin who gave the sticks # Placeholders: # %player% - person who will get the sellstick # %amount% - amount of SellSticks that %player% will receive GiveMessage: You gave %player% %amount% SellSticks! - + # Message to send to the player who received the sticks # Placeholders: # %amount% - money that the seller receives. diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 15efd8f..594f5d6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,18 +1,12 @@ name: SellStick main: com.shmkane.sellstick.SellStick -version: "${project.version}" -api-version: 1.20 -author: shmkane, TreemanKing, 1UnderTheSun +version: ${version} +api-version: 1.21 +author: shmkane, TreemanKing, 1UnderTheSun, CodfishBender website: https://github.com/shmkane/SellStick -depend: [Essentials, Vault] +depend: [Essentials, Vault, CommandAPI] softdepend: [ShopGuiPlus] -commands: - sellstick: - description: Sell all items in a chest, barrel or shulker box easily! - aliases: [ss] - permission: sellstick.use - permissions: sellstick.reload: default: op