commit 1669a114bcdc0168e123340931dd18cde2c50321 Author: Len <40720638+destro174@users.noreply.github.com> Date: Sat Feb 3 21:58:02 2024 +0100 Essentia plugin Basic plugin with some essential utilities and commands. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67ae6e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# +.idea + +.gradle/ +build/ + +# Eclipse stuff +.classpath +.project +.settings/ + +# VSCode stuff +.vscode/ + +# netbeans +nbproject/ +nbactions.xml + +# we use maven! +build.xml + +# maven +target/ +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +bin/ +dist/ +manifest.mf + +# Mac filesystem dust +.DS_Store/ +.DS_Store + +# intellij +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Linux temp files +*~ + +!gradle/wrapper/gradle-wrapper.jar diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..54966ac --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,20 @@ +pipeline { + agent any + stages { + stage('Gradle') { + steps { + sh './gradlew build' + } + } + stage('Archive') { + steps { + archiveArtifacts artifacts: 'build/libs/', followSymlinks: false + } + } + stage('discord') { + steps { + discordSend description: "Build: ${BUILD_NUMBER}", showChangeset: true, result: currentBuild.currentResult, title: currentBuild.fullProjectName, webhookURL: env.discordwebhook + } + } + } +} \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts new file mode 100644 index 0000000..14e5b19 --- /dev/null +++ b/api/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + `maven-publish` +} + +dependencies { + compileOnly("com.alttd:Comet-API:1.20.4-R0.1-SNAPSHOT") +} + +tasks { + jar { + archiveFileName.set("${rootProject.name}-${project.name}.jar") + } +} + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + } + } + + repositories{ + maven { + name = "maven" + url = uri("https://repo.destro.xyz/snapshots") + credentials(PasswordCredentials::class) + } + } +} \ No newline at end of file diff --git a/api/src/main/java/com/alttd/essentia/EssentiaAPI.java b/api/src/main/java/com/alttd/essentia/EssentiaAPI.java new file mode 100644 index 0000000..b9d0df2 --- /dev/null +++ b/api/src/main/java/com/alttd/essentia/EssentiaAPI.java @@ -0,0 +1,22 @@ +package com.alttd.essentia; + +import org.jetbrains.annotations.ApiStatus; + +public interface EssentiaAPI { + + static EssentiaAPI get() { + return Provider.instance; + } + + final class Provider { + private static EssentiaAPI instance = null; + + @ApiStatus.Internal + static void register(EssentiaAPI instance) { + if (Provider.instance != null) + throw new UnsupportedOperationException("Cannot redefine singleton"); + + Provider.instance = instance; + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..b2139ef --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,97 @@ +import java.io.FileOutputStream +import java.net.URL +import java.io.ByteArrayOutputStream + +plugins { + id("java") + id("java-library") + id("com.github.johnrengelman.shadow") version "8.1.1" + id("maven-publish") +} +allprojects { + group = "com.alttd.essentia" + version = "Build-" + (System.getenv("BUILD_NUMBER") ?: gitCommit()) + description = "Altitude essentials ;)" +} + +subprojects { + apply() + apply(plugin = "maven-publish") + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } +} + +dependencies { + implementation(project(":api")) + implementation(project(":plugin")) +} + +tasks { + shadowJar { + archiveFileName.set("${project.name}.jar") + minimize() { + exclude { + it.moduleName == "api" + } + exclude { + it.moduleName == "plugin" + } + } + listOf( + "xyz.destro.utheo.comet" + ).forEach { relocate(it, "${rootProject.group}.lib.${it.substringAfterLast(".")}") } + } + + build { + dependsOn(shadowJar) + } + + jar { + enabled = false +// archiveFileName.set("${rootProject.name}.jar") + } +} + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + } + } + + configure { + repositories { + maven { + name = "maven" + url = uri("https://repo.destro.xyz/snapshots/") + credentials(PasswordCredentials::class) + } + } + } +} + +dependencies { + compileOnly("com.alttd:Comet-API:1.20.4-R0.1-SNAPSHOT") +} + +fun gitCommit(): String { + val os = ByteArrayOutputStream() + project.exec { + isIgnoreExitValue = true + commandLine = "git rev-parse --short HEAD".split(" ") + standardOutput = os + } + return "git-" + String(os.toByteArray()).trim() +} + +fun download(link: String, path: String) { + URL(link).openStream().use { input -> + FileOutputStream(File(path)).use { output -> + input.copyTo(output) + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 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..ffb3de8 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Feb 02 09:46:57 CET 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/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. +# + +############################################################################## +# +# 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/master/subprojects/plugins/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 + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# 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"' + +# 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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# 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 + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + 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 + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# 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..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@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 + +@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=. +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%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts new file mode 100644 index 0000000..c8834d3 --- /dev/null +++ b/plugin/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + `maven-publish` +// id("net.minecrell.plugin-yml.bukkit") version "0.6.0" +} + +dependencies { + implementation(project(":api")) + compileOnly("com.alttd:Comet-API:1.20.4-R0.1-SNAPSHOT") + + compileOnly("org.projectlombok:lombok:1.18.24") + annotationProcessor("org.projectlombok:lombok:1.18.24") +} + +tasks { + jar { + archiveFileName.set("${rootProject.name}-${project.name}.jar") + } + + processResources { + filteringCharset = Charsets.UTF_8.name() + duplicatesStrategy = DuplicatesStrategy.INCLUDE + filesMatching("plugin.yml") { + expand(Pair("version", rootProject.version)) + } + } +} + +//bukkit { +// name = rootProject.name +// main = "$group.${rootProject.name}Plugin" +// version = "${rootProject.version}" +// apiVersion = "1.20" +// authors = listOf("destro174") +//} \ No newline at end of file diff --git a/plugin/lombok.config b/plugin/lombok.config new file mode 100644 index 0000000..18f74be --- /dev/null +++ b/plugin/lombok.config @@ -0,0 +1 @@ +lombok.accessors.fluent = true \ No newline at end of file diff --git a/plugin/src/main/java/com/alttd/essentia/EssentiaPlugin.java b/plugin/src/main/java/com/alttd/essentia/EssentiaPlugin.java new file mode 100644 index 0000000..0afda65 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/EssentiaPlugin.java @@ -0,0 +1,67 @@ +package com.alttd.essentia; + +import com.alttd.essentia.commands.admin.*; +import com.alttd.essentia.commands.player.*; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.listeners.PlayerListener; +import lombok.Getter; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.slf4j.Logger; + +import java.nio.file.Path; + +public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI { + + @Getter + private static EssentiaPlugin instance; + + @Override + public void onLoad() { + instance = this; + EssentiaAPI.Provider.register(instance); + } + + @Override + public void onEnable() { + loadConfiguration(); + loadCommands(); + loadEventListeners(); + } + + @Override + public void onDisable() { + getServer().getScheduler().cancelTasks(this); + } + + public void loadConfiguration() { + Config.init(); + } + + public void loadCommands() { + getCommand("essentia").setExecutor(new EssentiaCommand(this)); + getCommand("teleportaccept").setExecutor(new TeleportAcceptCommand(this)); + getCommand("teleportdeny").setExecutor(new TeleportDenyCommand(this)); + getCommand("teleportrequest").setExecutor(new TeleportRequestCommand(this)); + getCommand("teleportrequesthere").setExecutor(new TeleportRequestHereCommand(this)); + getCommand("teleporttoggle").setExecutor(new TeleportToggleCommand(this)); + getCommand("clearinventory").setExecutor(new ClearInventoryCommand(this)); + getCommand("home").setExecutor(new HomeCommand(this)); + getCommand("homes").setExecutor(new HomeListCommand(this)); + getCommand("sethome").setExecutor(new SetHomeCommand(this)); + getCommand("deletehome").setExecutor(new DelHomeCommand(this)); + getCommand("back").setExecutor(new BackCommand(this)); + getCommand("deathback").setExecutor(new DeathBackCommand(this)); + getCommand("fly").setExecutor(new FlyCommand(this)); + getCommand("gamemode").setExecutor(new GamemodeCommand(this)); + getCommand("heal").setExecutor(new HealCommand(this)); + getCommand("feed").setExecutor(new FeedCommand(this)); + getCommand("enchant").setExecutor(new EnchantCommand(this)); + } + + public void loadEventListeners() { + final PluginManager pluginManager = getServer().getPluginManager(); + pluginManager.registerEvents(new PlayerListener(this), this); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/AdminSubCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/AdminSubCommand.java new file mode 100644 index 0000000..5ad587e --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/AdminSubCommand.java @@ -0,0 +1,13 @@ +package com.alttd.essentia.commands; + +import com.alttd.essentia.EssentiaPlugin; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public abstract class AdminSubCommand extends SubCommand { + + protected AdminSubCommand(EssentiaPlugin plugin, String name, String... aliases) { + super(plugin, name, aliases); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/PlayerSubCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/PlayerSubCommand.java new file mode 100644 index 0000000..d21dfdc --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/PlayerSubCommand.java @@ -0,0 +1,26 @@ +package com.alttd.essentia.commands; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public abstract class PlayerSubCommand extends SubCommand { + + protected PlayerSubCommand(EssentiaPlugin plugin, String name, String... aliases) { + super(plugin, name, aliases); + } + + @Override + public boolean execute(CommandSender sender, String... args) { + if (!(sender instanceof Player player)) { + sender.sendRichMessage(Config.PLAYER_ONLY_COMMAND); + return true; + } + + return execute(player, PlayerConfig.getConfig(player), args); + } + + protected abstract boolean execute(Player player, PlayerConfig playerConfig, String... args); +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/SubCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/SubCommand.java new file mode 100644 index 0000000..95fcb3c --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/SubCommand.java @@ -0,0 +1,70 @@ +package com.alttd.essentia.commands; + +import com.alttd.essentia.EssentiaPlugin; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public abstract class SubCommand implements TabExecutor { + + protected EssentiaPlugin plugin; + private final String name; + private final String[] aliases; + private final Map subCommands = new LinkedHashMap<>(); + + protected SubCommand(EssentiaPlugin plugin, String name, String... aliases) { + this.plugin = plugin; + this.name = name; + this.aliases = aliases; + } + + protected abstract boolean execute(CommandSender sender, String... args); + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { + if (args.length > 0) { + SubCommand subCommand = getSubCommand(args[0]); + if (subCommand != null) { + return subCommand.onCommand(sender, cmd, args[0], Arrays.copyOfRange(args, 1, args.length)); + } + } + return execute(sender, args); + } + + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { + if (args.length == 0) { + return subCommands.keySet().stream() + .sorted(String::compareToIgnoreCase) + .collect(Collectors.toList()); + } + + SubCommand subCommand = getSubCommand(args[0]); + if (subCommand != null) { + return subCommand.onTabComplete(sender, command, args[0], Arrays.copyOfRange(args, 1, args.length)); + } else if (args.length == 1) { + return subCommands.keySet().stream() + .filter(s -> s.toLowerCase().startsWith(args[0])) + .sorted(String::compareToIgnoreCase) + .collect(Collectors.toList()); + } + return null; + } + + public void registerSubCommand(SubCommand subCommand) { + subCommands.put(subCommand.name.toLowerCase(), subCommand); + for (String alias : subCommand.aliases) { + subCommands.putIfAbsent(alias.toLowerCase(), subCommand); + } + } + + private SubCommand getSubCommand(String name) { + return subCommands.get(name.toLowerCase()); + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/ClearInventoryCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/ClearInventoryCommand.java new file mode 100644 index 0000000..2ef1069 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/ClearInventoryCommand.java @@ -0,0 +1,49 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import com.alttd.essentia.configuration.Config; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class ClearInventoryCommand extends AdminSubCommand { + + public ClearInventoryCommand(EssentiaPlugin plugin) { + super(plugin, "clearinventory"); + // TODO - register clear other subcommand + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + if (args.length > 0) { // TODO - make this into a subcommand + if (!sender.hasPermission("essentia.command.clearinventory.other")) { + sender.sendRichMessage(Config.COMMAND_NO_PERMISSION); + return true; + } + Player player = Bukkit.getPlayer(args[0]); + if (player == null) { + sender.sendRichMessage(Config.PLAYER_NOT_FOUND); + return true; + } + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("requester", sender.name()), + Placeholder.component("target", player.displayName()) + ); + sender.sendRichMessage(Config.PLAYER_INVENTORY_CLEARED, placeholders); + player.sendRichMessage(Config.INVENTORY_CLEARED_BY_OTHER, placeholders); + player.getInventory().clear(); + return true; + } + if (!(sender instanceof Player player)) { + sender.sendRichMessage(Config.PLAYER_ONLY_COMMAND); + return true; + } + player.getInventory().clear(); + player.sendRichMessage(Config.INVENTORY_CLEARED); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/EnchantCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/EnchantCommand.java new file mode 100644 index 0000000..32273f6 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/EnchantCommand.java @@ -0,0 +1,38 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import org.bukkit.NamespacedKey; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class EnchantCommand extends AdminSubCommand { + + public EnchantCommand(EssentiaPlugin plugin) { + super(plugin, "enchant"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + // TODO + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String @NotNull [] args) { + if (args.length == 1) { + return Arrays.stream(Enchantment.values()) + .map(Enchantment::getKey) + .map(NamespacedKey::getKey) + .filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase())) + .collect(Collectors.toList()); + } + return null; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/EssentiaCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/EssentiaCommand.java new file mode 100644 index 0000000..73fb4fa --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/EssentiaCommand.java @@ -0,0 +1,19 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.SubCommand; +import org.bukkit.command.CommandSender; + +public class EssentiaCommand extends SubCommand { + + public EssentiaCommand(EssentiaPlugin plugin) { + super(plugin, "essentia"); + + registerSubCommand(new ReloadCommand(plugin)); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/FeedCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/FeedCommand.java new file mode 100644 index 0000000..c85f1af --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/FeedCommand.java @@ -0,0 +1,41 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import com.alttd.essentia.configuration.Config; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class FeedCommand extends AdminSubCommand { + + public FeedCommand(EssentiaPlugin plugin) { + super(plugin, "feed"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + Player target = args.length > 0 ? org.bukkit.Bukkit.getPlayer(args[0]) : sender instanceof Player player ? player : null; + if (target == null) { + sender.sendRichMessage(Config.PLAYER_NOT_FOUND); + return true; + } + if (!sender.hasPermission("essentia.command.feed" + (target != sender ? ".other" : "")) ) { + sender.sendRichMessage(Config.COMMAND_NO_PERMISSION); + return true; + } + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("requester", sender.name()), + Placeholder.component("target", target.displayName()) + ); + + target.setFoodLevel(20); + target.setSaturation(20); + sender.sendRichMessage(target == sender ? Config.FEED_SELF : Config.FEED_OTHER, placeholders); + if (target != sender) + target.sendRichMessage(Config.FEED_BY_OTHER, placeholders); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/FlyCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/FlyCommand.java new file mode 100644 index 0000000..0b742f7 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/FlyCommand.java @@ -0,0 +1,61 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import com.alttd.essentia.configuration.Config; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.apache.commons.lang3.BooleanUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class FlyCommand extends AdminSubCommand { + + public FlyCommand(EssentiaPlugin plugin) { + super(plugin, "fly"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + if (args.length > 0) { + if (!sender.hasPermission("essentia.command.fly.other")) { + sender.sendRichMessage(Config.COMMAND_NO_PERMISSION); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendRichMessage(Config.PLAYER_NOT_FOUND); + return true; + } + target.setAllowFlight(!target.getAllowFlight()); + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("player", sender.name()), + Placeholder.component("target", target.name()), + Placeholder.unparsed("status", BooleanUtils.toStringOnOff(target.getAllowFlight())) + ); + sender.sendRichMessage(Config.TOGGLED_FLIGHT_BY_OTHER, placeholders); + target.sendRichMessage(Config.TOGGLED_FLIGHT_PLAYER, placeholders); + return true; + } + if (!(sender instanceof Player player)) { + sender.sendRichMessage(Config.PLAYER_ONLY_COMMAND); + return true; + } + + player.setAllowFlight(!player.getAllowFlight()); + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("player", player.name()), + Placeholder.parsed("status", BooleanUtils.toStringOnOff(player.getAllowFlight())) + ); + sender.sendRichMessage(Config.TOGGLED_FLIGHT, placeholders); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/GamemodeCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/GamemodeCommand.java new file mode 100644 index 0000000..e325ad2 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/GamemodeCommand.java @@ -0,0 +1,66 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import com.alttd.essentia.configuration.Config; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class GamemodeCommand extends AdminSubCommand { + + public GamemodeCommand(EssentiaPlugin plugin) { + super(plugin, "gamemode"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + // TODO -- refactor all "other" subcommands to follow this style, cleaner and easier? + Player target = args.length > 1 ? Bukkit.getPlayer(args[1]) : sender instanceof Player player ? player : null; + if (target == null) { + sender.sendRichMessage(Config.PLAYER_NOT_FOUND); + return true; + } + + if (!sender.hasPermission("essentia.command.gamemode" + (target != sender ? ".other" : "")) ) { + sender.sendRichMessage(Config.COMMAND_NO_PERMISSION); + return true; + } + + GameMode gameMode = GameMode.SURVIVAL; + switch (args[0].toLowerCase()) { + case "creative", "c" -> gameMode = GameMode.CREATIVE; + case "spectator", "sp" -> gameMode = GameMode.SPECTATOR; + } + target.setGameMode(gameMode); + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("requester", sender.name()), + Placeholder.component("target", target.displayName()), + Placeholder.unparsed("gamemode", gameMode.toString()) + ); + sender.sendRichMessage(target == sender ? Config.GAMEMODE_SET : Config.GAMEMODE_SET_OTHER, placeholders); + if (target != sender) + target.sendRichMessage(Config.GAMEMODE_SET_BY_OTHER, placeholders); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + String name = args[0].trim().toLowerCase(); + return Arrays.stream(GameMode.values()).map(GameMode::toString) + .filter(string -> string.toLowerCase().startsWith(name)).collect(Collectors.toList()); + } + return null; + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/HealCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/HealCommand.java new file mode 100644 index 0000000..1fcd814 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/HealCommand.java @@ -0,0 +1,39 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import com.alttd.essentia.configuration.Config; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class HealCommand extends AdminSubCommand { + + public HealCommand(EssentiaPlugin plugin) { + super(plugin, "heal"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + Player target = args.length > 0 ? org.bukkit.Bukkit.getPlayer(args[0]) : sender instanceof Player player ? player : null; + if (target == null) { + sender.sendRichMessage(Config.PLAYER_NOT_FOUND); + return true; + } + if (!sender.hasPermission("essentia.command.heal" + (target != sender ? ".other" : "")) ) { + sender.sendRichMessage(Config.COMMAND_NO_PERMISSION); + return true; + } + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("requester", sender.name()), + Placeholder.component("target", target.displayName()) + ); + target.setHealth(20); + sender.sendRichMessage(target == sender ? Config.HEAL_SELF : Config.HEAL_OTHER, placeholders); + if (target != sender) + target.sendRichMessage(Config.HEAL_BY_OTHER, placeholders); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/admin/ReloadCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/admin/ReloadCommand.java new file mode 100644 index 0000000..49edc6a --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/admin/ReloadCommand.java @@ -0,0 +1,19 @@ +package com.alttd.essentia.commands.admin; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.AdminSubCommand; +import org.bukkit.command.CommandSender; + +public class ReloadCommand extends AdminSubCommand { + + public ReloadCommand(EssentiaPlugin plugin) { + super(plugin, "reload"); + } + + @Override + protected boolean execute(CommandSender sender, String... args) { + plugin.loadConfiguration(); + return true; + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/BackCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/BackCommand.java new file mode 100644 index 0000000..19d4c1c --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/BackCommand.java @@ -0,0 +1,34 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.tasks.TeleportSounds; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class BackCommand extends PlayerSubCommand { + + public BackCommand(EssentiaPlugin plugin) { + super(plugin, "back"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + Location back = playerConfig.getBackLocation(false); + + if (back == null) { + player.sendRichMessage(Config.NO_BACK_LOCATION); + return true; + } + + new TeleportSounds(back, player.getLocation()) + .runTaskLater(plugin, 1); + + player.teleportAsync(back).thenAccept(result -> + player.sendRichMessage(Config.TELEPORTING_BACK)); + return true; + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/DeathBackCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/DeathBackCommand.java new file mode 100644 index 0000000..460ec25 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/DeathBackCommand.java @@ -0,0 +1,34 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.tasks.TeleportSounds; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class DeathBackCommand extends PlayerSubCommand { + + public DeathBackCommand(EssentiaPlugin plugin) { + super(plugin, "back"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + Location back = playerConfig.getBackLocation(true); + + if (back == null) { + player.sendRichMessage(Config.NO_DEATH_LOCATION); + return true; + } + + new TeleportSounds(back, player.getLocation()) + .runTaskLater(plugin, 1); + + player.teleportAsync(back).thenAccept(result -> + player.sendRichMessage(Config.TELEPORTING_BACK_DEATH)); + return true; + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/DelHomeCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/DelHomeCommand.java new file mode 100644 index 0000000..fb9afce --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/DelHomeCommand.java @@ -0,0 +1,36 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; + +public class DelHomeCommand extends PlayerSubCommand { + + public DelHomeCommand(EssentiaPlugin plugin) { + super(plugin, "deletehome"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + // TODO -- subcommand to remove other player homes +// if (args.length > 1) { +// if (!player.hasPermission("essentia.command.delhome.other")) { +// return true; +// } +// } + + String home = (args.length > 0) ? args[0] : "home"; + if (playerConfig.getHome(home) == null) { + player.sendRichMessage(Config.HOME_DOES_NOT_EXIST, Placeholder.unparsed("home", home)); + return true; + } + + playerConfig.setHome(home, null); + player.sendRichMessage(Config.HOME_DELETED, Placeholder.unparsed("home", home)); + return true; + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/HomeCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/HomeCommand.java new file mode 100644 index 0000000..665b401 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/HomeCommand.java @@ -0,0 +1,78 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.tasks.TeleportSounds; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class HomeCommand extends PlayerSubCommand { + + public HomeCommand(EssentiaPlugin plugin) { + super(plugin, "home"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + String home = null; + if (args.length == 0) { + int count = playerConfig.getHomeCount(); + if (count == 0) { + if (player.getBedSpawnLocation() != null) { + home = "bed"; + } + } else if (count == 1) { + home = playerConfig.getConfigurationSection("home").getKeys(false) + .stream().findFirst().orElse(null); + } else { + player.sendRichMessage(Config.SPECIFY_HOME, Placeholder.unparsed("homelist", String.join(", ", playerConfig.getHomeList()))); + return true; + } + if (home == null || home.isEmpty()) { + player.sendRichMessage(Config.HOME_NOT_SET); + return true; + } + return true; + } else { + home = args[0]; + } + // TODO - subcommand to teleport to others homes +// if (args.length > 1) { +// if (!player.hasPermission("essentia.command.home.other")) { +// return true +// } +// } + + Location homeLoc = home.equalsIgnoreCase("bed") ? + player.getBedSpawnLocation() : playerConfig.getHome(home); + if (homeLoc == null) { + player.sendRichMessage(Config.HOME_DOES_NOT_EXIST); + return true; + } + + new TeleportSounds(homeLoc, player.getLocation()) + .runTaskLater(plugin, 1); + + String homeName = home; + player.teleportAsync(homeLoc).thenAccept(result -> + player.sendRichMessage(Config.HOME_TELEPORT, Placeholder.unparsed("home", homeName)) + ); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + return PlayerConfig.getConfig((Player) sender).getMatchingHomeNames(args[0]); + } + return null; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/HomeListCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/HomeListCommand.java new file mode 100644 index 0000000..30b7fe2 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/HomeListCommand.java @@ -0,0 +1,29 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; + +public class HomeListCommand extends PlayerSubCommand { + + public HomeListCommand(EssentiaPlugin plugin) { + super(plugin, "homes"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + // TODO - subcommand to list other homes +// if (args.length > 0) { +// if (!player.hasPermission("essentia.command.homes.other")) { +// return true; +// } +// } + + // TODO - clickable homes that run /home + player.sendRichMessage(Config.HOME_LIST, Placeholder.unparsed("homelist", String.join(", ", playerConfig.getHomeList()))); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/SetHomeCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/SetHomeCommand.java new file mode 100644 index 0000000..840b32a --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/SetHomeCommand.java @@ -0,0 +1,43 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; + +public class SetHomeCommand extends PlayerSubCommand { + + + public SetHomeCommand(EssentiaPlugin plugin) { + super(plugin, "sethome"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + // TODO -- subcommand to allow setting other player homes +// if (args.length > 1) { +// if (!player.hasPermission("essentia.command.sethome.other")) { +// return true; +// } +// } + + String home = (args.length > 0) ? args[0] : "home"; + if (home.equalsIgnoreCase("bed") || home.contains(".")) { + player.sendRichMessage(Config.INVALID_HOME_NAME); + return true; + } + + int limit = 5; // TODO -- player home limits hardcoded for now + int count = playerConfig.getHomeCount(); + if (limit >= 0 && count >= limit) { + player.sendRichMessage(Config.HOME_SET_MAX, Placeholder.unparsed("limit", String.valueOf(limit))); + return true; + } + + playerConfig.setHome(home, player.getLocation()); + player.sendRichMessage(Config.HOME_SET, Placeholder.unparsed("home", home)); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportAcceptCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportAcceptCommand.java new file mode 100644 index 0000000..561a336 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportAcceptCommand.java @@ -0,0 +1,27 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.request.Request; +import org.bukkit.entity.Player; + +public class TeleportAcceptCommand extends PlayerSubCommand { + + public TeleportAcceptCommand(EssentiaPlugin plugin) { + super(plugin, "teleportaccept"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + Request request = playerConfig.request(); + if (request == null) { + player.sendRichMessage(Config.NO_PENDING_REQUESTS); + return true; + } + + request.accept(); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportDenyCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportDenyCommand.java new file mode 100644 index 0000000..ff7ec73 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportDenyCommand.java @@ -0,0 +1,27 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.request.Request; +import org.bukkit.entity.Player; + +public class TeleportDenyCommand extends PlayerSubCommand { + + public TeleportDenyCommand(EssentiaPlugin plugin) { + super(plugin, "teleportdeny"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + Request request = playerConfig.request(); + if (request == null) { + player.sendRichMessage(Config.NO_PENDING_REQUESTS); + return true; + } + + request.deny(); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestCommand.java new file mode 100644 index 0000000..daa1efb --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestCommand.java @@ -0,0 +1,72 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.request.TeleportRequest; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class TeleportRequestCommand extends PlayerSubCommand { + + public TeleportRequestCommand(EssentiaPlugin plugin) { + super(plugin, "teleportrequest"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + if (args.length < 1) { + player.sendRichMessage(Config.NO_PLAYER_SPECIFIED); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + player.sendRichMessage(Config.PLAYER_NOT_ONLINE); + return true; + } + + if (target == player) { + player.sendRichMessage(Config.REQUEST_TO_SELF); + return true; + } + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("target", target.displayName()) + ); + + PlayerConfig targetConfig = PlayerConfig.getConfig(target); + if (targetConfig.request() != null) { + player.sendRichMessage(Config.TARGET_HAS_PENDING_REQUEST, placeholders); + return true; + } + + if (!targetConfig.allowTeleports()) { + player.sendRichMessage(Config.TELEPORT_TOGGLED_OFF, placeholders); + return true; + } + + targetConfig.request(new TeleportRequest(plugin, player, target)); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + String name = args[0].trim().toLowerCase(); + return Bukkit.getOnlinePlayers().stream() + .map(Player::getName) + .filter(playerName -> playerName.toLowerCase().startsWith(name)).collect(Collectors.toList()); + } + return null; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestHereCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestHereCommand.java new file mode 100644 index 0000000..82a6615 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportRequestHereCommand.java @@ -0,0 +1,73 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.request.TeleportHereRequest; +import com.alttd.essentia.request.TeleportRequest; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class TeleportRequestHereCommand extends PlayerSubCommand { + + public TeleportRequestHereCommand(EssentiaPlugin plugin) { + super(plugin, "teleportrequesthere"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + if (args.length < 1) { + player.sendRichMessage(Config.NO_PLAYER_SPECIFIED); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + player.sendRichMessage(Config.PLAYER_NOT_ONLINE); + return true; + } + + if (target == player) { + player.sendRichMessage(Config.REQUEST_TO_SELF); + return true; + } + + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("target", target.displayName()) + ); + + PlayerConfig targetConfig = PlayerConfig.getConfig(target); + if (targetConfig.request() != null) { + player.sendRichMessage(Config.TARGET_HAS_PENDING_REQUEST, placeholders); + return true; + } + + if (!targetConfig.allowTeleports()) { + player.sendRichMessage(Config.TELEPORT_TOGGLED_OFF, placeholders); + return true; + } + + targetConfig.request(new TeleportHereRequest(plugin, player, target)); + return true; + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (args.length == 1) { + String name = args[0].trim().toLowerCase(); + return Bukkit.getOnlinePlayers().stream() + .map(Player::getName) + .filter(playerName -> playerName.toLowerCase().startsWith(name)).collect(Collectors.toList()); + } + return null; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportToggleCommand.java b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportToggleCommand.java new file mode 100644 index 0000000..daf2ec1 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/commands/player/TeleportToggleCommand.java @@ -0,0 +1,27 @@ +package com.alttd.essentia.commands.player; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.commands.PlayerSubCommand; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.apache.commons.lang3.BooleanUtils; +import org.bukkit.entity.Player; + +public class TeleportToggleCommand extends PlayerSubCommand { + + public TeleportToggleCommand(EssentiaPlugin plugin) { + super(plugin, "teleporttoggle"); + } + + @Override + protected boolean execute(Player player, PlayerConfig playerConfig, String... args) { + playerConfig.setAllowTeleports(!playerConfig.allowTeleports()); + TagResolver placeholders = TagResolver.resolver( + Placeholder.parsed("toggle", BooleanUtils.toStringOnOff(playerConfig.allowTeleports())) + ); + player.sendRichMessage(Config.TELEPORT_TOGGLE_SET, placeholders); + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/configuration/Config.java b/plugin/src/main/java/com/alttd/essentia/configuration/Config.java new file mode 100755 index 0000000..4611ce4 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/configuration/Config.java @@ -0,0 +1,239 @@ +package com.alttd.essentia.configuration; + +import com.alttd.essentia.EssentiaPlugin; +import com.google.common.base.Throwables; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.logging.Level; + +public class Config { + + private static final String HEADER = """ + Essentia main configuration file + """; + + private static File CONFIG_FILE; + public static File CONFIG_PATH; + public static YamlConfiguration config; + + static int version; + + public static void init() { + CONFIG_PATH = EssentiaPlugin.instance().getDataFolder(); + CONFIG_FILE = new File(CONFIG_PATH, "config.yml"); + config = new YamlConfiguration(); + try { + config.load(CONFIG_FILE); + } catch (IOException ignore) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not load config.yml, please correct your syntax errors", ex); + Throwables.throwIfUnchecked(ex); + } + config.options().header(HEADER); + config.options().copyDefaults(true); + + version = getInt("config-version", 1); + set("config-version", 1); + + readConfig(Config.class, null); + } + + static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + Throwables.throwIfUnchecked(ex); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); + } + } + } + } + saveConfig(); + } + + static void saveConfig() { + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); + } + } + + private static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); + } + + private static boolean getBoolean(String path, boolean def) { + config.addDefault(path, def); + return config.getBoolean(path, config.getBoolean(path)); + } + + private static double getDouble(String path, double def) { + config.addDefault(path, def); + return config.getDouble(path, config.getDouble(path)); + } + + private static int getInt(String path, int def) { + config.addDefault(path, def); + return config.getInt(path, config.getInt(path)); + } + + private static List getList(String path, T def) { + config.addDefault(path, def); + return config.getList(path, config.getList(path)); + } + + private static String getString(String path, String def) { + config.addDefault(path, def); + return config.getString(path, config.getString(path)); + } + + protected static void log(Level level, String s) { + Bukkit.getLogger().log(level, s); + } + + public static int TELEPORT_REQUEST_TIMEOUT = 30; + public static boolean TELEPORT_REQUEST_TIMEOUT_MESSAGES = true; + public static boolean BACK_ON_DEATH = false; + public static boolean TELEPORT_SOUNDS = true; + public static Sound SOUND_TO; + public static Sound SOUND_FROM; + public static int BACK_COOLDOWN = 60; + public static boolean UNSAFE_ENCHANTMENTS = false; + private static void settings() { + TELEPORT_REQUEST_TIMEOUT = getInt("teleport-request-timeout", TELEPORT_REQUEST_TIMEOUT); + TELEPORT_REQUEST_TIMEOUT_MESSAGES = getBoolean("teleport-request-timeout-message", TELEPORT_REQUEST_TIMEOUT_MESSAGES); + BACK_ON_DEATH = getBoolean("back-on-death", BACK_ON_DEATH); + TELEPORT_SOUNDS = getBoolean("use-teleport-sounds", TELEPORT_SOUNDS); + try { + SOUND_TO = Sound.valueOf(config.getString("sound-to", "ENTITY_ENDERMAN_TELEPORT")); + } catch (IllegalArgumentException e) { + SOUND_TO = Sound.ENTITY_ENDERMAN_TELEPORT; + } + try { + SOUND_FROM = Sound.valueOf(config.getString("sound-from", "ENTITY_ENDERMAN_TELEPORT")); + } catch (IllegalArgumentException e) { + SOUND_FROM = Sound.ENTITY_ENDERMAN_TELEPORT; + } + UNSAFE_ENCHANTMENTS = getBoolean("unsafe-enchantments", UNSAFE_ENCHANTMENTS); + BACK_COOLDOWN = getInt("back-cooldown", BACK_COOLDOWN); + } + + public static String REQUEST_TIMED_OUT = "Your teleport request has timed out!"; + public static String TELEPORT_ACCEPT_REQUESTER = " has accepted your teleport request."; + public static String TELEPORT_ACCEPT_TARGET = "You have accepted the teleport request from ."; + public static String TELEPORT_DENIED_REQUESTER = " has denied your teleport request."; + public static String TELEPORT_DENIED_TARGET = "You have denied the teleport request from ."; + public static String TELEPORT_REQUEST_REQUESTER = "Teleport request sent to ."; + public static String TELEPORT_REQUEST_TARGET = " has requested to teleport to you. Type /tpaccept or /tpdeny."; + public static String TELEPORT_REQUESTHERE_REQUESTER = "Teleport here request sent to ."; + public static String TELEPORT_REQUESTHERE_TARGET = " has requested you to teleport to them. Type /tpaccept or /tpdeny."; + public static String TELEPORT_TOGGLE_SET = "Teleport requests toggled ."; + public static String NO_PENDING_REQUESTS = "You do not have any pending teleport requests."; + public static String TELEPORT_TOGGLED_OFF = " has teleports toggled off!"; + public static String TARGET_HAS_PENDING_REQUEST = " has pending request!"; + public static String REQUEST_TO_SELF = "You can not teleport to yourself!"; + + public static String PLAYER_ONLY_COMMAND = "This command is only available to players."; + public static String NO_PLAYER_SPECIFIED = "You must specify a player name!"; + public static String PLAYER_NOT_FOUND = "That player does not exist!"; + public static String PLAYER_NOT_ONLINE = "That player is not online right now!"; + public static String COMMAND_NO_PERMISSION = "You do not have permission for that command!"; + public static String PLAYER_INVENTORY_CLEARED = "You have cleared the inventory of ."; + public static String INVENTORY_CLEARED_BY_OTHER = "Your inventory has been cleared by "; + public static String INVENTORY_CLEARED = "You have cleared your inventory."; + public static String SPECIFY_HOME = "Please specify a home!"; + public static String HOME_NOT_SET = "You have not set a home!"; + public static String HOME_DOES_NOT_EXIST = "Home does not exist!"; + public static String HOME_TELEPORT = "Teleporting to home ."; + public static String HOME_LIST = "Homes: "; + public static String HOME_SET = "Home set."; + public static String HOME_SET_MAX = "You have reached the maximum of homes."; + public static String INVALID_HOME_NAME = "Invalid home name!"; + public static String HOME_DELETED = "The home has been deleted."; + public static String TOGGLED_FLIGHT_BY_OTHER = "Toggled flight on ."; + public static String TOGGLED_FLIGHT_PLAYER = " toggled flight ."; + public static String TOGGLED_FLIGHT = "Toggled fly ."; + public static String NO_BACK_LOCATION = "No back location found!"; + public static String TELEPORTING_BACK = "Teleporting back to previous location."; + public static String NO_DEATH_LOCATION = "No death location found!"; + public static String TELEPORTING_BACK_DEATH = "Teleporting back to previous death location."; + public static String BACK_DEATH_HINT = "Type /dback to go back to where you died."; + public static String GAMEMODE_SET = "Gamemode set to ."; + public static String GAMEMODE_SET_OTHER = "Gamemode for set to ."; + public static String GAMEMODE_SET_BY_OTHER = "Gamemode set to by ."; + + public static String HEAL_SELF = "Your health has been restored."; + public static String HEAL_OTHER = "'s health has been restored."; + public static String HEAL_BY_OTHER = " has restored your health."; + + public static String FEED_SELF = "You just fed yourself."; + public static String FEED_OTHER = "You have fed ."; + public static String FEED_BY_OTHER = " has fed you."; + private static void messages() { + REQUEST_TIMED_OUT = getString("messages.request.time-out", REQUEST_TIMED_OUT); + TELEPORT_ACCEPT_TARGET = getString("messages.request.teleport-accept-target", TELEPORT_ACCEPT_TARGET); + TELEPORT_ACCEPT_REQUESTER = getString("messages.request.teleport-accept-requester", TELEPORT_ACCEPT_REQUESTER); + TELEPORT_DENIED_TARGET = getString("messages.request.teleport-denied-target", TELEPORT_DENIED_TARGET); + TELEPORT_DENIED_REQUESTER = getString("messages.request.teleport-denied-requester", TELEPORT_DENIED_REQUESTER); + TELEPORT_REQUESTHERE_TARGET = getString("messages.request.teleport-requesthere-target", TELEPORT_REQUESTHERE_TARGET); + TELEPORT_REQUEST_TARGET = getString("messages.request.teleport-request-target", TELEPORT_REQUEST_TARGET); + TELEPORT_TOGGLE_SET = getString("messages.request.teleport-toggle-set", TELEPORT_TOGGLE_SET); + NO_PENDING_REQUESTS = getString("messages.request.no-pending-requests", NO_PENDING_REQUESTS); + TELEPORT_TOGGLED_OFF = getString("messages.request.target-toggled-off", TELEPORT_TOGGLED_OFF); + TARGET_HAS_PENDING_REQUEST = getString("messages.request.target-has-pending-request", TARGET_HAS_PENDING_REQUEST); + REQUEST_TO_SELF = getString("messages.request.request-to-self", REQUEST_TO_SELF); + + PLAYER_ONLY_COMMAND = getString("messages.command.player-only-command", PLAYER_ONLY_COMMAND); + NO_PLAYER_SPECIFIED = getString("messages.command.no-player-specified", NO_PLAYER_SPECIFIED); + PLAYER_NOT_FOUND = config.getString("messages.command.player-not-found", PLAYER_NOT_FOUND); + PLAYER_NOT_ONLINE = config.getString("messages.command.player-not-online", PLAYER_NOT_ONLINE); + COMMAND_NO_PERMISSION = config.getString("messages.command.no-permission", COMMAND_NO_PERMISSION); + PLAYER_INVENTORY_CLEARED = config.getString("messages.command.clear-inventory.player-inventory-cleared", PLAYER_INVENTORY_CLEARED); + INVENTORY_CLEARED_BY_OTHER = config.getString("messages.command.clear-inventory.inventory-clear-by-other", INVENTORY_CLEARED_BY_OTHER); + INVENTORY_CLEARED = config.getString("messages.command.clear-inventory.inventory-cleared", INVENTORY_CLEARED); + SPECIFY_HOME = config.getString("messages.command.home.specify-home", SPECIFY_HOME); + HOME_NOT_SET = config.getString("messages.command.home.home-not-set", HOME_NOT_SET); + HOME_DOES_NOT_EXIST = config.getString("messages.command.home.home-does-not-exist", HOME_DOES_NOT_EXIST); + HOME_TELEPORT = config.getString("messages.command.home.home-teleport", HOME_TELEPORT); + HOME_LIST = config.getString("messages.command.home.home-list", HOME_LIST); + HOME_SET = config.getString("messages.command.home.home-set", HOME_SET); + HOME_SET_MAX = config.getString("messages.command.home.home-set-max", HOME_SET_MAX); + INVALID_HOME_NAME = config.getString("messages.command.home.invalid-home-name", INVALID_HOME_NAME); + HOME_DELETED = config.getString("messages.command.home.invalid-home-name", HOME_DELETED); + + TOGGLED_FLIGHT_BY_OTHER = config.getString("messages.command.fly.toggled-by-other", TOGGLED_FLIGHT_BY_OTHER); + TOGGLED_FLIGHT_PLAYER = config.getString("messages.command.fly.toggled-flight-other", TOGGLED_FLIGHT_PLAYER); + TOGGLED_FLIGHT = config.getString("messages.command.fly.toggled-flight", TOGGLED_FLIGHT); + NO_BACK_LOCATION = config.getString("messages.command.back.no-back-location", NO_BACK_LOCATION); + TELEPORTING_BACK = config.getString("messages.command.back.teleporting-back", TELEPORTING_BACK); + NO_DEATH_LOCATION = config.getString("messages.command.back.no-death-location", NO_DEATH_LOCATION); + TELEPORTING_BACK_DEATH = config.getString("messages.command.back.teleporting-back-death", TELEPORTING_BACK_DEATH); + BACK_DEATH_HINT = config.getString("messages.command.back.dback-hint", BACK_DEATH_HINT); + GAMEMODE_SET = config.getString("messages.command.gamemode.gamemode-set", GAMEMODE_SET); + GAMEMODE_SET_OTHER = config.getString("messages.command.gamemode.gamemode-set-other", GAMEMODE_SET_OTHER); + GAMEMODE_SET_BY_OTHER = config.getString("messages.command.gamemode.gamemode-set-by-other", GAMEMODE_SET_BY_OTHER); + HEAL_SELF = config.getString("messages.command.heal.heal-self", HEAL_SELF); + HEAL_OTHER = config.getString("messages.command.heal.heal-other", HEAL_OTHER); + HEAL_BY_OTHER = config.getString("messages.command.heal.heal-by-other", HEAL_BY_OTHER); + FEED_SELF = config.getString("messages.command.feed.feed-self", FEED_SELF); + FEED_OTHER = config.getString("messages.command.feed.feed-other", FEED_OTHER); + FEED_BY_OTHER = config.getString("messages.command.feed.feed-by-other", FEED_BY_OTHER); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/configuration/PlayerConfig.java b/plugin/src/main/java/com/alttd/essentia/configuration/PlayerConfig.java new file mode 100644 index 0000000..ddb02a6 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/configuration/PlayerConfig.java @@ -0,0 +1,181 @@ +package com.alttd.essentia.configuration; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.request.Request; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class PlayerConfig extends YamlConfiguration { + + private static final Map configs = new HashMap<>(); + + public static PlayerConfig getConfig(Player player) { + synchronized (configs) { + return configs.computeIfAbsent(player, k -> new PlayerConfig(player)); + } + } + + public static void remove(Player player) { + synchronized (configs) { + configs.remove(player); + } + } + + public static void removeAll() { + synchronized (configs) { + configs.clear(); + } + } + + private final File file; + private final Object saveLock = new Object(); + private final OfflinePlayer player; + @Getter @Setter private Request request; + + private PlayerConfig(Player player) { + super(); + this.player = player; + this.file = new File(EssentiaPlugin.instance().getDataFolder(), "PlayerData" + File.separator + player.getUniqueId() + ".yml"); + reload(); + } + + private void reload() { + synchronized (saveLock) { + try { + load(file); + } catch (Exception ignore) { + } + } + } + + private void save() { + synchronized (saveLock) { + try { + save(file); + } catch (Exception ignore) { + } + } + } + + Location getStoredLocation(String path) { + if (get(path) == null) { + return null; + } + World world = Bukkit.getWorld(getString(path + ".world", "")); + if (world == null) { + return null; + } + double x = getDouble(path + ".x"); + double y = getDouble(path + ".y"); + double z = getDouble(path + ".z"); + float pitch = (float) getDouble(path + ".pitch"); + float yaw = (float) getDouble(path + ".yaw"); + return new Location(world, x, y, z, yaw, pitch); + } + + void setStoredLocation(String path, Location location) { + if (location == null) { + set(path, null); + save(); + return; + } + set(path + ".world", location.getWorld().getName()); + set(path + ".x", location.getX()); + set(path + ".y", location.getY()); + set(path + ".z", location.getZ()); + set(path + ".pitch", location.getPitch()); + set(path + ".yaw", location.getYaw()); + save(); + } + + public Location getBackLocation(boolean death) { + return getStoredLocation(death ? "teleports.death" : "teleports.back"); + } + + public void setBackLocation(boolean death, Location location) { + setStoredLocation(death ? "teleports.death" : "teleports.back", location); + } + + public Location getHome(String name) { + return getStoredLocation("home." + name); + } + + + public void setHome(String name, Location location) { + setStoredLocation("home." + name, location); + } + + public int getHomeCount() { + ConfigurationSection section = getConfigurationSection("home"); + if (section == null) { + return 0; + } + return section.getKeys(false).size(); + } + + public List getMatchingHomeNames(String name) { + ConfigurationSection section = getConfigurationSection("home"); + if (section == null) { + return null; + } + List list = section.getValues(false).keySet().stream() + .filter(home -> home.toLowerCase().startsWith(name.toLowerCase())) + .collect(Collectors.toList()); + + if (player.getBedSpawnLocation() != null && "bed".startsWith(name.toLowerCase())) + list.add("bed"); + + return list; + } + + public Map getHomeData() { + ConfigurationSection section = getConfigurationSection("home"); + if (section == null) { + return null; + } + Map map = new HashMap<>(); + for (String key : section.getValues(false).keySet()) { + map.put(key, getHome(key)); + } + if (player.getBedSpawnLocation() != null) + map.put("bed", player.getBedSpawnLocation()); + + return map; + } + + public List getHomeList() { + ConfigurationSection section = getConfigurationSection("home"); + if (section == null) { + return null; + } + List list = new ArrayList<>(section.getValues(false).keySet()); + if (player.getBedSpawnLocation() != null) + list.add("bed"); + + return list; + } + + public boolean allowTeleports() { + return getBoolean("allow-teleports", true); + } + + public void setAllowTeleports(boolean allowTeleports) { + set("allow-teleports", allowTeleports); + save(); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/listeners/PlayerListener.java b/plugin/src/main/java/com/alttd/essentia/listeners/PlayerListener.java new file mode 100644 index 0000000..c64d458 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/listeners/PlayerListener.java @@ -0,0 +1,63 @@ +package com.alttd.essentia.listeners; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.util.HashSet; +import java.util.Set; + +public class PlayerListener implements Listener { + + private EssentiaPlugin plugin; + private final Set backAllowCauses = new HashSet<>(); + + public PlayerListener(EssentiaPlugin plugin) { + this.plugin = plugin; + + backAllowCauses.add(PlayerTeleportEvent.TeleportCause.PLUGIN); + backAllowCauses.add(PlayerTeleportEvent.TeleportCause.COMMAND); + backAllowCauses.add(PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + + @EventHandler(ignoreCancelled = true) + public void backOnDeath(PlayerDeathEvent event) { + if (!Config.BACK_ON_DEATH) { + return; + } + Player player = event.getEntity(); + if (!player.hasPermission("essentia.command.deathback")) { + return; + } + + PlayerConfig playerConfig = PlayerConfig.getConfig(player); + playerConfig.setBackLocation(true, player.getLocation()); + player.sendRichMessage(Config.BACK_DEATH_HINT); + } + + @EventHandler(ignoreCancelled = true) + public void backOnTeleport(PlayerTeleportEvent event) { + if (!backAllowCauses.contains(event.getCause())) { + return; + } + + Player player = event.getPlayer(); + if (!player.hasPermission("essentia.command.back")) { + return; + } + + Location to = event.getTo(); + Location from = event.getFrom(); + + // only save location if teleporting more than 5 blocks + if (!to.getWorld().equals(from.getWorld()) || to.distanceSquared(from) > 25) { + PlayerConfig.getConfig(player).setBackLocation(false, event.getFrom()); + } + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/request/Request.java b/plugin/src/main/java/com/alttd/essentia/request/Request.java new file mode 100644 index 0000000..1b9fd6a --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/request/Request.java @@ -0,0 +1,72 @@ +package com.alttd.essentia.request; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.configuration.PlayerConfig; +import com.alttd.essentia.tasks.RequestTimeout; +import com.alttd.essentia.tasks.TeleportSounds; +import lombok.Getter; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.entity.Player; + +public abstract class Request { + + private final EssentiaPlugin plugin; + @Getter private final Player requester; + @Getter private final Player target; + private final RequestTimeout timeoutTask; + + TagResolver placeholders; + + public Request(EssentiaPlugin plugin, Player requester, Player target) { + this.plugin = plugin; + this.requester = requester; + this.target = target; + + this.timeoutTask = new RequestTimeout(this); + + if (Config.TELEPORT_REQUEST_TIMEOUT > 0) { + this.timeoutTask.runTaskLater(plugin, + Config.TELEPORT_REQUEST_TIMEOUT * 20L); + } + + placeholders = TagResolver.resolver( + Placeholder.component("requester", requester().displayName()), + Placeholder.component("target", target().displayName()) + ); + } + + public void accept() { + teleport(); + + target.sendRichMessage(Config.TELEPORT_ACCEPT_TARGET, placeholders); + requester.sendRichMessage(Config.TELEPORT_ACCEPT_REQUESTER, placeholders); + + cancel(); + } + + public void deny() { + target.sendRichMessage(Config.TELEPORT_DENIED_TARGET, placeholders); + requester.sendRichMessage(Config.TELEPORT_DENIED_REQUESTER, placeholders); + + cancel(); + } + + protected abstract void teleport(); + + void playTeleportSounds() { + if (Config.TELEPORT_SOUNDS) { + new TeleportSounds(target.getLocation(), requester.getLocation()) + .runTaskLater(plugin, 1); + } + } + + public void cancel() { + try { + timeoutTask.cancel(); + PlayerConfig.getConfig(target).request(null); + } catch (IllegalStateException ignore) { + } + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/request/TeleportHereRequest.java b/plugin/src/main/java/com/alttd/essentia/request/TeleportHereRequest.java new file mode 100644 index 0000000..9295e37 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/request/TeleportHereRequest.java @@ -0,0 +1,25 @@ +package com.alttd.essentia.request; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.configuration.Config; +import org.bukkit.entity.Player; + +public class TeleportHereRequest extends Request { + + public TeleportHereRequest(EssentiaPlugin plugin, Player requester, Player target) { + super(plugin, requester, target); + + target.sendRichMessage(Config.TELEPORT_REQUEST_TARGET, placeholders); + requester.sendRichMessage(Config.TELEPORT_REQUESTHERE_REQUESTER, placeholders); + } + + @Override + protected void teleport() { + if (!target().isOnline() || !requester().isOnline()) { + cancel(); + return; + } + target().teleportAsync(requester().getLocation()).thenAccept(result -> playTeleportSounds()); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/request/TeleportRequest.java b/plugin/src/main/java/com/alttd/essentia/request/TeleportRequest.java new file mode 100644 index 0000000..3fd6ef0 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/request/TeleportRequest.java @@ -0,0 +1,25 @@ +package com.alttd.essentia.request; + +import com.alttd.essentia.EssentiaPlugin; +import com.alttd.essentia.configuration.Config; +import org.bukkit.entity.Player; + +public class TeleportRequest extends Request { + + public TeleportRequest(EssentiaPlugin plugin, Player requester, Player target) { + super(plugin, requester, target); + + target.sendRichMessage(Config.TELEPORT_REQUESTHERE_TARGET, placeholders); + requester.sendRichMessage(Config.TELEPORT_REQUEST_REQUESTER, placeholders); + } + + @Override + protected void teleport() { + if (!target().isOnline() || !requester().isOnline()) { + cancel(); + return; + } + target().teleportAsync(requester().getLocation()).thenAccept(result -> playTeleportSounds()); + } + +} diff --git a/plugin/src/main/java/com/alttd/essentia/tasks/RequestTimeout.java b/plugin/src/main/java/com/alttd/essentia/tasks/RequestTimeout.java new file mode 100644 index 0000000..0b6dd51 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/tasks/RequestTimeout.java @@ -0,0 +1,31 @@ +package com.alttd.essentia.tasks; + +import com.alttd.essentia.configuration.Config; +import com.alttd.essentia.request.Request; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.scheduler.BukkitRunnable; + +public class RequestTimeout extends BukkitRunnable { + private final Request request; + + public RequestTimeout(Request request) { + this.request = request; + } + + @Override + public void run() { + if (!request.target().isOnline() || !request.requester().isOnline()) { + request.cancel(); + return; + } + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("requester", request.requester().displayName()), + Placeholder.component("target", request.target().displayName()) + ); + request.requester().sendRichMessage(Config.REQUEST_TIMED_OUT, placeholders); + request.target().sendRichMessage(Config.REQUEST_TIMED_OUT, placeholders); + + request.cancel(); + } +} diff --git a/plugin/src/main/java/com/alttd/essentia/tasks/TeleportSounds.java b/plugin/src/main/java/com/alttd/essentia/tasks/TeleportSounds.java new file mode 100644 index 0000000..91fc302 --- /dev/null +++ b/plugin/src/main/java/com/alttd/essentia/tasks/TeleportSounds.java @@ -0,0 +1,30 @@ +package com.alttd.essentia.tasks; + +import com.alttd.essentia.configuration.Config; + +import org.bukkit.Location; +import org.bukkit.scheduler.BukkitRunnable; + +public class TeleportSounds extends BukkitRunnable { + + private final Location to; + private final Location from; + + public TeleportSounds(Location to, Location from) { + this.to = to; + this.from = from; + } + + @Override + public void run() { + if (Config.TELEPORT_SOUNDS) { + if (Config.SOUND_TO != null) { + to.getWorld().playSound(to, Config.SOUND_TO, 1.0F, 1.0F); + } + + if (Config.SOUND_FROM != null) { + from.getWorld().playSound(from, Config.SOUND_FROM, 1.0F, 1.0F); + } + } + } +} diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml new file mode 100644 index 0000000..5f56084 --- /dev/null +++ b/plugin/src/main/resources/plugin.yml @@ -0,0 +1,106 @@ +name: Essentia +version: ${version} +main: com.alttd.essentia.EssentiaPlugin +description: Altitude essentials ;) +authors: + - destro174 +api-version: "1.20" + +commands: + essentia: + description: Reload configs. + permission: essentia.command.essentia-reload + usage: / (reload) + teleportaccept: + description: Accept teleport request. + permission: essentia.command.teleportaccept + usage: / + aliases: + - tpaccept + teleportdeny: + description: Decline teleport request. + permission: essentia.command.teleportdeny + usage: / + aliases: + - tpdeny + teleportrequest: + description: Request to teleport to another player. + permission: essentia.command.teleportrequest + usage: / player + aliases: + - tpa + - tprequest + - tparequest + teleportrequesthere: + description: Request another player to teleport to you. + permission: essentia.command.teleportrequesthere + usage: / player + aliases: + - tpah + - tpahere + teleporttoggle: + description: Toggle teleport requests on/off. + permission: essentia.command.teleporttoggle + usage: / + aliases: + - tptoggle + clearinventory: + description: Clears your inventory. + permission: essentia.command.clearinventory + usage: / (player) + home: + description: Teleports the player home. + permission: essentia.command.home + usage: / (home (player)) + homes: + description: List the player's homes. + permission: essentia.command.homes + usage: / (player) + aliases: + - listhomes + sethome: + description: Sets the player's home. + permission: essentia.command.sethome + usage: / (home (player)) + aliases: + - homeset + deletehome: + description: Deletes a home. + permission: essentia.command.deletehome + usage: / [home (player)] + aliases: + - delhome + back: + description: Go back to previous location. + permission: essentia.command.back + usage: / + deathback: + description: Go back to previous death location. + permission: essentia.command.deathback + usage: / + aliases: + - dback + fly: + description: Toggles creative flymode for yourself or another player. + permission: essentia.command.fly + usage: / (player) + gamemode: + description: Set gamemode for yourself or another player. + permission: essentia.command.gamemode + usage: / (player) + aliases: + - gm + heal: + description: Heals yourself or another player. + permission: essentia.command.heal + usage: / (player) + aliases: + - health + feed: + description: Refill hunger and saturation. + permission: essentia.command.feed + usage: / (player) + enchant: + description: Enchants the item in hand + permission: essentia.command.enchant + usage: / [enchantment/all] (level) (unsafe) \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..7377a7e --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,19 @@ +rootProject.name = "Essentia" + +include(":api") +include(":plugin") + +dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy + maven("https://jitpack.io") + } +} + +pluginManagement { + repositories { + gradlePluginPortal() + } +}