Commit so i can branch it.
This commit is contained in:
19
build.gradle
19
build.gradle
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
group = 'me.trouper'
|
||||
@@ -24,9 +25,6 @@ dependencies {
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
// Configure the Minecraft version for our task.
|
||||
// This is the only required configuration besides applying the plugin.
|
||||
// Your plugin's jar (or shadowJar if present) will be used automatically.
|
||||
minecraftVersion("1.21.5")
|
||||
}
|
||||
}
|
||||
@@ -57,3 +55,18 @@ processResources {
|
||||
expand props
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
groupId = project.group
|
||||
artifactId = 'alias'
|
||||
version = project.version
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
0
gradle.properties
Normal file
0
gradle.properties
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
#Thu May 29 23:20:25 CDT 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
41
gradlew
vendored
41
gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# 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
|
||||
# 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/.
|
||||
@@ -80,13 +80,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
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"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +131,29 @@ 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.
|
||||
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
|
||||
@@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
|
||||
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.
|
||||
|
||||
# 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, 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" \
|
||||
@@ -205,6 +214,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# 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.
|
||||
|
||||
35
gradlew.bat
vendored
35
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 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.
|
||||
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
|
||||
|
||||
@@ -56,11 +57,11 @@ 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.
|
||||
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
|
||||
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
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
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
package me.trouper.alias;
|
||||
|
||||
import me.trouper.alias.server.Manager;
|
||||
import me.trouper.alias.utils.visual.BlockDisplayRaytracer;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import me.trouper.alias.data.Common;
|
||||
import me.trouper.alias.server.AutoRegistrar;
|
||||
import me.trouper.alias.server.commands.QuickCommand;
|
||||
import me.trouper.alias.server.events.GuiListener;
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
import me.trouper.alias.server.systems.AbstractWand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class Alias extends JavaPlugin {
|
||||
|
||||
private static Alias instance;
|
||||
private Manager manager;
|
||||
private static Class<? extends JavaPlugin> host;
|
||||
private static AutoRegistrar autoRegistrar;
|
||||
private static Common common;
|
||||
private static boolean enabled;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
getLogger().info("Instantiating Plugin");
|
||||
instance = this;
|
||||
|
||||
public static synchronized void register(JavaPlugin plugin, Common common) {
|
||||
if (plugin == null || enabled) return;
|
||||
Alias.host = plugin.getClass();
|
||||
Alias.common = common;
|
||||
|
||||
new GuiListener().register();
|
||||
autoRegistrar = new AutoRegistrar(plugin);
|
||||
autoRegistrar.loadAll(common.getPackageName());
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().info("Instantiating Manager");
|
||||
manager = new Manager();
|
||||
|
||||
getLogger().info("Initializing Manager");
|
||||
manager.init();
|
||||
|
||||
getLogger().info("Successfully enabled TrimAlias.");
|
||||
public static Class<? extends JavaPlugin> getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("Saved all IO files.");
|
||||
manager.io.saveAll();
|
||||
getLogger().info("Saved all IO files.");
|
||||
public static AutoRegistrar getAutoRegistrar() {
|
||||
return autoRegistrar;
|
||||
}
|
||||
|
||||
public static me.trouper.alias.Alias getInstance() {
|
||||
return instance;
|
||||
public static Common getCommon() {
|
||||
return common;
|
||||
}
|
||||
public NamespacedKey getNameSpace() {
|
||||
return new NamespacedKey(getInstance(),"_alias");
|
||||
}
|
||||
public Manager getManager() {
|
||||
return manager;
|
||||
|
||||
public static void updateCommon(Common common) {
|
||||
Alias.common = common;
|
||||
}
|
||||
}
|
||||
|
||||
97
src/main/java/me/trouper/alias/data/Common.java
Normal file
97
src/main/java/me/trouper/alias/data/Common.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package me.trouper.alias.data;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Common {
|
||||
|
||||
private final String packageName;
|
||||
private int mainColor;
|
||||
private int secondaryColor;
|
||||
private String pluginName;
|
||||
private String flatPrefix;
|
||||
private boolean flat;
|
||||
private boolean debugMode;
|
||||
private final Set<String> debuggerExclusions;
|
||||
|
||||
public Common(String packageName, int mainColor, int secondaryColor, String pluginName, String flatPrefix, boolean flat) {
|
||||
this.packageName = packageName;
|
||||
this.mainColor = mainColor;
|
||||
this.secondaryColor = secondaryColor;
|
||||
this.pluginName = pluginName;
|
||||
this.flatPrefix = flatPrefix;
|
||||
this.flat = flat;
|
||||
this.debugMode = false;
|
||||
this.debuggerExclusions = new HashSet<>();
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public int getMainColor() {
|
||||
return mainColor;
|
||||
}
|
||||
|
||||
public void setMainColor(int mainColor) {
|
||||
this.mainColor = mainColor;
|
||||
}
|
||||
|
||||
public String getPluginName() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
public void setPluginName(String pluginName) {
|
||||
this.pluginName = pluginName;
|
||||
}
|
||||
|
||||
public String getFlatPrefix() {
|
||||
return flatPrefix;
|
||||
}
|
||||
|
||||
public void setFlatPrefix(String flatPrefix) {
|
||||
this.flatPrefix = flatPrefix;
|
||||
}
|
||||
|
||||
public boolean useFlat() {
|
||||
return flat;
|
||||
}
|
||||
|
||||
public void setFlat(boolean flat) {
|
||||
this.flat = flat;
|
||||
}
|
||||
|
||||
public int getSecondaryColor() {
|
||||
return secondaryColor;
|
||||
}
|
||||
|
||||
public void setSecondaryColor(int secondaryColor) {
|
||||
this.secondaryColor = secondaryColor;
|
||||
}
|
||||
|
||||
public boolean getDebugMode() {
|
||||
return debugMode;
|
||||
}
|
||||
|
||||
public void setDebugMode(boolean mode) {
|
||||
this.debugMode = mode;
|
||||
}
|
||||
|
||||
public Set<String> getDebuggerExclusions() {
|
||||
return debuggerExclusions;
|
||||
}
|
||||
|
||||
public boolean addDebuggerExclusion(String methodName) {
|
||||
return this.debuggerExclusions.add(methodName);
|
||||
}
|
||||
|
||||
public boolean removeDebuggerExclusion(String methodName) {
|
||||
return this.debuggerExclusions.remove(methodName);
|
||||
}
|
||||
|
||||
public String getTempTag() {
|
||||
return "$/" + pluginName + "/ TEMP";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
147
src/main/java/me/trouper/alias/data/JsonSerializable.java
Executable file
147
src/main/java/me/trouper/alias/data/JsonSerializable.java
Executable file
@@ -0,0 +1,147 @@
|
||||
package me.trouper.alias.data;
|
||||
|
||||
import com.google.gson.*;
|
||||
import me.trouper.alias.utils.misc.FileValidationUtils;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface JsonSerializable<T> {
|
||||
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().setStrictness(Strictness.LENIENT).create();
|
||||
File getFile();
|
||||
|
||||
default String serialize(boolean pretty) {
|
||||
Gson gson;
|
||||
if (pretty) {
|
||||
gson = new GsonBuilder().setPrettyPrinting().setStrictness(Strictness.LENIENT).create();
|
||||
}
|
||||
else {
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
try {
|
||||
String json = gson.toJson(this);
|
||||
if (json == null) {
|
||||
throw new IllegalStateException("json parse failed for " + this.getClass().getSimpleName());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default T deserialize(String json) {
|
||||
try {
|
||||
JsonSerializable<?> v = gson.fromJson(json, this.getClass());
|
||||
if (v == null) {
|
||||
throw new IllegalStateException("json parse failed");
|
||||
}
|
||||
return (T)v;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
default JsonObject getJson() {
|
||||
return gson.toJsonTree(this).getAsJsonObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a json element given the specified member path
|
||||
* @param path Path separated by a period . between each member name
|
||||
* @return the JsonElement at the end of the path, otherwise null
|
||||
*/
|
||||
default JsonElement get(String path) {
|
||||
JsonElement root = gson.toJsonTree(this);
|
||||
JsonElement json = root;
|
||||
|
||||
for (String memberName : path.split("\\.")) {
|
||||
JsonElement e = json.getAsJsonObject().get(memberName);
|
||||
if (e != null)
|
||||
json = e;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return json == root ? null : json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a json element given the specified member path
|
||||
* @param path Path separated by a period . between each member name
|
||||
*/
|
||||
default boolean set(String path, Object obj) {
|
||||
JsonElement root = gson.toJsonTree(this);
|
||||
JsonElement json = root;
|
||||
String[] paths = path.split("\\.");
|
||||
|
||||
if (paths.length == 0)
|
||||
return false;
|
||||
if (paths.length == 1) {
|
||||
root.getAsJsonObject().add(path, gson.toJsonTree(obj));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < paths.length - 1; i++) {
|
||||
JsonElement e = json.getAsJsonObject().get(paths[i]);
|
||||
if (e != null)
|
||||
json = e;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (json != root) {
|
||||
json.getAsJsonObject().add(paths[paths.length - 1], gson.toJsonTree(obj));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default void save() {
|
||||
String json = serialize(true);
|
||||
File f = getFile();
|
||||
|
||||
if (FileValidationUtils.validate(f)) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(f);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
bw.write(json);
|
||||
bw.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default <O> O getOrDef(O val, O def) {
|
||||
return val != null ? val : def;
|
||||
}
|
||||
|
||||
static <T extends JsonSerializable<?>> T load(File file, Class<T> jsonSerializable, T fallback) {
|
||||
if (FileValidationUtils.validate(file)) {
|
||||
try {
|
||||
FileReader fr = new FileReader(file);
|
||||
BufferedReader br = new BufferedReader(fr);
|
||||
T t = gson.fromJson(br, jsonSerializable);
|
||||
|
||||
if (t == null) {
|
||||
throw new IllegalStateException("json parse failed!");
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static <T extends JsonSerializable<?>> T load(String path, Class<T> jsonSerializable, T fallback) {
|
||||
return load(new File(path), jsonSerializable, fallback);
|
||||
}
|
||||
}
|
||||
78
src/main/java/me/trouper/alias/server/AutoRegistrar.java
Normal file
78
src/main/java/me/trouper/alias/server/AutoRegistrar.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package me.trouper.alias.server;
|
||||
|
||||
import me.trouper.alias.server.commands.QuickCommand;
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
import me.trouper.alias.server.systems.AbstractWand;
|
||||
import me.trouper.alias.utils.misc.ReflectionUtils;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class AutoRegistrar {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final List<QuickCommand> quickCommands = new ArrayList<>();
|
||||
private final List<QuickListener> quickListeners = new ArrayList<>();
|
||||
private final List<AbstractWand> wands = new ArrayList<>();
|
||||
|
||||
public AutoRegistrar(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void loadAll(String basePackage) {
|
||||
Set<Class<?>> classes = ReflectionUtils.getClassesInPackage(plugin, basePackage);
|
||||
|
||||
for (Class<?> clazz : classes) {
|
||||
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()) || clazz.isEnum() || clazz.isAnnotation()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isCommand = QuickCommand.class.isAssignableFrom(clazz);
|
||||
boolean isWand = AbstractWand.class.isAssignableFrom(clazz);
|
||||
boolean isListener = QuickListener.class.isAssignableFrom(clazz);
|
||||
|
||||
if (!isCommand && !isWand && !isListener) continue;
|
||||
|
||||
try {
|
||||
Object instance = clazz.getDeclaredConstructor().newInstance();
|
||||
|
||||
if (instance instanceof QuickCommand command) {
|
||||
command.register();
|
||||
quickCommands.add(command);
|
||||
plugin.getLogger().info("Registered QuickCommand: " + clazz.getSimpleName());
|
||||
}
|
||||
|
||||
if (instance instanceof AbstractWand wand) {
|
||||
wand.register();
|
||||
wands.add(wand);
|
||||
plugin.getLogger().info("Registered AbstractWand: " + clazz.getSimpleName());
|
||||
}
|
||||
|
||||
else if (instance instanceof QuickListener listener) {
|
||||
listener.register();
|
||||
quickListeners.add(listener);
|
||||
plugin.getLogger().info("Registered QuickListener: " + clazz.getSimpleName());
|
||||
}
|
||||
|
||||
} catch (Throwable t) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to instantiate: " + clazz.getName(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<QuickCommand> getQuickCommands() {
|
||||
return quickCommands;
|
||||
}
|
||||
|
||||
public List<QuickListener> getQuickListeners() {
|
||||
return quickListeners;
|
||||
}
|
||||
|
||||
public List<AbstractWand> getWands() {
|
||||
return wands;
|
||||
}
|
||||
}
|
||||
86
src/main/java/me/trouper/alias/server/Main.java
Executable file → Normal file
86
src/main/java/me/trouper/alias/server/Main.java
Executable file → Normal file
@@ -1,71 +1,83 @@
|
||||
package me.trouper.alias.server;
|
||||
|
||||
import io.papermc.paper.registry.RegistryAccess;
|
||||
import me.trouper.alias.data.IO;
|
||||
import me.trouper.alias.data.io.Config;
|
||||
import me.trouper.alias.data.io.Storage;
|
||||
import me.trouper.alias.utils.Text;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import me.trouper.alias.Alias;
|
||||
import me.trouper.alias.data.Common;
|
||||
import me.trouper.alias.server.systems.Text;
|
||||
import me.trouper.alias.utils.misc.Randomizer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public interface Main {
|
||||
Main main = new Main() {};
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
default RegistryAccess getRegistryAccess() {
|
||||
return RegistryAccess.registryAccess();
|
||||
return RegistryAccess.registryAccess();
|
||||
}
|
||||
|
||||
default me.trouper.alias.Alias getPlugin() {
|
||||
return me.trouper.alias.Alias.getInstance();
|
||||
default JavaPlugin getPlugin() {
|
||||
Class<? extends JavaPlugin> host = Alias.getHost();
|
||||
if (host == null) throw new RuntimeException("Alias is not enabled. Make sure to call Alias#register() in your JavaPlugin#onLoad() method!");
|
||||
return getPlugin(host);
|
||||
}
|
||||
|
||||
default Manager man() {
|
||||
return getPlugin().getManager();
|
||||
default <T extends JavaPlugin> T getPlugin(Class<T> pluginClass) {
|
||||
return JavaPlugin.getPlugin(pluginClass);
|
||||
}
|
||||
|
||||
default IO io() {
|
||||
return man().io;
|
||||
};
|
||||
|
||||
default Config config() {
|
||||
return io().config;
|
||||
default Common getCommon() {
|
||||
return Alias.getCommon();
|
||||
}
|
||||
|
||||
default Storage storage() {
|
||||
return io().storage;
|
||||
default void infoAny(Audience player, String message, Object... args) {
|
||||
Text.messageAny(Text.Pallet.INFO, player, message, args);
|
||||
}
|
||||
|
||||
default void info(CommandSender player, String message, Object... args) {
|
||||
Text.sendMessage(Text.Pallet.INFO, player, message, args);
|
||||
default void errorAny(Audience player, String message, Object... args) {
|
||||
Text.messageAny(Text.Pallet.ERROR,player, message, args);
|
||||
}
|
||||
|
||||
default void error(CommandSender player, String message, Object... args) {
|
||||
Text.sendMessage(Text.Pallet.ERROR, player, message, args);
|
||||
default void warningAny(Audience player, String message, Object... args) {
|
||||
Text.messageAny(Text.Pallet.WARNING, player, message, args);
|
||||
}
|
||||
|
||||
default void warning(CommandSender player, String message, Object... args) {
|
||||
Text.sendMessage(Text.Pallet.WARNING, player, message, args);
|
||||
default void successAny(Audience player, String message, Object... args) {
|
||||
Text.messageAny(Text.Pallet.SUCCESS, player, message, args);
|
||||
}
|
||||
|
||||
default void success(CommandSender player, String message, Object... args) {
|
||||
Text.sendMessage(Text.Pallet.SUCCESS, player, message, args);
|
||||
default void messageAny(Audience player, String message, Object... args) {
|
||||
Text.messageAny(Text.Pallet.NEUTRAL, player, message, args);
|
||||
}
|
||||
|
||||
default void message(CommandSender player, String message, Object... args) {
|
||||
Text.sendMessage(Text.Pallet.NEUTRAL, player, message, args);
|
||||
default void info(Audience player, Component message, Component... args) {
|
||||
Text.message(Text.Pallet.INFO, player, message, args);
|
||||
}
|
||||
|
||||
default void checkPre(boolean check, String msg, Object... args) {
|
||||
if (!check) {
|
||||
throw new IllegalArgumentException(msg.formatted(args));
|
||||
}
|
||||
default void error(Audience player, Component message, Component... args) {
|
||||
Text.message(Text.Pallet.ERROR,player, message, args);
|
||||
}
|
||||
|
||||
default void checkPre(BooleanSupplier check, String msg, Object... args) {
|
||||
checkPre(check.getAsBoolean(), msg, args);
|
||||
default void warning(Audience player, Component message, Component... args) {
|
||||
Text.message(Text.Pallet.WARNING, player, message, args);
|
||||
}
|
||||
|
||||
default void success(Audience player, Component message, Component... args) {
|
||||
Text.message(Text.Pallet.SUCCESS, player, message, args);
|
||||
}
|
||||
|
||||
default void message(Audience player, Component message, Component... args) {
|
||||
Text.message(Text.Pallet.NEUTRAL, player, message, args);
|
||||
}
|
||||
|
||||
default Random random() {
|
||||
return new Random();
|
||||
}
|
||||
|
||||
default Randomizer randomizer() {
|
||||
return new Randomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
115
src/main/java/me/trouper/alias/server/commands/Args.java
Executable file
115
src/main/java/me/trouper/alias/server/commands/Args.java
Executable file
@@ -0,0 +1,115 @@
|
||||
package me.trouper.alias.server.commands;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public record Args(String... args) {
|
||||
|
||||
public Arg getAll() {
|
||||
return getAll(0);
|
||||
}
|
||||
|
||||
public Arg getAll(int beginIndex) {
|
||||
String str = "";
|
||||
for (int i = beginIndex; i < args.length; i++) {
|
||||
str = str.concat(args[i] + " ");
|
||||
}
|
||||
return new Arg(str.trim());
|
||||
}
|
||||
|
||||
public Arg get(int index) {
|
||||
if (args.length == 0)
|
||||
throw new IllegalArgumentException("not enough arguments: arguments are empty");
|
||||
if (index < 0 || index >= args.length)
|
||||
throw new IllegalArgumentException("not enough arguments: argument %s is missing".formatted(index + 1));
|
||||
return new Arg(args[index]);
|
||||
}
|
||||
|
||||
public Arg first() {
|
||||
return get(0);
|
||||
}
|
||||
|
||||
public Arg last() {
|
||||
return get(args.length - 1);
|
||||
}
|
||||
|
||||
public boolean match(int index, String arg) {
|
||||
if (index < 0 || index >= args.length) {
|
||||
return false;
|
||||
}
|
||||
return get(index).toString().equalsIgnoreCase(arg);
|
||||
}
|
||||
|
||||
public void when(int index, String match, Consumer<Arg> action) {
|
||||
if (match(index, match)) {
|
||||
action.accept(get(index));
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return args.length;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return args.length == 0;
|
||||
}
|
||||
|
||||
public static class Arg {
|
||||
private final String arg;
|
||||
|
||||
public Arg(String arg) {
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
public int toInt() {
|
||||
return Integer.parseInt(arg);
|
||||
}
|
||||
|
||||
public long toLong() {
|
||||
return Long.parseLong(arg);
|
||||
}
|
||||
|
||||
public byte toByte() {
|
||||
return Byte.parseByte(arg);
|
||||
}
|
||||
|
||||
public short toShort() {
|
||||
return Short.parseShort(arg);
|
||||
}
|
||||
|
||||
public double toDouble() {
|
||||
return Double.parseDouble(arg);
|
||||
}
|
||||
|
||||
public float toFloat() {
|
||||
return Float.parseFloat(arg);
|
||||
}
|
||||
|
||||
public boolean toBool() {
|
||||
return Boolean.parseBoolean(arg);
|
||||
}
|
||||
|
||||
public char toChar() {
|
||||
return arg.isEmpty() ? ' ' : arg.charAt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return arg;
|
||||
}
|
||||
|
||||
public <T extends Enum<?>> T toEnum(Class<T> enumType) {
|
||||
return toEnum(enumType, null);
|
||||
}
|
||||
|
||||
public <T extends Enum<?>> T toEnum(Class<T> enumType, T fallback) {
|
||||
String arg = this.arg.replace('-', '_');
|
||||
for (T constant : enumType.getEnumConstants())
|
||||
if (arg.equalsIgnoreCase(constant.name()))
|
||||
return constant;
|
||||
|
||||
if (fallback == null)
|
||||
throw new IllegalArgumentException("'%s' is not a value of %s".formatted(arg, enumType.getSimpleName()));
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/main/java/me/trouper/alias/server/commands/CommandRegistry.java
Executable file
19
src/main/java/me/trouper/alias/server/commands/CommandRegistry.java
Executable file
@@ -0,0 +1,19 @@
|
||||
package me.trouper.alias.server.commands;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CommandRegistry {
|
||||
|
||||
String value();
|
||||
String usage() default "none";
|
||||
Permission permission() default @Permission("");
|
||||
boolean printStackTrace() default false;
|
||||
boolean playersAllowed() default true;
|
||||
boolean consoleAllowed() default true;
|
||||
boolean blocksAllowed() default true;
|
||||
}
|
||||
14
src/main/java/me/trouper/alias/server/commands/Permission.java
Executable file
14
src/main/java/me/trouper/alias/server/commands/Permission.java
Executable file
@@ -0,0 +1,14 @@
|
||||
package me.trouper.alias.server.commands;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Permission {
|
||||
|
||||
String value();
|
||||
String message() default "You do not have permission for this command!";
|
||||
}
|
||||
111
src/main/java/me/trouper/alias/server/commands/QuickCommand.java
Normal file
111
src/main/java/me/trouper/alias/server/commands/QuickCommand.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package me.trouper.alias.server.commands;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import me.trouper.alias.server.commands.completions.CompletionBuilder;
|
||||
import me.trouper.alias.server.commands.completions.CompletionNode;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface QuickCommand extends TabExecutor, Main {
|
||||
|
||||
|
||||
void handleCommand(CommandSender sender, Command command, String label, Args args);
|
||||
void handleCompletion(CommandSender sender, Command command, String label, Args args, CompletionBuilder b);
|
||||
|
||||
default void register() {
|
||||
CommandRegistry registry = this.getClass().getAnnotation(CommandRegistry.class);
|
||||
PluginCommand command = getPlugin().getCommand(registry.value());
|
||||
|
||||
if (command != null) {
|
||||
command.setExecutor(this);
|
||||
command.setTabCompleter(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
|
||||
CommandRegistry registry = this.getClass().getAnnotation(CommandRegistry.class);
|
||||
if (registry == null) {
|
||||
return true;
|
||||
}
|
||||
switch (sender) {
|
||||
case Player player when !registry.playersAllowed() -> {
|
||||
error(sender, Component.text("Players are not allowed to run this command!"));
|
||||
return true;
|
||||
}
|
||||
case ConsoleCommandSender consoleCommandSender when !registry.consoleAllowed() -> {
|
||||
error(sender, Component.text("This command cannot be ran from the console!"));
|
||||
return true;
|
||||
}
|
||||
case BlockCommandSender blockCommandSender when !registry.blocksAllowed() -> {
|
||||
error(sender, Component.text("This command cannot be ran from a command block!"));
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String perm = registry.permission().value();
|
||||
if (perm != null && !perm.isEmpty() && !sender.hasPermission(perm)) {
|
||||
error(sender, Component.text(registry.permission().message()));
|
||||
return true;
|
||||
}
|
||||
handleCommand(sender, command, label, new Args(args));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (registry.printStackTrace()) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
error(sender, Component.text("Correct Usage: {0}"),Component.text(registry.usage()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
|
||||
try {
|
||||
CompletionBuilder b = new CompletionBuilder(label);
|
||||
handleCompletion(sender, command, label, new Args(args),b);
|
||||
CompletionNode node = b.getRootNode();
|
||||
|
||||
if (args.length == 0) {
|
||||
return node.getOptions();
|
||||
}
|
||||
for (int i = 0; i < args.length - 1; i++) {
|
||||
node = node.next(args[i]);
|
||||
}
|
||||
|
||||
String end = args[args.length - 1];
|
||||
List<String> a = new ArrayList<>(node.getOptions());
|
||||
|
||||
if (node.isOptionsRegex()) {
|
||||
List<String> regexResult = new ArrayList<>();
|
||||
for (CompletionNode option : node.getNextOptions()) {
|
||||
boolean regexMatches = CompletionNode.containsRegex(option, end) || end.isEmpty();
|
||||
for (String s : option.getValues())
|
||||
regexResult.add((regexMatches ? "§d" : "§c") + s + "§r");
|
||||
}
|
||||
return regexResult;
|
||||
}
|
||||
else {
|
||||
a.removeIf(s -> !s.toLowerCase().contains(end.toLowerCase()));
|
||||
return a;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
default CommandRegistry getRegistry() {
|
||||
return this.getClass().getAnnotation(CommandRegistry.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package me.trouper.alias.server.commands.completions;
|
||||
|
||||
import me.trouper.alias.utils.misc.ArrayUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CompletionBuilder {
|
||||
|
||||
private final CompletionNode root;
|
||||
private final List<CompletionBuilder> options;
|
||||
private boolean isBranch;
|
||||
private String regex;
|
||||
|
||||
CompletionBuilder(List<String> names) {
|
||||
this.root = new CompletionNode(names, new ArrayList<>(), null);
|
||||
this.options = new ArrayList<>();
|
||||
this.isBranch = false;
|
||||
}
|
||||
|
||||
public CompletionBuilder(String names) {
|
||||
this.root = new CompletionNode(names);
|
||||
this.options = new ArrayList<>();
|
||||
this.isBranch = false;
|
||||
}
|
||||
|
||||
public CompletionBuilder(String regex, String details) {
|
||||
this.root = new CompletionNode(Collections.singletonList(details), new ArrayList<>(), regex);
|
||||
this.options = new ArrayList<>();
|
||||
this.isBranch = false;
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public CompletionBuilder then(CompletionBuilder arg) {
|
||||
options.add(arg);
|
||||
root.nextOptions.add(arg.root);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompletionBuilder arg(List<String> name) {
|
||||
CompletionBuilder b = new CompletionBuilder(name);
|
||||
b.isBranch = true;
|
||||
return b;
|
||||
}
|
||||
|
||||
public CompletionBuilder argRegex(String regex, String details) {
|
||||
CompletionBuilder b = new CompletionBuilder(regex, details);
|
||||
b.isBranch = true;
|
||||
return b;
|
||||
}
|
||||
|
||||
public CompletionBuilder argInt(String details) {
|
||||
return argRegex("^ *\\-?\\d+ *$", details);
|
||||
}
|
||||
|
||||
public CompletionBuilder argPosInt(String details) {
|
||||
return argRegex("^ *\\d+ *$", details);
|
||||
}
|
||||
|
||||
public CompletionBuilder argDecimal(String details) {
|
||||
return argRegex("^ *\\-?\\d*\\.?\\d+ *$", details);
|
||||
}
|
||||
|
||||
public CompletionBuilder argPosDecimal(String details) {
|
||||
return argRegex("^ *\\d*\\.?\\d+ *$", details);
|
||||
}
|
||||
|
||||
public CompletionBuilder argBool() {
|
||||
return arg("true", "false");
|
||||
}
|
||||
|
||||
public CompletionBuilder argEnum(Class<? extends Enum<?>> type, boolean lowercase) {
|
||||
return arg(ArrayUtils.enumNames(type, lowercase));
|
||||
}
|
||||
|
||||
public CompletionBuilder argEnum(Class<? extends Enum<?>> type) {
|
||||
return argEnum(type, true);
|
||||
}
|
||||
|
||||
public CompletionBuilder argOnlinePlayers() {
|
||||
return arg(ArrayUtils.playerNames());
|
||||
}
|
||||
|
||||
public CompletionBuilder arg(String... names) {
|
||||
return arg(Arrays.asList(names));
|
||||
}
|
||||
|
||||
public CompletionBuilder arg(String name) {
|
||||
return arg(Collections.singletonList(name));
|
||||
}
|
||||
|
||||
public <T> CompletionBuilder arg(Collection<T> input, Function<T, String> toString) {
|
||||
return arg(input.stream().map(toString).toList());
|
||||
}
|
||||
|
||||
public CompletionBuilder next(String name) {
|
||||
for (CompletionBuilder o : options) {
|
||||
if (CompletionNode.strictContains(o.root, name)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompletionNode getRootNode() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public CompletionNode build() {
|
||||
if (this.isBranch) {
|
||||
throw new IllegalArgumentException("build() cannot be called on branches!");
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
public boolean isBranch() {
|
||||
return isBranch;
|
||||
}
|
||||
|
||||
public boolean isRegex() {
|
||||
return regex != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package me.trouper.alias.server.commands.completions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CompletionNode {
|
||||
|
||||
final List<String> values;
|
||||
final List<CompletionNode> nextOptions;
|
||||
final String regex;
|
||||
|
||||
CompletionNode(List<String> values, List<CompletionNode> nextOptions, String regex) {
|
||||
this.values = values;
|
||||
this.nextOptions = nextOptions;
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
CompletionNode(String values) {
|
||||
this(List.of(values), new ArrayList<>(), null);
|
||||
}
|
||||
|
||||
public static boolean strictContains(CompletionNode parent, String subject) {
|
||||
for (String value : parent.values)
|
||||
if (value.equals(subject))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean contains(CompletionNode parent, String subject) {
|
||||
for (String value : parent.values)
|
||||
if (value.contains(subject))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean containsRegex(CompletionNode parent, String subject) {
|
||||
if (parent.regex == null)
|
||||
return false;
|
||||
return subject.matches(parent.regex);
|
||||
}
|
||||
|
||||
public boolean optionsRegexMatchesArg(String argument) {
|
||||
for (CompletionNode option : nextOptions)
|
||||
if (containsRegex(option, argument))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public CompletionNode next(String argument) {
|
||||
for (CompletionNode option : nextOptions)
|
||||
if (containsRegex(option, argument))
|
||||
return option;
|
||||
|
||||
for (CompletionNode option : nextOptions)
|
||||
if (strictContains(option, argument))
|
||||
return option;
|
||||
|
||||
for (CompletionNode option : nextOptions)
|
||||
if (contains(option, argument))
|
||||
return option;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
List<String> a = new ArrayList<>();
|
||||
for (CompletionNode o : nextOptions) {
|
||||
a.addAll(o.values);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
public boolean isRegex() {
|
||||
return regex != null;
|
||||
}
|
||||
|
||||
public boolean isOptionsRegex() {
|
||||
return nextOptions.stream().anyMatch(CompletionNode::isRegex);
|
||||
}
|
||||
|
||||
public List<CompletionNode> getNextOptions() {
|
||||
return nextOptions;
|
||||
}
|
||||
|
||||
public List<String> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.trouper.alias.server.events;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
|
||||
public class GuiListener implements QuickListener {
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent e) {
|
||||
QuickGui.handleClick(e);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent e) {
|
||||
QuickGui.handleClose(e);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryDrag(InventoryDragEvent e) {
|
||||
QuickGui.handleDrag(e);
|
||||
}
|
||||
}
|
||||
4
src/main/java/me/trouper/alias/server/events/QuickListener.java
Executable file → Normal file
4
src/main/java/me/trouper/alias/server/events/QuickListener.java
Executable file → Normal file
@@ -5,8 +5,8 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public interface QuickListener extends Listener, Main {
|
||||
default QuickListener registerEvents() {
|
||||
Bukkit.getPluginManager().registerEvents(this, this.getPlugin());
|
||||
default QuickListener register() {
|
||||
Bukkit.getPluginManager().registerEvents(this,this.getPlugin());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.trouper.alias.server.systems;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public abstract class AbstractWand implements QuickListener, Main {
|
||||
|
||||
private final String usePermission;
|
||||
private final ItemStack wandItem;
|
||||
|
||||
public AbstractWand(String usePermission, ItemStack wandItem) {
|
||||
this.wandItem = wandItem.clone();
|
||||
this.usePermission = usePermission;
|
||||
}
|
||||
|
||||
public String getUsePermission() {
|
||||
return usePermission;
|
||||
}
|
||||
public ItemStack getWandItem() {
|
||||
return wandItem.clone();
|
||||
}
|
||||
|
||||
private boolean isWand(ItemStack item) {
|
||||
return item != null && item.isSimilar(wandItem);
|
||||
}
|
||||
|
||||
private boolean isHoldingWand(Player p) {
|
||||
ItemStack inMain = p.getInventory().getItemInMainHand();
|
||||
ItemStack inOff = p.getInventory().getItemInOffHand();
|
||||
return isWand(inMain) || isWand(inOff);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSwapHands(PlayerSwapHandItemsEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
|
||||
e.setCancelled(true);
|
||||
|
||||
if (p.isSneaking()) onSwapHandSneak(p);
|
||||
else onSwapHand(p);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onAnimate(PlayerAnimationEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
|
||||
if (!e.getAnimationType().equals(PlayerAnimationType.ARM_SWING)) return;
|
||||
if (e.getPlayer().getTargetEntity(5) == null) return;
|
||||
e.setCancelled(true);
|
||||
|
||||
if (p.isSneaking()) onLeftClickSneak(p);
|
||||
else onLeftClick(p);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInteract(PlayerInteractEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
|
||||
|
||||
Action action = e.getAction();
|
||||
|
||||
if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return;
|
||||
e.setCancelled(true);
|
||||
|
||||
if (p.isSneaking()) onRightClickSneak(p);
|
||||
else onRightClick(p);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onScroll(PlayerItemHeldEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
|
||||
|
||||
int prev = e.getPreviousSlot();
|
||||
int curr = e.getNewSlot();
|
||||
|
||||
if (!p.isSneaking() || !isWand(p.getInventory().getItem(prev))) return;
|
||||
|
||||
if (curr < prev) onScrollUp(e.getPlayer());
|
||||
else if (curr > prev) onScrollDown(e.getPlayer());
|
||||
}
|
||||
|
||||
protected void onSwapHand(Player player) {}
|
||||
protected void onSwapHandSneak(Player player) {}
|
||||
protected void onRightClick(Player player) {}
|
||||
protected void onRightClickSneak(Player player) {}
|
||||
protected void onLeftClick(Player player) {}
|
||||
protected void onLeftClickSneak(Player player) {}
|
||||
protected void onScrollUp(Player player) {}
|
||||
protected void onScrollDown(Player player) {}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.trouper.alias.server.systems;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TaskManager implements Main {
|
||||
private final ConcurrentHashMap<Integer, Boolean> tasks = new ConcurrentHashMap<>();
|
||||
private volatile boolean closed = false;
|
||||
|
||||
public int scheduleTask(Runnable task, long delay) {
|
||||
if (closed) return -1;
|
||||
|
||||
int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> {
|
||||
if (!closed && tasks.containsKey(taskId)) {
|
||||
task.run();
|
||||
tasks.remove(taskId);
|
||||
}
|
||||
}, delay).getTaskId();
|
||||
|
||||
if (!closed) {
|
||||
tasks.put(taskId, Boolean.TRUE);
|
||||
return taskId;
|
||||
} else {
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
tasks.keySet().forEach(Bukkit.getScheduler()::cancelTask);
|
||||
tasks.clear();
|
||||
}
|
||||
}
|
||||
469
src/main/java/me/trouper/alias/server/systems/Text.java
Normal file
469
src/main/java/me/trouper/alias/server/systems/Text.java
Normal file
@@ -0,0 +1,469 @@
|
||||
package me.trouper.alias.server.systems;
|
||||
|
||||
import me.trouper.alias.Alias;
|
||||
import me.trouper.alias.server.Main;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.TextReplacementConfig;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.SoundCategory;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Text implements Main {
|
||||
|
||||
/**
|
||||
* Messages an audience applying pallet formatting to the text and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* Supports both flat messages and fancy wrapped messages based on Alias configuration.
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param playSound If the pallet's sound should be played.
|
||||
* @param audience Any audience.
|
||||
* @param text The message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
*/
|
||||
public static void messageAny(Pallet pallet, boolean playSound, Audience audience, String text, Object... args) {
|
||||
message(pallet, playSound, audience, color(text), Arrays.stream(args).map(object -> Component.text(String.valueOf(object))).toArray(ComponentLike[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages an audience applying pallet formatting to the text and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* Supports both flat messages and fancy wrapped messages based on Alias configuration.
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param audience Any audience.
|
||||
* @param text The message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
*/
|
||||
public static void messageAny(Pallet pallet, Audience audience, String text, Object... args) {
|
||||
messageAny(pallet,true,audience,text,args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages an audience applying pallet formatting to the component and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* Preserves existing formatting like click events and hover events.
|
||||
* Supports both flat messages and fancy wrapped messages based on Alias configuration.
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param playSound If the pallet's sound should be played.
|
||||
* @param audience Any audience.
|
||||
* @param text The component message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
*/
|
||||
public static void message(Pallet pallet, boolean playSound, Audience audience, ComponentLike text, ComponentLike... args) {
|
||||
Component message = getMessage(pallet, text, args);
|
||||
audience.sendMessage(message);
|
||||
if (playSound && audience instanceof Player p) p.playSound(p.getLocation(), pallet.sound.sound, SoundCategory.VOICE, 10f, pallet.sound.pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages an audience applying pallet formatting to the component and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* Preserves existing formatting like click events and hover events.
|
||||
* Supports both flat messages and fancy wrapped messages based on Alias configuration.
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param audience Any audience.
|
||||
* @param text The component message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
*/
|
||||
public static void message(Pallet pallet, Audience audience, ComponentLike text, ComponentLike... args) {
|
||||
message(pallet,true,audience,text,args);
|
||||
}
|
||||
|
||||
public static void sendWarning(Audience audience, String warning, Object... args) {
|
||||
messageAny(Pallet.WARNING, audience, warning, args);
|
||||
}
|
||||
|
||||
public static void sendError(Audience audience, String error, Object... args) {
|
||||
messageAny(Pallet.ERROR, audience, error, args);
|
||||
}
|
||||
|
||||
public static void sendInfo(Audience audience, String info, Object... args) {
|
||||
messageAny(Pallet.INFO, audience, info, args);
|
||||
}
|
||||
|
||||
public static void sendSuccess(Audience audience, String success, Object... args) {
|
||||
messageAny(Pallet.SUCCESS, audience, success, args);
|
||||
}
|
||||
|
||||
public static void sendMessage(Audience audience, String text, Object... args) {
|
||||
messageAny(Pallet.NEUTRAL, audience, text, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component form of a message, applying pallet formatting to the text and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param text The message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
* @return The final component, formatted according to flat/fancy setting.
|
||||
*/
|
||||
public static Component getMessageAny(Pallet pallet, String text, Object... args) {
|
||||
return getMessage(pallet, color(text), Arrays.stream(args).map(arg -> color(String.valueOf(arg))).toArray(ComponentLike[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component form of a message, applying pallet formatting to the component and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
|
||||
* Preserves existing formatting like click events and hover events.
|
||||
* Supports both flat messages and fancy wrapped messages based on Alias configuration.
|
||||
* @param pallet The colors to use for text and arguments.
|
||||
* @param text The component message to format
|
||||
* @param args Qualified placeholders to color.
|
||||
* @return The final component, formatted according to flat/fancy setting.
|
||||
*/
|
||||
public static Component getMessage(Pallet pallet, ComponentLike text, ComponentLike... args) {
|
||||
Component formattedMessage = format(pallet, text, args);
|
||||
|
||||
if (main.getCommon().useFlat()) {
|
||||
return formatFlatMessage(formattedMessage);
|
||||
} else {
|
||||
return formatFancyMessage(formattedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a message as a flat prefixed message.
|
||||
* @param message The formatted message component
|
||||
* @return The message with flat prefix applied
|
||||
*/
|
||||
private static Component formatFlatMessage(Component message) {
|
||||
Component prefix = color(main.getCommon().getFlatPrefix());
|
||||
return prefix.append(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a message as a fancy wrapped message with line prefixes.
|
||||
* Uses native Adventure API component processing for consistency.
|
||||
* @param message The formatted message component
|
||||
* @return The message with fancy formatting and line wrapping
|
||||
*/
|
||||
private static Component formatFancyMessage(Component message) {
|
||||
List<Component> wrappedLines = wrapComponent(message, 50, (int) Math.round((main.getCommon().getPluginName().length() + 3) * 1.3));
|
||||
// 50 is slightly below the average character width of someone's minecraft chat. The 3 is to account for the bolded "| " and the 1.3 is to account for bolding the plugin name.
|
||||
if (wrappedLines.isEmpty()) {
|
||||
wrappedLines.add(Component.empty());
|
||||
}
|
||||
|
||||
Component result = Component.empty().appendNewline();
|
||||
|
||||
Component firstLine = Component.empty()
|
||||
.append(Component.text("| ", TextColor.color(main.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD))
|
||||
.append(Component.text(main.getCommon().getPluginName() + " ", TextColor.color(main.getCommon().getMainColor()), TextDecoration.BOLD))
|
||||
.append(wrappedLines.get(0));
|
||||
|
||||
result = result.append(firstLine);
|
||||
|
||||
for (int i = 1; i < wrappedLines.size(); i++) {
|
||||
Component line = Component.empty()
|
||||
.append(Component.text("| ", TextColor.color(main.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD))
|
||||
.append(wrappedLines.get(i));
|
||||
|
||||
result = result.appendNewline().append(line);
|
||||
}
|
||||
|
||||
return result.appendNewline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a component into multiple lines based on visible character count.
|
||||
* Preserves all Adventure API formatting including colors, decorations, and events.
|
||||
* @param component The component to wrap
|
||||
* @param maxLineLength Maximum visible characters per line
|
||||
* @param firstLineOffset Offset for the first line (plugin name length)
|
||||
* @return List of wrapped component lines
|
||||
*/
|
||||
private static List<Component> wrapComponent(Component component, int maxLineLength, int firstLineOffset) {
|
||||
List<Component> lines = new ArrayList<>();
|
||||
List<ComponentWord> words = extractWords(component);
|
||||
|
||||
if (words.isEmpty()) {
|
||||
lines.add(Component.empty());
|
||||
return lines;
|
||||
}
|
||||
|
||||
Component currentLine = Component.empty();
|
||||
int currentLineLength = firstLineOffset;
|
||||
boolean isFirstLine = true;
|
||||
|
||||
for (int i = 0; i < words.size(); i++) {
|
||||
ComponentWord word = words.get(i);
|
||||
int wordLength = word.visibleLength();
|
||||
int spaceNeeded = (currentLine.equals(Component.empty()) ? 0 : 1) + wordLength;
|
||||
|
||||
if (currentLineLength + spaceNeeded > maxLineLength && !currentLine.equals(Component.empty())) {
|
||||
lines.add(currentLine);
|
||||
currentLine = Component.empty();
|
||||
currentLineLength = 0;
|
||||
}
|
||||
|
||||
if (!currentLine.equals(Component.empty())) {
|
||||
currentLine = currentLine.append(Component.space());
|
||||
currentLineLength++;
|
||||
}
|
||||
|
||||
currentLine = currentLine.append(word.component());
|
||||
currentLineLength += wordLength;
|
||||
}
|
||||
|
||||
if (!currentLine.equals(Component.empty())) {
|
||||
lines.add(currentLine);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts words from a component while preserving their formatting.
|
||||
* @param component The component to extract words from
|
||||
* @return List of ComponentWord objects
|
||||
*/
|
||||
private static List<ComponentWord> extractWords(Component component) {
|
||||
List<ComponentWord> words = new ArrayList<>();
|
||||
extractWordsRecursive(component, Style.empty(), words);
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively extracts words from a component tree, preserving inherited styles.
|
||||
* @param component The current component
|
||||
* @param inheritedStyle The style inherited from parent components
|
||||
* @param words The list to add words to
|
||||
*/
|
||||
private static void extractWordsRecursive(Component component, Style inheritedStyle, List<ComponentWord> words) {
|
||||
Style currentStyle = inheritedStyle.merge(component.style());
|
||||
|
||||
|
||||
if (component instanceof TextComponent textComponent) {
|
||||
String text = textComponent.content();
|
||||
if (!text.isEmpty()) {
|
||||
|
||||
String[] textWords = text.split("\\s+");
|
||||
for (String word : textWords) {
|
||||
if (!word.isEmpty()) {
|
||||
Component wordComponent = Component.text(word).style(currentStyle);
|
||||
words.add(new ComponentWord(wordComponent, getVisibleLength(word)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Component child : component.children()) {
|
||||
extractWordsRecursive(child, currentStyle, words);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visible length of text, excluding formatting codes.
|
||||
* @param text The text to measure
|
||||
* @return The visible character count
|
||||
*/
|
||||
private static int getVisibleLength(String text) {
|
||||
return PlainTextComponentSerializer.plainText().serialize(Component.text(text)).length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for LegacyComponentSerializer, using ampersand (&) codes.
|
||||
* @param msg the legacy text
|
||||
* @return The deserialized component
|
||||
*/
|
||||
public static Component color(String msg) {
|
||||
if (msg.contains("§")) return LegacyComponentSerializer.legacySection().deserialize(msg);
|
||||
return LegacyComponentSerializer.legacyAmpersand().deserialize(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ampersand codes to section codes.
|
||||
* @param ampersands String with ampersand codes
|
||||
* @return String with section codes
|
||||
*/
|
||||
public static String legacyAmpersandColor(String ampersands) {
|
||||
return ampersands.replaceAll("&","§");
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a string message with pallet colors and argument replacement.
|
||||
* @param pallet The color pallet to use
|
||||
* @param text The message text
|
||||
* @param args Arguments to replace placeholders
|
||||
* @return Formatted component
|
||||
*/
|
||||
public static Component format(Pallet pallet, String text, Object... args) {
|
||||
return format(pallet, Component.text(text), Arrays.stream(args).map(arg->Component.text(arg.toString())).toArray(Component[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a component message with pallet colors and argument replacement.
|
||||
* Placeholders are zero-indexed and curly braced: {0}, {1}, {2}...
|
||||
* @param pallet The color pallet to use
|
||||
* @param text The message component
|
||||
* @param args Argument components to replace placeholders
|
||||
* @return Formatted component with colors applied
|
||||
*/
|
||||
public static Component format(Pallet pallet, ComponentLike text, ComponentLike... args) {
|
||||
Component resultComponent = text.asComponent().color(pallet.mainText);
|
||||
|
||||
if (args == null || args.length == 0) {
|
||||
return resultComponent;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Component argument = args[i].asComponent();
|
||||
if (shouldRecolor(argument)) {
|
||||
TextColor newColor = getArgColor(pallet, i);
|
||||
argument = argument.color(newColor);
|
||||
}
|
||||
|
||||
TextReplacementConfig replacementConfig = TextReplacementConfig.builder()
|
||||
.matchLiteral("{" + i + "}")
|
||||
.replacement(argument)
|
||||
.build();
|
||||
|
||||
resultComponent = resultComponent.replaceText(replacementConfig);
|
||||
}
|
||||
|
||||
return resultComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an argument component should have its color overridden by the pallet.
|
||||
* @param component The component to check.
|
||||
* @return Currently always returns true, indicating recoloring should occur.
|
||||
*/
|
||||
private static boolean shouldRecolor(Component component) {
|
||||
Set<TextColor> colors = new HashSet<>();
|
||||
collectColors(component,colors);
|
||||
return colors.size() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively checks a component, adding its colors to a set.
|
||||
* @param component The component to collect.
|
||||
* @param colors A mutable HashSet of colors.
|
||||
*/
|
||||
private static void collectColors(Component component, Set<TextColor> colors) {
|
||||
if (component.color() != null) {
|
||||
colors.add(component.color());
|
||||
}
|
||||
for (Component child : component.children()) {
|
||||
collectColors(child, colors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes color codes from a string.
|
||||
* @param input The input string
|
||||
* @return String with color codes removed
|
||||
*/
|
||||
public static String removeColors(String input) {
|
||||
if (input == null) return null;
|
||||
|
||||
input = input.replaceAll("(?i)[&§][0-9a-fk-or]", ""); // Legacy colors
|
||||
input = input.replaceAll("(?i)[&§]#[a-f0-9]{6}", ""); // Legacy hex colors
|
||||
input = input.replaceAll("(?i)§x(§[a-f0-9]){6}", ""); // Old hex colors
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes color codes from a component, returning plain text.
|
||||
* @param input The input component
|
||||
* @return Component with plain text only
|
||||
*/
|
||||
public static Component removeColors(ComponentLike input) {
|
||||
if (input == null) return Component.text("");
|
||||
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(input.asComponent());
|
||||
return Component.text(plainText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate argument color based on the argument index.
|
||||
* @param pallet The color pallet
|
||||
* @param argIndex The argument index (0-indexed)
|
||||
* @return The appropriate TextColor for the argument
|
||||
*/
|
||||
private static TextColor getArgColor(Pallet pallet, int argIndex) {
|
||||
return switch (argIndex) {
|
||||
case 1 -> pallet.arg2;
|
||||
case 2 -> pallet.arg3;
|
||||
default -> pallet.argDefault;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a word extracted from a component with its formatting preserved.
|
||||
*/
|
||||
private static record ComponentWord(Component component, int visibleLength) {}
|
||||
|
||||
/**
|
||||
* Color pallets for different message types with appropriate colors and sounds.
|
||||
*/
|
||||
public enum Pallet {
|
||||
ERROR(
|
||||
TextColor.color(0xD3A6A4), // Soft red for main text
|
||||
TextColor.color(0xFFF1AE), // Light yellow for default args
|
||||
TextColor.color(0xFF796D), // Coral for second arg
|
||||
TextColor.color(0xC62828), // Dark red for third arg
|
||||
new SoundData(Sound.BLOCK_NOTE_BLOCK_BASS, 1)
|
||||
),
|
||||
WARNING(
|
||||
TextColor.color(0xFFF3CD), // Light yellow for main text
|
||||
TextColor.color(0xFFF9F5), // Very light cream for default args
|
||||
TextColor.color(0xFFD54F), // Gold for second arg
|
||||
TextColor.color(0xFFA000), // Orange for third arg
|
||||
new SoundData(Sound.BLOCK_NOTE_BLOCK_BIT, 0.5F)
|
||||
),
|
||||
INFO(
|
||||
TextColor.color(0xBBDEFB), // Light blue for main text
|
||||
TextColor.color(0xD2D0EA), // Light lavender for default args
|
||||
TextColor.color(0x64B5F6), // Medium blue for second arg
|
||||
TextColor.color(0x1976D2), // Dark blue for third arg
|
||||
new SoundData(Sound.BLOCK_NOTE_BLOCK_CHIME, 0.7F)
|
||||
),
|
||||
SUCCESS(
|
||||
TextColor.color(0xCDFFC7), // Light green for main text
|
||||
TextColor.color(0xFFFFFF), // White for default args
|
||||
TextColor.color(0xB0FFE3), // Light mint for second arg
|
||||
TextColor.color(0x63CD83), // Medium green for third arg
|
||||
new SoundData(Sound.BLOCK_NOTE_BLOCK_PLING, 1.5F)
|
||||
),
|
||||
NEUTRAL(
|
||||
TextColor.color(0xD3D3D3), // Light gray for main text
|
||||
TextColor.color(0xFFFFFF), // White for default args
|
||||
TextColor.color(0xFFB3F8), // Light pink for second arg
|
||||
TextColor.color(0xE280FF), // Purple for third arg
|
||||
new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL, 1)
|
||||
),
|
||||
LOCATION(
|
||||
TextColor.color(0xAAAAAA), // Gray for main text
|
||||
TextColor.color(0xFFB0C1), // Light pink for default args
|
||||
TextColor.color(0xB6F5B6), // Light green for second arg
|
||||
TextColor.color(0xB0C1FF), // Light blue for third arg
|
||||
new SoundData(Sound.UI_HUD_BUBBLE_POP, 2)
|
||||
);
|
||||
|
||||
private final TextColor mainText;
|
||||
private final TextColor argDefault;
|
||||
private final TextColor arg2;
|
||||
private final TextColor arg3;
|
||||
private final SoundData sound;
|
||||
|
||||
Pallet(TextColor mainText, TextColor argDefault, TextColor arg2, TextColor arg3, SoundData sound) {
|
||||
this.mainText = mainText;
|
||||
this.argDefault = argDefault;
|
||||
this.arg2 = arg2;
|
||||
this.arg3 = arg3;
|
||||
this.sound = sound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sound data for message types.
|
||||
* @param sound The sound to play
|
||||
* @param pitch The pitch of the sound
|
||||
*/
|
||||
public record SoundData(Sound sound, float pitch) {}
|
||||
}
|
||||
52
src/main/java/me/trouper/alias/server/systems/Verbose.java
Normal file
52
src/main/java/me/trouper/alias/server/systems/Verbose.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package me.trouper.alias.server.systems;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class Verbose implements Main {
|
||||
|
||||
/**
|
||||
* A dynamic verbose system which uses the format from the {@link Text} system.
|
||||
* @param backtrace The number of calls up the stacktrace to go.
|
||||
* @param verbose A message with 0 indexed curly brace placeholders. {0}, {1}, {2}...
|
||||
* @param args Qualified placeholder values.
|
||||
*/
|
||||
public static void send(int backtrace, String verbose, Object... args) {
|
||||
if (!main.getCommon().getDebugMode()) return;
|
||||
String callerInfo = "Unknown Caller";
|
||||
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
if (stackTrace.length > 2 + backtrace) {
|
||||
StackTraceElement caller = stackTrace[2 + backtrace];
|
||||
|
||||
String className = caller.getClassName();
|
||||
className = className.substring(className.lastIndexOf(".") + 1);
|
||||
if (className.contains("-")) callerInfo = "Protected";
|
||||
else callerInfo = className + "." + caller.getMethodName();
|
||||
|
||||
|
||||
if (main.getCommon().getDebuggerExclusions().contains(callerInfo)) return;
|
||||
}
|
||||
|
||||
Component message = Text.format(Text.Pallet.INFO,verbose,args);
|
||||
message = Text.format(Text.Pallet.INFO,Component.text("{0} [DEBUG ^ {1}] [{2}] » {3}"),Component.text(main.getCommon().getPluginName()), Component.text(backtrace), Component.text(callerInfo), message);
|
||||
|
||||
main.getPlugin().getComponentLogger().info(message);
|
||||
|
||||
for (Player operator : Bukkit.getOnlinePlayers()) {
|
||||
if (!operator.isOp()) continue;
|
||||
operator.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dynamic verbose system which uses the format from the {@link Text} system.
|
||||
* @param verbose A message with 0 indexed curly brace placeholders. {0}, {1}, {2}...
|
||||
* @param args Qualified placeholder values.
|
||||
*/
|
||||
public static void send(String verbose, Object... args) {
|
||||
send(1,verbose,args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package me.trouper.alias.server.systems.burning;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockSupport;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class BlockBurner implements Closeable, Main {
|
||||
private final BurnOptions options;
|
||||
private final BurnPalette palette;
|
||||
private boolean isClosed = false;
|
||||
private final Set<Block> visited = new HashSet<>();
|
||||
private final Map<Block, Material> burning = new HashMap<>();
|
||||
private final Set<Integer> tasks = new HashSet<>();
|
||||
|
||||
public BlockBurner(BurnOptions options) {
|
||||
this.options = options;
|
||||
this.palette = new BurnPalette();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
tasks.forEach(task -> Bukkit.getScheduler().cancelTask(task));
|
||||
visited.clear();
|
||||
burning.clear();
|
||||
isClosed = true;
|
||||
}
|
||||
|
||||
public void burn(Block block, float heat) {
|
||||
if (isOccluded(block)) return;
|
||||
if (visited.contains(block)) return;
|
||||
|
||||
visited.add(block);
|
||||
|
||||
if (options.isDisabled()) return;
|
||||
if (block.isLiquid() || hasWater(block)) return;
|
||||
|
||||
Block blockBelow = block.getRelative(0, -1, 0);
|
||||
if (block.getType().isAir() && canPlaceFireOn(blockBelow)) {
|
||||
if (ThreadLocalRandom.current().nextFloat() > options.getSetFireChance()) return;
|
||||
|
||||
if (isClosed()) return;
|
||||
setBlock(block, palette.getFirePalette().getFirst());
|
||||
|
||||
if (isClosed()) return;
|
||||
int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), ()->{
|
||||
if (!canPlaceFireOn(blockBelow)) {
|
||||
setBlock(block, Material.AIR);
|
||||
}
|
||||
},20 * 10).getTaskId();
|
||||
|
||||
if (!isClosed()) {
|
||||
tasks.add(taskId);
|
||||
} else {
|
||||
tasks.add(taskId);
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.getType().isAir()) return;
|
||||
|
||||
List<BurnStage> burnStages = palette.burn(block, heat);
|
||||
if (burnStages == null) return;
|
||||
|
||||
burning.put(block, block.getType());
|
||||
scheduleBurnStages(block, burnStages);
|
||||
}
|
||||
|
||||
private void scheduleBurnStages(Block block, List<BurnStage> stages) {
|
||||
long totalDelay = 0;
|
||||
|
||||
for (BurnStage stage : stages) {
|
||||
totalDelay += stage.getDelay();
|
||||
if (isClosed()) return;
|
||||
|
||||
int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), ()->{
|
||||
if (block.getType().isAir()) return;
|
||||
setBlock(block, stage.getBlockData());
|
||||
},totalDelay).getTaskId();
|
||||
|
||||
if (!isClosed()) {
|
||||
tasks.add(taskId);
|
||||
} else {
|
||||
tasks.add(taskId);
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOccluded(Block block) {
|
||||
return isOccluding(block.getRelative(0, 1, 0)) &&
|
||||
isOccluding(block.getRelative(0, -1, 0)) &&
|
||||
isOccluding(block.getRelative(1, 0, 0)) &&
|
||||
isOccluding(block.getRelative(-1, 0, 0)) &&
|
||||
isOccluding(block.getRelative(0, 0, 1)) &&
|
||||
isOccluding(block.getRelative(0, 0, -1));
|
||||
}
|
||||
|
||||
private boolean isOccluding(Block block) {
|
||||
Material material = burning.getOrDefault(block, block.getType());
|
||||
return material.isOccluding();
|
||||
}
|
||||
|
||||
private boolean canPlaceFireOn(Block block) {
|
||||
return block.getBlockData().isFaceSturdy(BlockFace.UP, BlockSupport.RIGID);
|
||||
}
|
||||
|
||||
private boolean hasWater(Block block) {
|
||||
Material type = block.getType();
|
||||
if (type == Material.KELP || type == Material.KELP_PLANT ||
|
||||
type == Material.SEAGRASS || type == Material.TALL_SEAGRASS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockData data = block.getBlockData();
|
||||
if (data instanceof Waterlogged) {
|
||||
return ((Waterlogged) data).isWaterlogged();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setBlock(Block block, Material material) {
|
||||
if (isClosed()) return;
|
||||
block.setType(material);
|
||||
}
|
||||
|
||||
private void setBlock(Block block, BlockData data) {
|
||||
if (isClosed()) return;
|
||||
block.setBlockData(data);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.trouper.alias.server.systems.burning;
|
||||
|
||||
public class BurnOptions {
|
||||
private boolean disabled = false;
|
||||
private double setFireChance = 1.0 / 80;
|
||||
|
||||
public boolean isDisabled() { return disabled; }
|
||||
public void setDisabled(boolean disabled) { this.disabled = disabled; }
|
||||
|
||||
public double getSetFireChance() { return setFireChance; }
|
||||
public void setSetFireChance(double setFireChance) { this.setFireChance = setFireChance; }
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package me.trouper.alias.server.systems.burning;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class BurnPalette {
|
||||
private final List<List<BlockData>> burnWave;
|
||||
private final List<Material> firePalette;
|
||||
private final List<BlockData> preBurn;
|
||||
private final List<BlockData> midBurn;
|
||||
private final List<BlockData> midHeatPalette;
|
||||
private final List<BlockData> highHeat;
|
||||
|
||||
private final List<BlockData> shortGrassPalette;
|
||||
private final List<BlockData> grassPalette;
|
||||
private final List<BlockData> stonePalette;
|
||||
private final List<BlockData> leavesPalette;
|
||||
private final List<BlockData> stoneBrickSlabPalette;
|
||||
private final List<BlockData> stoneBrickStairsPalette;
|
||||
private final List<BlockData> darkPrismarinePalette;
|
||||
private final List<BlockData> burnWaveTrail;
|
||||
|
||||
public BurnPalette() {
|
||||
// Initialize burn wave trail
|
||||
this.burnWaveTrail = Arrays.asList(
|
||||
Material.ORANGE_STAINED_GLASS.createBlockData(),
|
||||
Material.BLACK_STAINED_GLASS.createBlockData(),
|
||||
Material.GRAY_STAINED_GLASS.createBlockData(),
|
||||
Material.LIGHT_GRAY_STAINED_GLASS.createBlockData()
|
||||
);
|
||||
|
||||
// Initialize main burn wave
|
||||
List<BlockData> baseBurnWave = new ArrayList<>();
|
||||
addRepeated(baseBurnWave, Material.ORANGE_STAINED_GLASS, 3);
|
||||
addRepeated(baseBurnWave, Material.SHROOMLIGHT, 2);
|
||||
baseBurnWave.add(Material.ORANGE_TERRACOTTA.createBlockData());
|
||||
baseBurnWave.add(Material.ORANGE_CONCRETE.createBlockData());
|
||||
baseBurnWave.add(Material.HONEYCOMB_BLOCK.createBlockData());
|
||||
|
||||
this.burnWave = new ArrayList<>();
|
||||
for (BlockData block : baseBurnWave) {
|
||||
List<BlockData> waveStage = new ArrayList<>();
|
||||
waveStage.add(block);
|
||||
waveStage.add(block);
|
||||
waveStage.add(block);
|
||||
waveStage.addAll(burnWaveTrail);
|
||||
this.burnWave.add(waveStage);
|
||||
}
|
||||
|
||||
this.firePalette = Arrays.asList(Material.FIRE);
|
||||
this.preBurn = Arrays.asList(Material.ORANGE_TERRACOTTA.createBlockData());
|
||||
this.midBurn = Arrays.asList(
|
||||
Material.MAGMA_BLOCK.createBlockData(),
|
||||
Material.ORANGE_TERRACOTTA.createBlockData()
|
||||
);
|
||||
this.midHeatPalette = Arrays.asList(Material.MAGMA_BLOCK.createBlockData());
|
||||
this.highHeat = Arrays.asList(
|
||||
Material.MAGMA_BLOCK.createBlockData(),
|
||||
Material.BLACKSTONE.createBlockData(),
|
||||
Material.MUD.createBlockData(),
|
||||
Material.TUFF.createBlockData()
|
||||
);
|
||||
|
||||
this.shortGrassPalette = createShortGrassPalette();
|
||||
this.grassPalette = createGrassPalette();
|
||||
this.stonePalette = createStonePalette();
|
||||
this.leavesPalette = createLeavesPalette();
|
||||
this.stoneBrickSlabPalette = createStoneBrickSlabPalette();
|
||||
this.stoneBrickStairsPalette = createStoneBrickStairsPalette();
|
||||
this.darkPrismarinePalette = createDarkPrismarinePalette();
|
||||
}
|
||||
|
||||
public List<BurnStage> burn(Block block, float heat) {
|
||||
Material type = block.getType();
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
|
||||
if (type == Material.SHORT_GRASS) {
|
||||
return Arrays.asList(new BurnStage(
|
||||
random.nextLong(1, 10),
|
||||
getRandomElement(shortGrassPalette)
|
||||
));
|
||||
}
|
||||
|
||||
if (Tag.LOGS_THAT_BURN.isTagged(type)) {
|
||||
BlockData preservedData = Material.POLISHED_BASALT.createBlockData();
|
||||
block.getBlockData().copyTo(preservedData);
|
||||
|
||||
return Arrays.asList(
|
||||
new BurnStage(random.nextLong(0, 1), getRandomElement(preBurn)),
|
||||
new BurnStage(random.nextLong(1, 10), getRandomElement(midBurn)),
|
||||
new BurnStage(random.nextLong(20, 200), preservedData)
|
||||
);
|
||||
}
|
||||
|
||||
if (Tag.LEAVES.isTagged(type)) {
|
||||
return Arrays.asList(
|
||||
new BurnStage(random.nextLong(1, 10), getRandomElement(preBurn)),
|
||||
new BurnStage(random.nextLong(1, 10), getRandomElement(midBurn)),
|
||||
new BurnStage(random.nextLong(10, 80), getRandomElement(leavesPalette))
|
||||
);
|
||||
}
|
||||
|
||||
if (type == Material.STONE_BRICK_STAIRS) {
|
||||
BlockData preservedData = getRandomElement(stoneBrickStairsPalette).clone();
|
||||
block.getBlockData().copyTo(preservedData);
|
||||
return Arrays.asList(new BurnStage(random.nextLong(10, 60), preservedData));
|
||||
}
|
||||
|
||||
if (type == Material.STONE_BRICK_SLAB) {
|
||||
BlockData preservedData = getRandomElement(stoneBrickSlabPalette).clone();
|
||||
block.getBlockData().copyTo(preservedData);
|
||||
return Arrays.asList(new BurnStage(random.nextLong(10, 60), preservedData));
|
||||
}
|
||||
|
||||
Set<Material> grassTypes = Set.of(Material.GRASS_BLOCK, Material.DIRT, Material.SNOW_BLOCK,
|
||||
Material.SAND, Material.PODZOL, Material.COARSE_DIRT);
|
||||
if (grassTypes.contains(type)) {
|
||||
return Arrays.asList(
|
||||
new BurnStage(random.nextLong(1, 5), getRandomElement(midBurn)),
|
||||
new BurnStage(random.nextLong(10, 60), getRandomElement(applyHeat(grassPalette, heat)))
|
||||
);
|
||||
}
|
||||
|
||||
Set<Material> stoneTypes = Set.of(Material.STONE, Material.ANDESITE, Material.GRAVEL, Material.COBBLESTONE);
|
||||
if (stoneTypes.contains(type)) {
|
||||
return Arrays.asList(
|
||||
new BurnStage(random.nextLong(1, 5), getRandomElement(midBurn)),
|
||||
new BurnStage(random.nextLong(10, 60), getRandomElement(applyHeat(stonePalette, heat)))
|
||||
);
|
||||
}
|
||||
|
||||
if (type == Material.DARK_PRISMARINE) {
|
||||
return Arrays.asList(new BurnStage(
|
||||
random.nextLong(10, 60),
|
||||
getRandomElement(applyHeat(darkPrismarinePalette, heat))
|
||||
));
|
||||
}
|
||||
|
||||
if (Tag.WOODEN_FENCES.isTagged(type) || Tag.WOODEN_DOORS.isTagged(type) || Tag.WOODEN_TRAPDOORS.isTagged(type)) {
|
||||
if (random.nextFloat() < 0.2) return null;
|
||||
return Arrays.asList(new BurnStage(random.nextLong(10, 60), Material.AIR.createBlockData()));
|
||||
}
|
||||
|
||||
if (!type.isSolid()) {
|
||||
return Arrays.asList(new BurnStage(random.nextLong(1, 10), Material.AIR.createBlockData()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Material> getFirePalette() {
|
||||
return firePalette;
|
||||
}
|
||||
|
||||
public List<BlockData> getSmokePalette() {
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
|
||||
if (random.nextFloat() < 0.25) {
|
||||
List<Material> options = Arrays.asList(Material.BLACK_CONCRETE, Material.ORANGE_TERRACOTTA);
|
||||
return Arrays.asList(
|
||||
Material.SHROOMLIGHT.createBlockData(),
|
||||
Material.ORANGE_CONCRETE.createBlockData(),
|
||||
Material.ORANGE_TERRACOTTA.createBlockData(),
|
||||
options.get(random.nextInt(options.size())).createBlockData()
|
||||
);
|
||||
}
|
||||
|
||||
return Arrays.asList(
|
||||
Material.SHROOMLIGHT.createBlockData(),
|
||||
Material.ORANGE_CONCRETE.createBlockData(),
|
||||
Material.ORANGE_STAINED_GLASS.createBlockData(),
|
||||
Material.BLACK_STAINED_GLASS.createBlockData()
|
||||
);
|
||||
}
|
||||
|
||||
private List<BlockData> applyHeat(List<BlockData> list, float heat) {
|
||||
if (heat > 0.9) return highHeat;
|
||||
if (heat < 0.2) return list;
|
||||
|
||||
List<BlockData> result = new ArrayList<>(list);
|
||||
result.addAll(midHeatPalette);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addRepeated(List<BlockData> list, Material material, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
list.add(material.createBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T getRandomElement(List<T> list) {
|
||||
return list.get(ThreadLocalRandom.current().nextInt(list.size()));
|
||||
}
|
||||
|
||||
private List<BlockData> createShortGrassPalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.DEAD_BUSH, 12);
|
||||
palette.add(Material.DEAD_BRAIN_CORAL_FAN.createBlockData());
|
||||
palette.add(Material.DEAD_BRAIN_CORAL.createBlockData());
|
||||
palette.add(Material.DEAD_BUBBLE_CORAL_FAN.createBlockData());
|
||||
palette.add(Material.DEAD_FIRE_CORAL_FAN.createBlockData());
|
||||
palette.add(Material.DEAD_FIRE_CORAL.createBlockData());
|
||||
palette.add(Material.DEAD_HORN_CORAL_FAN.createBlockData());
|
||||
palette.add(Material.DEAD_TUBE_CORAL_FAN.createBlockData());
|
||||
|
||||
for (BlockData data : palette) {
|
||||
if (data instanceof Waterlogged) {
|
||||
((Waterlogged) data).setWaterlogged(false);
|
||||
}
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createGrassPalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.COARSE_DIRT, 4);
|
||||
addRepeated(palette, Material.ROOTED_DIRT, 4);
|
||||
addRepeated(palette, Material.TUFF, 2);
|
||||
palette.add(Material.DEAD_HORN_CORAL_BLOCK.createBlockData());
|
||||
palette.add(Material.DEAD_FIRE_CORAL_BLOCK.createBlockData());
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createStonePalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.TUFF, 3);
|
||||
palette.add(Material.ANDESITE.createBlockData());
|
||||
palette.add(Material.DEAD_HORN_CORAL_BLOCK.createBlockData());
|
||||
palette.add(Material.DEEPSLATE.createBlockData());
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createLeavesPalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
palette.add(Material.MANGROVE_ROOTS.createBlockData());
|
||||
addRepeated(palette, Material.AIR, 5);
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createStoneBrickSlabPalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.STONE_BRICK_SLAB, 15);
|
||||
palette.add(Material.ANDESITE_SLAB.createBlockData());
|
||||
palette.add(Material.COBBLESTONE_SLAB.createBlockData());
|
||||
palette.add(Material.TUFF_SLAB.createBlockData());
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createStoneBrickStairsPalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.STONE_BRICK_STAIRS, 15);
|
||||
palette.add(Material.ANDESITE_STAIRS.createBlockData());
|
||||
palette.add(Material.COBBLESTONE_STAIRS.createBlockData());
|
||||
palette.add(Material.TUFF_STAIRS.createBlockData());
|
||||
return palette;
|
||||
}
|
||||
|
||||
private List<BlockData> createDarkPrismarinePalette() {
|
||||
List<BlockData> palette = new ArrayList<>();
|
||||
addRepeated(palette, Material.DARK_PRISMARINE, 15);
|
||||
palette.add(Material.DEEPSLATE.createBlockData());
|
||||
palette.add(Material.COBBLED_DEEPSLATE.createBlockData());
|
||||
return palette;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.trouper.alias.server.systems.burning;
|
||||
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class BurnStage {
|
||||
private final long delay;
|
||||
private final BlockData blockData;
|
||||
|
||||
public BurnStage(long delay, BlockData blockData) {
|
||||
this.delay = delay;
|
||||
this.blockData = blockData;
|
||||
}
|
||||
|
||||
public long getDelay() { return delay; }
|
||||
public BlockData getBlockData() { return blockData; }
|
||||
}
|
||||
500
src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java
Normal file
500
src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java
Normal file
@@ -0,0 +1,500 @@
|
||||
package me.trouper.alias.server.systems.gui;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class QuickGui implements InventoryHolder, Main {
|
||||
|
||||
private static final Map<String, QuickGui> registry = new ConcurrentHashMap<>();
|
||||
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||
|
||||
private final Map<Integer, GuiAction> slotActions;
|
||||
private final Map<Integer, ItemStack> slotItems;
|
||||
private final Map<Integer, BukkitTask> animations;
|
||||
private final GuiAction globalAction;
|
||||
private final GuiCreateAction createAction;
|
||||
private final GuiCloseAction closeAction;
|
||||
private final GuiDragAction dragAction;
|
||||
private final Component title;
|
||||
private final int size;
|
||||
private final boolean preventDrag;
|
||||
private final Sound clickSound;
|
||||
private final float soundVolume;
|
||||
private final float soundPitch;
|
||||
|
||||
private Inventory inventory;
|
||||
private final Set<Player> viewers;
|
||||
|
||||
private QuickGui(Component title, int size, GuiAction globalAction,
|
||||
Map<Integer, GuiAction> slotActions, Map<Integer, ItemStack> slotItems,
|
||||
GuiCreateAction createAction, GuiCloseAction closeAction, GuiDragAction dragAction,
|
||||
boolean preventDrag, Sound clickSound, float soundVolume, float soundPitch) {
|
||||
this.title = title;
|
||||
this.size = size;
|
||||
this.globalAction = globalAction;
|
||||
this.slotActions = new HashMap<>(slotActions);
|
||||
this.slotItems = new HashMap<>(slotItems);
|
||||
this.createAction = createAction;
|
||||
this.closeAction = closeAction;
|
||||
this.dragAction = dragAction;
|
||||
this.preventDrag = preventDrag;
|
||||
this.clickSound = clickSound;
|
||||
this.soundVolume = soundVolume;
|
||||
this.soundPitch = soundPitch;
|
||||
this.animations = new HashMap<>();
|
||||
this.viewers = ConcurrentHashMap.newKeySet();
|
||||
}
|
||||
|
||||
public static QuickGui register(String id, QuickGui gui) {
|
||||
if (gui != null && id != null && !id.isEmpty()) {
|
||||
registry.put(id, gui);
|
||||
}
|
||||
return gui;
|
||||
}
|
||||
|
||||
public static Optional<QuickGui> getRegistered(String id) {
|
||||
return Optional.ofNullable(registry.get(id));
|
||||
}
|
||||
|
||||
public static Map<String, QuickGui> getRegistries() {
|
||||
return new HashMap<>(registry);
|
||||
}
|
||||
|
||||
public static void handleClick(InventoryClickEvent event) {
|
||||
if (event.getInventory().getHolder() instanceof QuickGui gui) {
|
||||
gui.onInventoryClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleClose(InventoryCloseEvent event) {
|
||||
if (event.getInventory().getHolder() instanceof QuickGui gui) {
|
||||
gui.onInventoryClose(event);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleDrag(InventoryDragEvent event) {
|
||||
if (event.getInventory().getHolder() instanceof QuickGui gui) {
|
||||
gui.onInventoryDrag(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
if (inventory == null) {
|
||||
int actualSize = calculateSize();
|
||||
inventory = Bukkit.createInventory(this, actualSize, title);
|
||||
createAction.onCreate(this, inventory);
|
||||
populateInventory();
|
||||
}
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public void open(Player player) {
|
||||
if (player != null && player.isOnline()) {
|
||||
player.openInventory(getInventory());
|
||||
viewers.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void closeAll() {
|
||||
new ArrayList<>(viewers).forEach(Player::closeInventory);
|
||||
}
|
||||
|
||||
public void updateItem(int slot, ItemStack item) {
|
||||
if (slot >= 0 && slot < getInventory().getSize()) {
|
||||
slotItems.put(slot, item);
|
||||
getInventory().setItem(slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateItem(int slot, ItemStack item, GuiAction action) {
|
||||
updateItem(slot, item);
|
||||
if (action != null) {
|
||||
slotActions.put(slot, action);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(int slot) {
|
||||
if (slot >= 0 && slot < getInventory().getSize()) {
|
||||
slotItems.remove(slot);
|
||||
slotActions.remove(slot);
|
||||
getInventory().setItem(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAnimation(int slot, List<ItemStack> frames, long interval) {
|
||||
stopAnimation(slot);
|
||||
|
||||
if (frames.isEmpty()) return;
|
||||
|
||||
BukkitTask task = new BukkitRunnable() {
|
||||
private int frameIndex = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (getInventory().getViewers().isEmpty()) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack frame = frames.get(frameIndex);
|
||||
getInventory().setItem(slot, frame);
|
||||
frameIndex = (frameIndex + 1) % frames.size();
|
||||
}
|
||||
}.runTaskTimer(main.getPlugin(), 0L, interval);
|
||||
|
||||
animations.put(slot, task);
|
||||
}
|
||||
|
||||
public void stopAnimation(int slot) {
|
||||
BukkitTask task = animations.remove(slot);
|
||||
if (task != null && !task.isCancelled()) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopAllAnimations() {
|
||||
animations.values().forEach(task -> {
|
||||
if (!task.isCancelled()) {
|
||||
task.cancel();
|
||||
}
|
||||
});
|
||||
animations.clear();
|
||||
}
|
||||
|
||||
private int calculateSize() {
|
||||
if (size > 0 && size % 9 == 0) {
|
||||
return Math.min(size, 54);
|
||||
}
|
||||
|
||||
int maxSlot = slotItems.keySet().stream()
|
||||
.mapToInt(Integer::intValue)
|
||||
.max()
|
||||
.orElse(8);
|
||||
|
||||
int rows = (maxSlot / 9) + 1;
|
||||
return Math.min(rows * 9, 54);
|
||||
}
|
||||
|
||||
private void populateInventory() {
|
||||
slotItems.forEach((slot, item) -> {
|
||||
if (slot < inventory.getSize()) {
|
||||
inventory.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onInventoryClick(InventoryClickEvent event) {
|
||||
if (event.getClickedInventory() == null ||
|
||||
event.getClickedInventory().getType() == InventoryType.PLAYER) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
if (!(event.getWhoClicked() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickSound != null) {
|
||||
player.playSound(player.getLocation(), clickSound, soundVolume, soundPitch);
|
||||
}
|
||||
|
||||
globalAction.onClick(this, event);
|
||||
|
||||
int slot = event.getSlot();
|
||||
GuiAction action = slotActions.get(slot);
|
||||
if (action != null) {
|
||||
action.onClick(this, event);
|
||||
}
|
||||
}
|
||||
|
||||
private void onInventoryClose(InventoryCloseEvent event) {
|
||||
if (event.getPlayer() instanceof Player player) {
|
||||
viewers.remove(player);
|
||||
}
|
||||
|
||||
if (viewers.isEmpty()) {
|
||||
stopAllAnimations();
|
||||
}
|
||||
|
||||
closeAction.onClose(this, event);
|
||||
}
|
||||
|
||||
private void onInventoryDrag(InventoryDragEvent event) {
|
||||
if (preventDrag) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
dragAction.onDrag(this, event);
|
||||
}
|
||||
|
||||
public Component getTitle() { return title; }
|
||||
public int getSize() { return size; }
|
||||
public Set<Player> getViewers() { return new HashSet<>(viewers); }
|
||||
public Map<Integer, ItemStack> getSlotItems() { return new HashMap<>(slotItems); }
|
||||
|
||||
public static GuiBuilder create() {
|
||||
return new GuiBuilder();
|
||||
}
|
||||
|
||||
public static class GuiBuilder {
|
||||
private Component title;
|
||||
private int size = -1;
|
||||
private GuiAction globalAction = (gui, event) -> {};
|
||||
private GuiCreateAction createAction = (gui, inv) -> {};
|
||||
private GuiCloseAction closeAction = (gui, event) -> {};
|
||||
private GuiDragAction dragAction = (gui, event) -> {};
|
||||
private final Map<Integer, GuiAction> slotActions = new HashMap<>();
|
||||
private final Map<Integer, ItemStack> slotItems = new HashMap<>();
|
||||
private boolean preventDrag = true;
|
||||
private Sound clickSound = Sound.UI_BUTTON_CLICK;
|
||||
private float soundVolume = 0.5f;
|
||||
private float soundPitch = 1.0f;
|
||||
|
||||
public GuiBuilder title(String title) {
|
||||
this.title = Component.text(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder title(Component title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder titleMini(String miniMessageTitle) {
|
||||
this.title = miniMessage.deserialize(miniMessageTitle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder size(int size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder rows(int rows) {
|
||||
this.size = Math.max(1, Math.min(6, rows)) * 9;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder onGlobalClick(GuiAction action) {
|
||||
this.globalAction = action != null ? action : (gui, event) -> {};
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder onCreate(GuiCreateAction action) {
|
||||
this.createAction = action != null ? action : (gui, inv) -> {};
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder onClose(GuiCloseAction action) {
|
||||
this.closeAction = action != null ? action : (gui, event) -> {};
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder onDrag(GuiDragAction action) {
|
||||
this.dragAction = action != null ? action : (gui, event) -> {};
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder allowDrag() {
|
||||
this.preventDrag = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder preventDrag() {
|
||||
this.preventDrag = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder clickSound(Sound sound, float volume, float pitch) {
|
||||
this.clickSound = sound;
|
||||
this.soundVolume = volume;
|
||||
this.soundPitch = pitch;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder noClickSound() {
|
||||
this.clickSound = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder item(int slot, ItemStack item) {
|
||||
return item(slot, item, null);
|
||||
}
|
||||
|
||||
public GuiBuilder item(int slot, ItemStack item, GuiAction action) {
|
||||
if (slot >= 0 && slot < 54 && item != null) {
|
||||
slotItems.put(slot, item);
|
||||
if (action != null) {
|
||||
slotActions.put(slot, action);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder item(int slot, Material material, String name) {
|
||||
return item(slot, material, name, null);
|
||||
}
|
||||
|
||||
public GuiBuilder item(int slot, Material material, String name, GuiAction action) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.displayName(Component.text(name).decoration(TextDecoration.ITALIC, false));
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item(slot, item, action);
|
||||
}
|
||||
|
||||
public GuiBuilder itemMini(int slot, Material material, String miniMessageName) {
|
||||
return itemMini(slot, material, miniMessageName, null);
|
||||
}
|
||||
|
||||
public GuiBuilder itemMini(int slot, Material material, String miniMessageName, GuiAction action) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.displayName(miniMessage.deserialize(miniMessageName).decoration(TextDecoration.ITALIC, false));
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item(slot, item, action);
|
||||
}
|
||||
|
||||
public GuiBuilder fillBorder(Material material) {
|
||||
return fillBorder(new ItemStack(material));
|
||||
}
|
||||
|
||||
public GuiBuilder fillBorder(ItemStack item) {
|
||||
int actualSize = size > 0 ? size : 54;
|
||||
int rows = actualSize / 9;
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
slotItems.put(i, item);
|
||||
if (rows > 1) {
|
||||
slotItems.put((rows - 1) * 9 + i, item);
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 1; row < rows - 1; row++) {
|
||||
slotItems.put(row * 9, item);
|
||||
slotItems.put(row * 9 + 8, item);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder fillEmpty(Material material) {
|
||||
return fillEmpty(new ItemStack(material));
|
||||
}
|
||||
|
||||
public GuiBuilder fillEmpty(ItemStack item) {
|
||||
int actualSize = size > 0 ? size : 54;
|
||||
for (int i = 0; i < actualSize; i++) {
|
||||
if (!slotItems.containsKey(i)) {
|
||||
slotItems.put(i, item);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QuickGui build() {
|
||||
Component finalTitle = title != null ? title : Component.text("Untitled GUI");
|
||||
return new QuickGui(finalTitle, size, globalAction, slotActions, slotItems,
|
||||
createAction, closeAction, dragAction, preventDrag,
|
||||
clickSound, soundVolume, soundPitch);
|
||||
}
|
||||
|
||||
public QuickGui buildAndRegister(String id) {
|
||||
QuickGui gui = build();
|
||||
return register(id, gui);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GuiAction {
|
||||
void onClick(QuickGui gui, InventoryClickEvent event);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GuiCreateAction {
|
||||
void onCreate(QuickGui gui, Inventory inventory);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GuiCloseAction {
|
||||
void onClose(QuickGui gui, InventoryCloseEvent event);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface GuiDragAction {
|
||||
void onDrag(QuickGui gui, InventoryDragEvent event);
|
||||
}
|
||||
|
||||
public static class GuiUtils {
|
||||
|
||||
public static QuickGui createConfirmDialog(String title, Consumer<Boolean> callback) {
|
||||
return create()
|
||||
.titleMini("<green>" + title)
|
||||
.rows(3)
|
||||
.fillBorder(Material.GRAY_STAINED_GLASS_PANE)
|
||||
.itemMini(11, Material.GREEN_CONCRETE, "<green><bold>CONFIRM",
|
||||
(gui, event) -> {
|
||||
callback.accept(true);
|
||||
event.getWhoClicked().closeInventory();
|
||||
})
|
||||
.itemMini(15, Material.RED_CONCRETE, "<red><bold>CANCEL",
|
||||
(gui, event) -> {
|
||||
callback.accept(false);
|
||||
event.getWhoClicked().closeInventory();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static GuiBuilder createPaginated(String title, List<ItemStack> items, int itemsPerPage) {
|
||||
GuiBuilder builder = create()
|
||||
.titleMini(title)
|
||||
.rows(6)
|
||||
.fillBorder(Material.GRAY_STAINED_GLASS_PANE);
|
||||
|
||||
int totalPages = (int) Math.ceil((double) items.size() / itemsPerPage);
|
||||
|
||||
if (totalPages > 1) {
|
||||
builder.itemMini(45, Material.ARROW, "<yellow>Previous Page")
|
||||
.itemMini(53, Material.ARROW, "<yellow>Next Page");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static ItemStack createSeparator(NamedTextColor color) {
|
||||
ItemStack pane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
||||
ItemMeta meta = pane.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.displayName(Component.text(" ").color(color));
|
||||
pane.setItemMeta(meta);
|
||||
}
|
||||
return pane;
|
||||
}
|
||||
}
|
||||
}
|
||||
351
src/main/java/me/trouper/alias/server/systems/tracing/BlockDisplayRaytracer.java
Executable file
351
src/main/java/me/trouper/alias/server/systems/tracing/BlockDisplayRaytracer.java
Executable file
@@ -0,0 +1,351 @@
|
||||
package me.trouper.alias.server.systems.tracing;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.BlockDisplay;
|
||||
import org.bukkit.entity.Display;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.bukkit.util.VoxelShape;
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BlockDisplayRaytracer implements Main {
|
||||
|
||||
public static void cleanup() {
|
||||
JavaPlugin plugin = main.getPlugin();
|
||||
List<World> worlds = plugin.getServer().getWorlds();
|
||||
List<Entity> entities = new ArrayList<>();
|
||||
for (World world : worlds) {
|
||||
entities.addAll(world.getEntities().stream().filter(entity -> entity.getScoreboardTags().contains(main.getCommon().getTempTag())).toList());
|
||||
entities.forEach(entity -> {
|
||||
if (entity != null) entity.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void outline(Material display, Location location, long stayTime, List<Player> viewers) {
|
||||
outline(display, location, 0.05, stayTime, viewers);
|
||||
}
|
||||
|
||||
public static void outline(Material display, Location corner1, Location corner2, double thickness, long stayTime, List<Player> viewers) {
|
||||
World world = corner1.getWorld();
|
||||
|
||||
int minX = Math.min(corner1.getBlockX(), corner2.getBlockX());
|
||||
int minY = Math.min(corner1.getBlockY(), corner2.getBlockY());
|
||||
int minZ = Math.min(corner1.getBlockZ(), corner2.getBlockZ());
|
||||
int maxX = Math.max(corner1.getBlockX(), corner2.getBlockX());
|
||||
int maxY = Math.max(corner1.getBlockY(), corner2.getBlockY());
|
||||
int maxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ());
|
||||
|
||||
Location a1 = new Location(world, minX, minY, minZ);
|
||||
Location a2 = new Location(world, maxX + 1, minY, minZ);
|
||||
Location a3 = new Location(world, maxX + 1, minY, maxZ + 1);
|
||||
Location a4 = new Location(world, minX, minY, maxZ + 1);
|
||||
|
||||
Location b1 = new Location(world, minX, maxY + 1, minZ);
|
||||
Location b2 = new Location(world, maxX + 1, maxY + 1, minZ);
|
||||
Location b3 = new Location(world, maxX + 1, maxY + 1, maxZ + 1);
|
||||
Location b4 = new Location(world, minX, maxY + 1, maxZ + 1);
|
||||
|
||||
trace(display, a1, a2, thickness, stayTime, viewers);
|
||||
trace(display, a2, a3, thickness, stayTime, viewers);
|
||||
trace(display, a3, a4, thickness, stayTime, viewers);
|
||||
trace(display, a4, a1, thickness, stayTime, viewers);
|
||||
|
||||
trace(display, b1, b2, thickness, stayTime, viewers);
|
||||
trace(display, b2, b3, thickness, stayTime, viewers);
|
||||
trace(display, b3, b4, thickness, stayTime, viewers);
|
||||
trace(display, b4, b1, thickness, stayTime, viewers);
|
||||
|
||||
trace(display, a1, b1, thickness, stayTime, viewers);
|
||||
trace(display, a2, b2, thickness, stayTime, viewers);
|
||||
trace(display, a3, b3, thickness, stayTime, viewers);
|
||||
trace(display, a4, b4, thickness, stayTime, viewers);
|
||||
}
|
||||
|
||||
|
||||
public static void outline(Material display, Location location, double thickness, long stayTime, List<Player> viewers) {
|
||||
Location og = location.getBlock().getLocation();
|
||||
|
||||
Location a1 = og.clone().add(0, 0, 0);
|
||||
Location a2 = og.clone().add(1, 0, 0);
|
||||
Location a3 = og.clone().add(1, 0, 1);
|
||||
Location a4 = og.clone().add(0, 0, 1);
|
||||
|
||||
Location b1 = og.clone().add(0, 1, 0);
|
||||
Location b2 = og.clone().add(1, 1, 0);
|
||||
Location b3 = og.clone().add(1, 1, 1);
|
||||
Location b4 = og.clone().add(0, 1, 1);
|
||||
|
||||
trace(display, a1, a2, thickness, stayTime, viewers);
|
||||
trace(display, a2, a3, thickness, stayTime, viewers);
|
||||
trace(display, a3, a4, thickness, stayTime, viewers);
|
||||
trace(display, a4, a1, thickness, stayTime, viewers);
|
||||
|
||||
trace(display, b1, b2, thickness, stayTime, viewers);
|
||||
trace(display, b2, b3, thickness, stayTime, viewers);
|
||||
trace(display, b3, b4, thickness, stayTime, viewers);
|
||||
trace(display, b4, b1, thickness, stayTime, viewers);
|
||||
|
||||
trace(display, a1, b1, thickness, stayTime, viewers);
|
||||
trace(display, a2, b2, thickness, stayTime, viewers);
|
||||
trace(display, a3, b3, thickness, stayTime, viewers);
|
||||
trace(display, a4, b4, thickness, stayTime, viewers);
|
||||
}
|
||||
|
||||
public static void trace(Material display, Location start, Location end, long stayTime, List<Player> viewers) {
|
||||
trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime, viewers);
|
||||
}
|
||||
|
||||
public static void trace(Material display, Location start, Location end, double thickness, long stayTime, List<Player> viewers) {
|
||||
trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime, viewers);
|
||||
}
|
||||
|
||||
public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, List<Player> viewers) {
|
||||
World world = start.getWorld();
|
||||
|
||||
BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> {
|
||||
AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1);
|
||||
Vector3f transition = new Vector3f(-(float)(thickness / 2F));
|
||||
Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance);
|
||||
Transformation trans = new Transformation(transition, angle, scale, angle);
|
||||
Location vector = entity.getLocation();
|
||||
|
||||
vector.setDirection(direction);
|
||||
entity.teleport(vector);
|
||||
entity.setBlock(display.createBlockData());
|
||||
entity.setBrightness(new Display.Brightness(15, 15));
|
||||
entity.setInterpolationDelay(0);
|
||||
entity.setTransformation(trans);
|
||||
entity.addScoreboardTag(main.getCommon().getTempTag());
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!viewers.contains(player)) {
|
||||
player.hideEntity(main.getPlugin(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime);
|
||||
});
|
||||
}
|
||||
|
||||
public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, Consumer<BlockDisplay> onEntitySpawn, List<Player> viewers) {
|
||||
World world = start.getWorld();
|
||||
|
||||
BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> {
|
||||
AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1);
|
||||
Vector3f transition = new Vector3f(-(float)(thickness / 2F));
|
||||
Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance);
|
||||
Transformation trans = new Transformation(transition, angle, scale, angle);
|
||||
Location vector = entity.getLocation();
|
||||
|
||||
vector.setDirection(direction);
|
||||
entity.teleport(vector);
|
||||
entity.setBlock(display.createBlockData());
|
||||
entity.setBrightness(new Display.Brightness(15, 15));
|
||||
entity.setInterpolationDelay(0);
|
||||
entity.setTransformation(trans);
|
||||
entity.addScoreboardTag(main.getCommon().getTempTag());
|
||||
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!viewers.contains(player)) {
|
||||
player.hideEntity(main.getPlugin(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime);
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> onEntitySpawn.accept(entity), 5);
|
||||
});
|
||||
}
|
||||
|
||||
public static List<BlockDisplay> outline(Material display, Location location, long stayTime) {
|
||||
return outline(display, location, 0.05, stayTime);
|
||||
}
|
||||
|
||||
public static List<BlockDisplay> outline(Material display, Location location, double thickness, long stayTime) {
|
||||
Location og = location.getBlock().getLocation();
|
||||
|
||||
Location a1 = og.clone().add(0, 0, 0);
|
||||
Location a2 = og.clone().add(1, 0, 0);
|
||||
Location a3 = og.clone().add(1, 0, 1);
|
||||
Location a4 = og.clone().add(0, 0, 1);
|
||||
|
||||
Location b1 = og.clone().add(0, 1, 0);
|
||||
Location b2 = og.clone().add(1, 1, 0);
|
||||
Location b3 = og.clone().add(1, 1, 1);
|
||||
Location b4 = og.clone().add(0, 1, 1);
|
||||
|
||||
List<BlockDisplay> a = new ArrayList<>();
|
||||
|
||||
a.add(trace(display, a1, a2, thickness, stayTime));
|
||||
a.add(trace(display, a2, a3, thickness, stayTime));
|
||||
a.add(trace(display, a3, a4, thickness, stayTime));
|
||||
a.add(trace(display, a4, a1, thickness, stayTime));
|
||||
|
||||
a.add(trace(display, b1, b2, thickness, stayTime));
|
||||
a.add(trace(display, b2, b3, thickness, stayTime));
|
||||
a.add(trace(display, b3, b4, thickness, stayTime));
|
||||
a.add(trace(display, b4, b1, thickness, stayTime));
|
||||
|
||||
a.add(trace(display, a1, b1, thickness, stayTime));
|
||||
a.add(trace(display, a2, b2, thickness, stayTime));
|
||||
a.add(trace(display, a3, b3, thickness, stayTime));
|
||||
a.add(trace(display, a4, b4, thickness, stayTime));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void highlightCollisions(Block block, Color color, long stayTime) {
|
||||
if (block == null || block.isEmpty() || !block.isCollidable())
|
||||
return;
|
||||
|
||||
VoxelShape shape = block.getCollisionShape();
|
||||
World world = block.getWorld();
|
||||
Vector offset = block.getLocation().toVector();
|
||||
|
||||
for (BoundingBox box : shape.getBoundingBoxes()) {
|
||||
highlight(box, offset, world, color, stayTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static void highlight(BoundingBox box, Vector offset, World world, Color color, long stayTime) {
|
||||
double x1 = box.getMinX() + offset.getX();
|
||||
double y1 = box.getMinY() + offset.getY();
|
||||
double z1 = box.getMinZ() + offset.getZ();
|
||||
double x2 = box.getMaxX() + offset.getX();
|
||||
double y2 = box.getMaxY() + offset.getY();
|
||||
double z2 = box.getMaxZ() + offset.getZ();
|
||||
|
||||
traceGlowing(world, x1, y1, z1, x2, y1, z1, color, stayTime);
|
||||
traceGlowing(world, x2, y1, z1, x2, y1, z2, color, stayTime);
|
||||
traceGlowing(world, x2, y1, z2, x1, y1, z2, color, stayTime);
|
||||
traceGlowing(world, x1, y1, z2, x1, y1, z1, color, stayTime);
|
||||
|
||||
traceGlowing(world, x1, y2, z1, x2, y2, z1, color, stayTime);
|
||||
traceGlowing(world, x2, y2, z1, x2, y2, z2, color, stayTime);
|
||||
traceGlowing(world, x2, y2, z2, x1, y2, z2, color, stayTime);
|
||||
traceGlowing(world, x1, y2, z2, x1, y2, z1, color, stayTime);
|
||||
|
||||
traceGlowing(world, x1, y1, z1, x1, y2, z1, color, stayTime);
|
||||
traceGlowing(world, x2, y1, z1, x2, y2, z1, color, stayTime);
|
||||
traceGlowing(world, x2, y1, z2, x2, y2, z2, color, stayTime);
|
||||
traceGlowing(world, x1, y1, z2, x1, y2, z2, color, stayTime);
|
||||
}
|
||||
|
||||
public static void traceGlowing(World world, double x1, double y1, double z1, double x2, double y2, double z2, Color color, long stayTime) {
|
||||
Location loc1 = new Location(world, x1, y1, z1);
|
||||
Location loc2 = new Location(world, x2, y2, z2);
|
||||
BlockDisplay ent = trace(Material.WHITE_CONCRETE, loc1, loc2, 0.01, stayTime);
|
||||
ent.setGlowColorOverride(color);
|
||||
ent.setGlowing(true);
|
||||
}
|
||||
|
||||
public static BlockDisplay trace(Material display, Location start, Location end, long stayTime) {
|
||||
return trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime);
|
||||
}
|
||||
|
||||
public static BlockDisplay trace(Material display, Location start, Location end, double thickness, long stayTime) {
|
||||
return trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime);
|
||||
}
|
||||
|
||||
public static BlockDisplay trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime) {
|
||||
World world = start.getWorld();
|
||||
|
||||
BlockDisplay entity = world.spawn(start, BlockDisplay.class);
|
||||
AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1);
|
||||
Vector3f transition = new Vector3f(-(float)(thickness / 2F));
|
||||
Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance);
|
||||
Transformation trans = new Transformation(transition, angle, scale, angle);
|
||||
Location vector = entity.getLocation();
|
||||
|
||||
vector.setDirection(direction);
|
||||
entity.teleport(vector);
|
||||
entity.setBlock(display.createBlockData());
|
||||
entity.setBrightness(new Display.Brightness(15, 15));
|
||||
entity.setInterpolationDelay(0);
|
||||
entity.setTransformation(trans);
|
||||
entity.addScoreboardTag(main.getCommon().getTempTag());
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static void transform(BlockDisplay display, Location start, Location end, double thickness) {
|
||||
Vector direction = end.toVector().subtract(start.toVector());
|
||||
double distance = direction.length();
|
||||
|
||||
Location loc = start.clone();
|
||||
loc.setDirection(direction);
|
||||
display.teleport(loc);
|
||||
|
||||
Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0); // Centered
|
||||
Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance);
|
||||
AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1);
|
||||
Transformation transformation = new Transformation(translation, rotation, scale, rotation);
|
||||
|
||||
display.setTransformation(transformation);
|
||||
}
|
||||
|
||||
public static void transform(BlockDisplay display, Location start, Vector direction, double distance, double thickness) {
|
||||
Location loc = start.clone();
|
||||
loc.setDirection(direction);
|
||||
display.teleport(loc);
|
||||
|
||||
Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0);
|
||||
Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance);
|
||||
AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1);
|
||||
Transformation transformation = new Transformation(translation, rotation, scale, rotation);
|
||||
|
||||
display.setTransformation(transformation);
|
||||
}
|
||||
|
||||
|
||||
public static void translate(BlockDisplay display, Vector3f offset) {
|
||||
Transformation current = display.getTransformation();
|
||||
Vector3f translation = new Vector3f(current.getTranslation()).add(offset);
|
||||
display.setTransformation(new Transformation(
|
||||
translation,
|
||||
current.getLeftRotation(),
|
||||
current.getScale(),
|
||||
current.getRightRotation()
|
||||
));
|
||||
}
|
||||
|
||||
public static void scale(BlockDisplay display, Vector3f scale) {
|
||||
Transformation current = display.getTransformation();
|
||||
display.setTransformation(new Transformation(
|
||||
current.getTranslation(),
|
||||
current.getLeftRotation(),
|
||||
scale,
|
||||
current.getRightRotation()
|
||||
));
|
||||
}
|
||||
|
||||
public static void rotate(BlockDisplay display, AxisAngle4f rotation) {
|
||||
Transformation current = display.getTransformation();
|
||||
display.setTransformation(new Transformation(
|
||||
current.getTranslation(),
|
||||
rotation,
|
||||
current.getScale(),
|
||||
rotation
|
||||
));
|
||||
}
|
||||
|
||||
public static void alignToDirection(BlockDisplay display, Vector direction) {
|
||||
Location loc = display.getLocation().clone();
|
||||
loc.setDirection(direction);
|
||||
display.teleport(loc);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package me.trouper.alias.server.systems.tracing;
|
||||
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.bukkit.util.VoxelShape;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CustomDisplayRaytracer {
|
||||
|
||||
public static final Predicate<Point> HIT_BLOCK = point -> {
|
||||
Block b = point.getBlock();
|
||||
Location l = point.getLoc();
|
||||
|
||||
if (b == null || b.isEmpty() || !b.isCollidable())
|
||||
return false;
|
||||
|
||||
Vector vec = l.toVector().subtract(b.getLocation().toVector());
|
||||
VoxelShape shape = b.getCollisionShape();
|
||||
|
||||
for (BoundingBox box : shape.getBoundingBoxes())
|
||||
if (box.contains(vec))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
public static final Predicate<Point> HIT_ENTITY = point -> {
|
||||
return !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty();
|
||||
};
|
||||
|
||||
public static final Predicate<Point> HIT_BLOCK_OR_ENTITY = point -> {
|
||||
return HIT_BLOCK.test(point) || HIT_ENTITY.test(point);
|
||||
};
|
||||
|
||||
public static final Predicate<Point> HIT_BLOCK_AND_ENTITY = point -> {
|
||||
return HIT_BLOCK.test(point) && HIT_ENTITY.test(point);
|
||||
};
|
||||
|
||||
public static Predicate<Point> hitEntityExclude(Entity exclude) {
|
||||
return point -> !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty();
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitAnythingExclude(Entity exclude) {
|
||||
return point -> HIT_BLOCK.test(point) || !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty();
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitEverythingExclude(Entity exclude) {
|
||||
return point -> HIT_BLOCK.test(point) && !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty();
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitEntityIf(Predicate<Entity> condition) {
|
||||
return point -> !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead() && condition.test(e)).isEmpty();
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitBlockIf(Predicate<Block> condition) {
|
||||
return point -> HIT_BLOCK.test(point) && condition.test(point.getBlock());
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitAnythingIf(Predicate<Entity> condition) {
|
||||
return point -> HIT_BLOCK.test(point) || !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead() && condition.test(e)).isEmpty();
|
||||
}
|
||||
|
||||
public static Predicate<Point> hitEverythingIf(Predicate<Entity> condition) {
|
||||
return point -> HIT_BLOCK.test(point) && !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead() && condition.test(e)).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
public static Point trace(Location start, Location end, Predicate<Point> hitCondition) {
|
||||
return trace(start, end, 0.5, hitCondition);
|
||||
}
|
||||
|
||||
public static Point trace(Location start, Location end, double interval, Predicate<Point> hitCondition) {
|
||||
return trace(start, end.toVector().subtract(start.toVector()), end.distance(start), interval, hitCondition);
|
||||
}
|
||||
|
||||
public static Point trace(Location start, Vector direction, double distance, Predicate<Point> hitCondition) {
|
||||
return trace(start, direction, distance, 0.5, hitCondition);
|
||||
}
|
||||
|
||||
public static Point trace(Location start, Vector direction, double distance, double interval, Predicate<Point> hitCondition) {
|
||||
if (interval < 0) throw new IllegalArgumentException("interval cannot be zero!");
|
||||
if (distance < 0) throw new IllegalArgumentException("distance cannot be zero!");
|
||||
|
||||
for (double i = 0.0; i < distance; i += interval) {
|
||||
Point point = blocksInFrontOf(start, direction, i, false);
|
||||
if (hitCondition.test(point)) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
return blocksInFrontOf(start, direction, distance, true);
|
||||
}
|
||||
|
||||
|
||||
public static BukkitTask traceDelayed(Plugin plugin, Location start, Vector direction, double distance, double interval, long tickDelay, int pointsPerTick, Predicate<Point> hitCondition) {
|
||||
|
||||
if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!");
|
||||
if (distance <= 0) throw new IllegalArgumentException("distance cannot be zero or negative!");
|
||||
if (tickDelay < 0) throw new IllegalArgumentException("tickDelay cannot be negative!");
|
||||
|
||||
Vector normalizedDir = direction.clone().normalize();
|
||||
|
||||
|
||||
return new BukkitRunnable() {
|
||||
private double currentDistance = 0.0;
|
||||
private boolean hit = false;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (hit || currentDistance > distance) {
|
||||
if (!hit) {
|
||||
Point finalPoint = blocksInFrontOf(start, normalizedDir, distance, true);
|
||||
hitCondition.test(finalPoint);
|
||||
}
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pointsPerTick && currentDistance <= distance; i++) {
|
||||
Point point = blocksInFrontOf(start, normalizedDir, currentDistance, false);
|
||||
if (hitCondition.test(point)) {
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
currentDistance += interval;
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 0, tickDelay);
|
||||
}
|
||||
|
||||
public static BukkitTask traceDelayed(Plugin plugin, Location start, Location end, double interval, long tickDelay, int pointsPerTick, Predicate<Point> hitCondition) {
|
||||
Vector direction = end.toVector().subtract(start.toVector()).normalize();
|
||||
double distance = start.distance(end);
|
||||
return traceDelayed(plugin, start, direction, distance, interval, tickDelay,pointsPerTick, hitCondition);
|
||||
}
|
||||
|
||||
public static BukkitTask traceDelayed(Plugin plugin,
|
||||
Location start,
|
||||
Location end,
|
||||
long tickDelay,
|
||||
Predicate<Point> hitCondition) {
|
||||
return traceDelayed(plugin, start, end,0.5, tickDelay, 1, hitCondition);
|
||||
}
|
||||
|
||||
public static BukkitTask traceDelayed(Plugin plugin,
|
||||
Location start,
|
||||
Vector direction,
|
||||
double distance,
|
||||
long tickDelay,
|
||||
Predicate<Point> hitCondition) {
|
||||
return traceDelayed(plugin, start, direction, distance, 0.5, tickDelay,1, hitCondition);
|
||||
}
|
||||
|
||||
public static Point traceWithReflection(Location start, Vector direction, double distance, double interval,
|
||||
int maxReflections, Predicate<Point> hitCondition,
|
||||
BiPredicate<Point, Block> blockReflectCondition,
|
||||
BiPredicate<Point, Entity> entityReflectCondition) {
|
||||
|
||||
|
||||
if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!");
|
||||
if (distance <= 0) throw new IllegalArgumentException("distance cannot be zero or negative!");
|
||||
|
||||
Vector normalizedDir = direction.clone().normalize();
|
||||
Location currentLocation = start.clone();
|
||||
Vector currentDirection = normalizedDir.clone();
|
||||
double remainingDistance = distance;
|
||||
int reflections = 0;
|
||||
|
||||
while (remainingDistance > 0 && reflections <= maxReflections) {
|
||||
for (double i = 0.0; i < remainingDistance; i += interval) {
|
||||
Point point = blocksInFrontOf(currentLocation, currentDirection, i, false);
|
||||
|
||||
if (hitCondition.test(point)) {
|
||||
return point;
|
||||
}
|
||||
|
||||
boolean shouldReflect = false;
|
||||
Vector newDirection = null;
|
||||
|
||||
if (HIT_BLOCK.test(point) && point.getBlock() != null) {
|
||||
Block hitBlock = point.getBlock();
|
||||
if (blockReflectCondition.test(point, hitBlock)) {
|
||||
Point previousPoint = blocksInFrontOf(currentLocation, currentDirection, Math.max(0, i - interval), false);
|
||||
|
||||
BlockFace hitFace = traceBlockFace(previousPoint.getLoc(), currentDirection, interval * 2);
|
||||
|
||||
if (hitFace != null) {
|
||||
Vector faceNormal = getFaceNormal(hitFace);
|
||||
newDirection = calculateReflection(currentDirection, faceNormal);
|
||||
shouldReflect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Entity> nearbyEntities = point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead());
|
||||
if (!nearbyEntities.isEmpty()) {
|
||||
for (Entity entity : nearbyEntities) {
|
||||
if (entityReflectCondition.test(point, entity)) {
|
||||
Point previousPoint = blocksInFrontOf(currentLocation, currentDirection, Math.max(0, i - interval), false);
|
||||
|
||||
newDirection = glanceReflect(currentDirection);
|
||||
shouldReflect = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReflect) {
|
||||
double backStep = Math.max(0, i - interval);
|
||||
currentLocation = blocksInFrontOf(currentLocation, currentDirection, backStep, false).getLoc();
|
||||
|
||||
currentDirection = newDirection;
|
||||
|
||||
remainingDistance -= backStep;
|
||||
|
||||
reflections++;
|
||||
|
||||
currentLocation = currentLocation.add(currentDirection.clone().multiply(interval * 0.1));
|
||||
remainingDistance -= interval * 0.1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + interval >= remainingDistance) {
|
||||
Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true);
|
||||
return finalPoint;
|
||||
}
|
||||
}
|
||||
|
||||
if (reflections > maxReflections) {
|
||||
Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true);
|
||||
return finalPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return blocksInFrontOf(start, normalizedDir, distance, true);
|
||||
}
|
||||
|
||||
private static Vector glanceReflect(Vector incident) {
|
||||
return offsetVector(incident,4).multiply(-1);
|
||||
}
|
||||
|
||||
private static BlockFace traceBlockFace(Location startLocation, Vector direction, double maxDistance) {
|
||||
Predicate<Block> blockPredicate = block -> true;
|
||||
Predicate<Entity> entityPredicate = entity -> false;
|
||||
|
||||
RayTraceResult result = startLocation.getWorld().rayTrace(startLocation, direction, maxDistance, FluidCollisionMode.NEVER,true,0.1,entityPredicate, blockPredicate);
|
||||
|
||||
if (result != null && result.getHitBlock() != null && result.getHitBlockFace() != null) {
|
||||
return result.getHitBlockFace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Vector calculateReflection(Vector incident, Vector normal) {
|
||||
// r = i - 2(i dot n)n
|
||||
double dot = incident.dot(normal);
|
||||
Vector reflection = incident.clone().subtract(normal.clone().multiply(2 * dot));
|
||||
|
||||
return reflection.normalize();
|
||||
}
|
||||
|
||||
private static Vector getFaceNormal(BlockFace face) {
|
||||
return switch (face) {
|
||||
case DOWN -> new Vector(0, -1, 0);
|
||||
case NORTH -> new Vector(0, 0, -1);
|
||||
case SOUTH -> new Vector(0, 0, 1);
|
||||
case EAST -> new Vector(1, 0, 0);
|
||||
case WEST -> new Vector(-1, 0, 0);
|
||||
default -> new Vector(0, 1, 0);
|
||||
};
|
||||
}
|
||||
|
||||
public static Point blocksInFrontOf(Location loc, Vector dir, double blocks, boolean missed) {
|
||||
return new Point(loc.clone().add(dir.getX() * blocks, dir.getY() * blocks, dir.getZ() * blocks), blocks, missed);
|
||||
}
|
||||
|
||||
public static Vector offsetVector(Vector original, double angleDegrees) {
|
||||
Random random = new Random();
|
||||
original = original.clone().normalize();
|
||||
|
||||
double yaw = Math.toDegrees(Math.atan2(-original.getX(), original.getZ()));
|
||||
double pitch = Math.toDegrees(Math.asin(-original.getY()));
|
||||
|
||||
double yawOffset = (random.nextDouble() * 2 - 1) * angleDegrees;
|
||||
double pitchOffset = (random.nextDouble() * 2 - 1) * angleDegrees;
|
||||
|
||||
yaw += yawOffset;
|
||||
pitch += pitchOffset;
|
||||
|
||||
pitch = Math.max(-90, Math.min(90, pitch));
|
||||
|
||||
double pitchRad = Math.toRadians(pitch);
|
||||
double yawRad = Math.toRadians(yaw);
|
||||
|
||||
double x = -Math.sin(yawRad) * Math.cos(pitchRad);
|
||||
double y = -Math.sin(pitchRad);
|
||||
double z = Math.cos(yawRad) * Math.cos(pitchRad);
|
||||
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
}
|
||||
88
src/main/java/me/trouper/alias/server/systems/tracing/Point.java
Executable file
88
src/main/java/me/trouper/alias/server/systems/tracing/Point.java
Executable file
@@ -0,0 +1,88 @@
|
||||
package me.trouper.alias.server.systems.tracing;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Point {
|
||||
private final Location loc;
|
||||
private final World world;
|
||||
private final Block block;
|
||||
private final boolean missed;
|
||||
private final double traveledDist;
|
||||
private List<Point> rayPath;
|
||||
|
||||
public Point(Location loc, double traveledDist, boolean missed) {
|
||||
this.loc = loc;
|
||||
this.world = loc.getWorld();
|
||||
this.block = loc.getBlock();
|
||||
this.missed = missed;
|
||||
this.traveledDist = traveledDist;
|
||||
|
||||
if (world == null) {
|
||||
throw new IllegalArgumentException("point world cannot be null!");
|
||||
}
|
||||
}
|
||||
|
||||
public List<Entity> getNearbyEntities(Entity exclude, int range, boolean requireContact, double expansionX, double expansionY, double expansionZ, Predicate<Entity> filter) {
|
||||
return new ArrayList<>(world.getNearbyEntities(loc, range, range, range, e -> {
|
||||
if (requireContact && !e.getBoundingBox().expand(expansionX, expansionY, expansionZ).contains(loc.toVector())) {
|
||||
return false;
|
||||
}
|
||||
return filter.test(e) && e != exclude;
|
||||
}));
|
||||
}
|
||||
|
||||
public List<Entity> getNearbyEntities(Entity exclude, int range, boolean requireContact, double expansion, Predicate<Entity> filter) {
|
||||
return getNearbyEntities(exclude, range, requireContact, expansion, expansion, expansion, filter);
|
||||
}
|
||||
|
||||
public List<Entity> getNearbyEntities(Entity exclude, int range, boolean requireContact, Predicate<Entity> filter) {
|
||||
return getNearbyEntities(exclude, range, requireContact, 0, filter);
|
||||
}
|
||||
|
||||
public List<Entity> getNearbyEntities(Entity exclude, int range, Predicate<Entity> filter) {
|
||||
return getNearbyEntities(exclude, range, false, filter);
|
||||
}
|
||||
|
||||
public double getTraveledDist() {
|
||||
return traveledDist;
|
||||
}
|
||||
|
||||
public boolean wasMissed() {
|
||||
return missed;
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public Location getLoc() {
|
||||
return loc;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public double distance(Location other) {
|
||||
return other.distance(loc);
|
||||
}
|
||||
|
||||
public Point addRayPoint(Point point) {
|
||||
rayPath.add(point);
|
||||
return point;
|
||||
}
|
||||
|
||||
public List<Point> setRayPath(List<Point> points) {
|
||||
rayPath = points;
|
||||
return rayPath;
|
||||
}
|
||||
|
||||
public List<Point> getRayPath = new ArrayList<>();
|
||||
}
|
||||
27
src/main/java/me/trouper/alias/server/systems/tracing/ReflectionResult.java
Executable file
27
src/main/java/me/trouper/alias/server/systems/tracing/ReflectionResult.java
Executable file
@@ -0,0 +1,27 @@
|
||||
package me.trouper.alias.server.systems.tracing;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class ReflectionResult {
|
||||
private final Point hitPoint;
|
||||
private final boolean shouldReflect;
|
||||
private final Vector reflectedDirection;
|
||||
|
||||
public ReflectionResult(Point hitPoint, boolean shouldReflect, Vector reflectedDirection) {
|
||||
this.hitPoint = hitPoint;
|
||||
this.shouldReflect = shouldReflect;
|
||||
this.reflectedDirection = reflectedDirection;
|
||||
}
|
||||
|
||||
public Point getHitPoint() {
|
||||
return hitPoint;
|
||||
}
|
||||
|
||||
public boolean shouldReflect() {
|
||||
return shouldReflect;
|
||||
}
|
||||
|
||||
public Vector getReflectedDirection() {
|
||||
return reflectedDirection;
|
||||
}
|
||||
}
|
||||
256
src/main/java/me/trouper/alias/server/systems/visual/DisplayUtils.java
Executable file
256
src/main/java/me/trouper/alias/server/systems/visual/DisplayUtils.java
Executable file
@@ -0,0 +1,256 @@
|
||||
package me.trouper.alias.server.systems.visual;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import me.trouper.alias.utils.misc.Randomizer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DisplayUtils implements Main {
|
||||
|
||||
// This will 100% get Javadoc in the future when I try to use it. Everything in here is so convoluted.
|
||||
|
||||
public static final Function<Particle, Consumer<Location>> PARTICLE_FACTORY = particle -> l -> l.getWorld().spawnParticle(particle, l, 1, 0, 0, 0, 0);
|
||||
|
||||
public static final BiFunction<Color, Float, Consumer<Location>> DUST_PARTICLE_FACTORY = (color, thickness) -> {
|
||||
Particle.DustOptions dust = new Particle.DustOptions(color, thickness);
|
||||
return l -> l.getWorld().spawnParticle(Particle.DUST, l, 1, 0, 0, 0, 0, dust);
|
||||
};
|
||||
|
||||
public static final Function<Boolean, Consumer<Location>> FLAME_PARTICLE_FACTORY = soul -> {
|
||||
Particle flame = soul ? Particle.SOUL_FIRE_FLAME : Particle.FLAME;
|
||||
return l -> l.getWorld().spawnParticle(flame, l, 1, 0, 0, 0, 0);
|
||||
};
|
||||
|
||||
public static void ring(Location loc, double radius, Color color, float thickness) {
|
||||
ring(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness));
|
||||
}
|
||||
|
||||
public static void sphere(Location center, double radius, double pointDistance, Consumer<Location> action) {
|
||||
double dPhi = pointDistance / radius;
|
||||
|
||||
for (double phi = 0.0; phi <= Math.PI; phi += dPhi) {
|
||||
double yOffset = radius * Math.cos(phi);
|
||||
double ringRadius = radius * Math.sin(phi);
|
||||
|
||||
if (ringRadius < 1e-6) {
|
||||
Location loc = center.clone().add(0, yOffset, 0);
|
||||
action.accept(loc);
|
||||
} else {
|
||||
double dTheta = pointDistance / ringRadius;
|
||||
|
||||
for (double theta = 0.0; theta < 2 * Math.PI; theta += dTheta) {
|
||||
double xOffset = ringRadius * Math.cos(theta);
|
||||
double zOffset = ringRadius * Math.sin(theta);
|
||||
|
||||
Location loc = center.clone().add(xOffset, yOffset, zOffset);
|
||||
action.accept(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sphereWave(Location center, double maxRadius, double radialStep, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
AtomicReference<Double> currentRadius = new AtomicReference<>(radialStep);
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
double r = currentRadius.get();
|
||||
if (r > maxRadius) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
sphere(center, r, maxDistanceBetweenPoints, action);
|
||||
currentRadius.set(r + radialStep);
|
||||
}, 0L, 1L);
|
||||
}
|
||||
|
||||
public static void ring(Location loc, double radius, Consumer<Location> action) {
|
||||
for (int theta = 0; theta < 360; theta += 10) {
|
||||
double x = Math.cos(Math.toRadians(theta)) * radius;
|
||||
double z = Math.sin(Math.toRadians(theta)) * radius;
|
||||
Location newLoc = loc.clone().add(x, 0, z);
|
||||
action.accept(newLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ring(Location loc, double radius, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
arc(loc, radius, 0, 360, maxDistanceBetweenPoints, action);
|
||||
}
|
||||
|
||||
public static void wave(Location loc, double radius, Color color, float thickness, double gap) {
|
||||
wave(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness), gap);
|
||||
}
|
||||
|
||||
public static void wave(Location loc, double radius, Consumer<Location> action, double gap) {
|
||||
AtomicReference<Double> i = new AtomicReference<>(gap);
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
if (i.get() >= radius) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
ring(loc, i.get(), action);
|
||||
i.set(i.get() + gap);
|
||||
}, 0, 1);
|
||||
}
|
||||
|
||||
public static void wave(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
AtomicReference<Double> r = new AtomicReference<>(radialGap);
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
if (r.get() > radius) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
ring(loc, r.get(), maxDistanceBetweenPoints, action);
|
||||
r.set(r.get() + radialGap);
|
||||
}, 0, 1);
|
||||
}
|
||||
|
||||
public static void disc(Location loc, double radius, Consumer<Location> action, double gap) {
|
||||
for (double i = gap; i < radius; i += gap) {
|
||||
ring(loc, i, action);
|
||||
}
|
||||
}
|
||||
|
||||
public static void disc(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
for (double r = radialGap; r <= radius; r += radialGap) {
|
||||
ring(loc, r, maxDistanceBetweenPoints, action);
|
||||
}
|
||||
}
|
||||
|
||||
public static void helix(Location loc, double radius, Consumer<Location> action, double gap, int height) {
|
||||
int theta = 0;
|
||||
for (double y = 0; y <= height; y += gap) {
|
||||
double x = Math.cos(Math.toRadians(theta)) * radius;
|
||||
double z = Math.sin(Math.toRadians(theta)) * radius;
|
||||
|
||||
Location newLoc = loc.clone().add(x, y, z);
|
||||
action.accept(newLoc);
|
||||
theta += 10;
|
||||
}
|
||||
}
|
||||
|
||||
public static void vortex(Location loc, double radius, Consumer<Location> action, double gapH, double gapV, int height) {
|
||||
double r = radius;
|
||||
int theta = 0;
|
||||
for (double y = 0; y <= height; y += gapV) {
|
||||
double x = Math.cos(Math.toRadians(theta)) * r;
|
||||
double z = Math.sin(Math.toRadians(theta)) * r;
|
||||
|
||||
Location newLoc = loc.clone().add(x, y, z);
|
||||
action.accept(newLoc);
|
||||
r += gapH;
|
||||
theta += 10;
|
||||
}
|
||||
}
|
||||
|
||||
public static void beam(Location loc, Consumer<Location> action, double gap, int height) {
|
||||
for (double y = 0; y <= height; y += gap) {
|
||||
Location newLoc = loc.clone().add(0, y, 0);
|
||||
action.accept(newLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void arc(Location loc, double radius, int angleFrom, int angleTo, Consumer<Location> action) {
|
||||
for (int theta = angleFrom; theta < angleTo; theta += 10) {
|
||||
double x = Math.cos(Math.toRadians(theta)) * radius;
|
||||
double z = Math.sin(Math.toRadians(theta)) * radius;
|
||||
Location newLoc = loc.clone().add(x, 0, z);
|
||||
action.accept(newLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void arc(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer<Location> action) {
|
||||
int angleSpan = angleTo - angleFrom;
|
||||
if (angleSpan <= 0) return;
|
||||
|
||||
int points = Math.max(2, (int) ((2 * Math.PI * radius * (angleSpan / 360.0)) / maxDistanceBetweenPoints));
|
||||
double angleStep = (double) angleSpan / points;
|
||||
|
||||
for (int i = 0; i <= points; i++) {
|
||||
double theta = angleFrom + (i * angleStep);
|
||||
double x = Math.cos(Math.toRadians(theta)) * radius;
|
||||
double z = Math.sin(Math.toRadians(theta)) * radius;
|
||||
Location point = loc.clone().add(x, 0, z);
|
||||
action.accept(point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void fan(Location loc, double radius, int angleFrom, int angleTo, Consumer<Location> action, double gap) {
|
||||
for (double i = gap; i < radius; i += gap) {
|
||||
arc(loc, i, angleFrom, angleTo, action);
|
||||
}
|
||||
}
|
||||
|
||||
public static void fan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer<Location> action, double radialGap) {
|
||||
for (double r = radialGap; r < radius; r += radialGap) {
|
||||
arc(loc, r, angleFrom, angleTo, maxDistanceBetweenPoints, action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void fanWave(Location loc, double radius, int sections, Consumer<Location> action, double gap) {
|
||||
double arcLength = 360.0 / sections;
|
||||
AtomicReference<Double> i = new AtomicReference<>(0.0);
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
if (i.get() >= 360) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
double start = i.get();
|
||||
fan(loc, radius, (int)start, (int)(start + arcLength), action, gap);
|
||||
i.set(i.get() + arcLength);
|
||||
}, 0, 5);
|
||||
}
|
||||
|
||||
public static void fanWaveRandom(Location loc, double radius, int sections, Consumer<Location> action, double gap) {
|
||||
double arcLength = 360.0 / sections;
|
||||
List<Double> ints = new ArrayList<>();
|
||||
for (double start = 0; start < 360; start += arcLength) {
|
||||
ints.add(start);
|
||||
}
|
||||
|
||||
AtomicInteger i = new AtomicInteger(0);
|
||||
Randomizer random = new Randomizer();
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
if (i.get() >= sections) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
double start = random.getRandomElement(ints);
|
||||
ints.remove(start);
|
||||
fan(loc, radius, (int)start, (int)(start + arcLength), action, gap);
|
||||
i.getAndIncrement();
|
||||
}, 0, 5);
|
||||
}
|
||||
|
||||
public static void waveFan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer<Location> action, double radialGap) {
|
||||
AtomicReference<Double> r = new AtomicReference<>(radialGap);
|
||||
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> {
|
||||
if (r.get() >= radius) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
arc(loc, r.get(), angleFrom, angleTo, maxDistanceBetweenPoints, action);
|
||||
r.set(r.get() + radialGap);
|
||||
}, 0, 1);
|
||||
}
|
||||
|
||||
public static void waveFan(Location loc, double radius, Vector direction, int angle, double maxDistanceBetweenPoints, Consumer<Location> action, double radialGap) {
|
||||
double baseAngle = Math.toDegrees(Math.atan2(direction.getZ(), direction.getX()));
|
||||
int angleFrom = (int) (baseAngle - angle / 2.0);
|
||||
int angleTo = (int) (baseAngle + angle / 2.0);
|
||||
waveFan(loc, radius, angleFrom, angleTo, maxDistanceBetweenPoints, action, radialGap);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package me.trouper.alias.server.systems.world;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import me.trouper.alias.server.systems.Verbose;
|
||||
import me.trouper.alias.server.systems.burning.BlockBurner;
|
||||
import me.trouper.alias.server.systems.burning.BurnOptions;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class ExplosionUtils implements Main {
|
||||
|
||||
public static class ExplosionOptions {
|
||||
private double coreRadius = 3.0;
|
||||
private double falloffRadius = 8.0;
|
||||
private double maxBurnRadius = 15.0;
|
||||
private double destructionDelay = 0.0; // SECONDS
|
||||
private double burnDelay = 0.5; // SECONDS
|
||||
private double maxHeat = 1.0;
|
||||
private double minHeat = 0.1;
|
||||
private boolean createParticles = true;
|
||||
private boolean playSound = true;
|
||||
private BurnOptions burnOptions = new BurnOptions();
|
||||
|
||||
public double getCoreRadius() { return coreRadius; }
|
||||
public void setCoreRadius(double coreRadius) { this.coreRadius = coreRadius; }
|
||||
|
||||
public double getFalloffRadius() { return falloffRadius; }
|
||||
public void setFalloffRadius(double falloffRadius) { this.falloffRadius = falloffRadius; }
|
||||
|
||||
public double getMaxBurnRadius() { return maxBurnRadius; }
|
||||
public void setMaxBurnRadius(double maxBurnRadius) { this.maxBurnRadius = maxBurnRadius; }
|
||||
|
||||
public double getDestructionDelay() { return destructionDelay; }
|
||||
public void setDestructionDelay(double destructionDelay) { this.destructionDelay = destructionDelay; }
|
||||
|
||||
public double getBurnDelay() { return burnDelay; }
|
||||
public void setBurnDelay(double burnDelay) { this.burnDelay = burnDelay; }
|
||||
|
||||
public double getMaxHeat() { return maxHeat; }
|
||||
public void setMaxHeat(double maxHeat) { this.maxHeat = maxHeat; }
|
||||
|
||||
public double getMinHeat() { return minHeat; }
|
||||
public void setMinHeat(double minHeat) { this.minHeat = minHeat; }
|
||||
|
||||
public boolean isCreateParticles() { return createParticles; }
|
||||
public void setCreateParticles(boolean createParticles) { this.createParticles = createParticles; }
|
||||
|
||||
public boolean isPlaySound() { return playSound; }
|
||||
public void setPlaySound(boolean playSound) { this.playSound = playSound; }
|
||||
|
||||
public BurnOptions getBurnOptions() { return burnOptions; }
|
||||
public void setBurnOptions(BurnOptions burnOptions) { this.burnOptions = burnOptions; }
|
||||
}
|
||||
|
||||
public static class ExplosionResult {
|
||||
private final Map<Block, BlockState> originalStates = new HashMap<>();
|
||||
private final Map<Block, ItemStack[]> originalInventories = new HashMap<>();
|
||||
private final List<Integer> scheduledTaskIds = new ArrayList<>();
|
||||
private final BlockBurner burner;
|
||||
|
||||
public ExplosionResult(BlockBurner burner) {
|
||||
this.burner = burner;
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (burner != null) {
|
||||
burner.close();
|
||||
}
|
||||
|
||||
scheduledTaskIds.forEach(task -> Bukkit.getScheduler().cancelTask(task));
|
||||
}
|
||||
|
||||
void recordSnapshot(Block block) {
|
||||
BlockState state = block.getState();
|
||||
originalStates.put(block, state);
|
||||
if (state instanceof InventoryHolder) {
|
||||
Inventory inv = ((InventoryHolder) state).getInventory();
|
||||
originalInventories.put(block, inv.getContents());
|
||||
}
|
||||
}
|
||||
|
||||
void addScheduledTask(int taskId) {
|
||||
scheduledTaskIds.add(taskId);
|
||||
}
|
||||
|
||||
public void restore() {
|
||||
for (int taskId : scheduledTaskIds) {
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
for (Map.Entry<Block, BlockState> entry : originalStates.entrySet()) {
|
||||
Block block = entry.getKey();
|
||||
BlockState snapshot = entry.getValue();
|
||||
|
||||
block.setBlockData(snapshot.getBlockData(), false);
|
||||
snapshot.update(true, false);
|
||||
|
||||
ItemStack[] contents = originalInventories.get(block);
|
||||
if (contents != null && block.getState() instanceof InventoryHolder) {
|
||||
Inventory inv = ((InventoryHolder) block.getState()).getInventory();
|
||||
inv.setContents(contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlockBurner getBurner() { return burner; }
|
||||
|
||||
public Map<Block, BlockState> getOriginalStates() {
|
||||
return originalStates;
|
||||
}
|
||||
|
||||
public Map<Block, ItemStack[]> getOriginalInventories() {
|
||||
return originalInventories;
|
||||
}
|
||||
|
||||
public List<Integer> getScheduledTaskIds() {
|
||||
return scheduledTaskIds;
|
||||
}
|
||||
}
|
||||
|
||||
public static ExplosionResult createExplosion(Location center, ExplosionOptions options) {
|
||||
World world = center.getWorld();
|
||||
if (world == null) throw new IllegalArgumentException("Center location must have a valid world");
|
||||
|
||||
Map<Block, Double> affectedBlocks = getBlocksInRadius(center, options.getMaxBurnRadius());
|
||||
|
||||
ExplosionResult result = new ExplosionResult(new BlockBurner(options.getBurnOptions()));
|
||||
|
||||
for (Block block : affectedBlocks.keySet()) {
|
||||
if (block.getType().isAir()) continue;
|
||||
|
||||
result.recordSnapshot(block);
|
||||
}
|
||||
|
||||
Set<Block> blocksToDestroy = new HashSet<>();
|
||||
Set<Block> blocksToBurn = new HashSet<>();
|
||||
Map<Block, Float> blocksHeatMap = new HashMap<>();
|
||||
|
||||
categorizeBlocks(affectedBlocks, options, blocksToDestroy, blocksToBurn, blocksHeatMap);
|
||||
|
||||
BlockBurner burner = new BlockBurner(options.getBurnOptions());
|
||||
|
||||
scheduleDestruction(blocksToDestroy, options,result);
|
||||
scheduleBurning(blocksToBurn, blocksHeatMap, burner, center, options,result);
|
||||
|
||||
if (options.isCreateParticles() || options.isPlaySound()) createExplosionEffects(center, options);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<Block, Double> getBlocksInRadius(Location center, double radius) {
|
||||
Map<Block, Double> blocks = new HashMap<>();
|
||||
World world = center.getWorld();
|
||||
|
||||
int radiusInt = (int) Math.ceil(radius);
|
||||
Vector centerVec = center.toVector();
|
||||
|
||||
for (int x = -radiusInt; x <= radiusInt; x++) {
|
||||
for (int y = -radiusInt; y <= radiusInt; y++) {
|
||||
for (int z = -radiusInt; z <= radiusInt; z++) {
|
||||
Block block = world.getBlockAt(
|
||||
center.getBlockX() + x,
|
||||
center.getBlockY() + y,
|
||||
center.getBlockZ() + z
|
||||
);
|
||||
|
||||
double distance = block.getLocation().toVector().distance(centerVec);
|
||||
if (distance <= radius) {
|
||||
blocks.put(block, distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private static void categorizeBlocks(Map<Block, Double> affectedBlocks, ExplosionOptions options,
|
||||
Set<Block> blocksToDestroy, Set<Block> blocksToBurn,
|
||||
Map<Block, Float> blocksHeatMap) {
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
|
||||
for (Map.Entry<Block, Double> entry : affectedBlocks.entrySet()) {
|
||||
Block block = entry.getKey();
|
||||
double distance = entry.getValue();
|
||||
|
||||
if (block.getType().isAir() || !block.getType().isBlock()) continue;
|
||||
|
||||
float heat = calculateHeat(distance, options);
|
||||
blocksHeatMap.put(block, heat);
|
||||
|
||||
if (distance <= options.getCoreRadius()) blocksToDestroy.add(block);
|
||||
|
||||
if (distance <= options.getFalloffRadius()) {
|
||||
double destructionChance = 1.0 - ((distance - options.getCoreRadius()) /
|
||||
(options.getFalloffRadius() - options.getCoreRadius()));
|
||||
|
||||
destructionChance *= (0.7 + random.nextDouble() * 0.6);
|
||||
|
||||
if (random.nextDouble() < destructionChance) blocksToDestroy.add(block);
|
||||
else blocksToBurn.add(block);
|
||||
} else {
|
||||
double burnChance = 1.0 - ((distance - options.getFalloffRadius()) /
|
||||
(options.getMaxBurnRadius() - options.getFalloffRadius()));
|
||||
burnChance *= (0.8 + random.nextDouble() * 0.6);
|
||||
|
||||
if (random.nextDouble() < burnChance) blocksToBurn.add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float calculateHeat(double distance, ExplosionOptions options) {
|
||||
double normalizedDistance = distance / options.getMaxBurnRadius();
|
||||
double heatRange = options.getMaxHeat() - options.getMinHeat();
|
||||
|
||||
double heatFactor = Math.pow(1.0 - normalizedDistance, 2.0);
|
||||
|
||||
return (float) (options.getMinHeat() + heatRange * heatFactor);
|
||||
}
|
||||
|
||||
private static void scheduleDestruction(Set<Block> blocksToDestroy, ExplosionOptions options, ExplosionResult result) {
|
||||
if (blocksToDestroy.isEmpty()) return;
|
||||
|
||||
long destructionDelayTicks = (long) (options.getDestructionDelay() * 20);
|
||||
|
||||
int outerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
List<Block> blockList = new ArrayList<>(blocksToDestroy);
|
||||
Collections.shuffle(blockList);
|
||||
|
||||
int blocksPerWave = Math.max(1, blockList.size() / 5);
|
||||
|
||||
for (int wave = 0; wave < 5; wave++) {
|
||||
int startIndex = wave * blocksPerWave;
|
||||
int endIndex = Math.min(startIndex + blocksPerWave, blockList.size());
|
||||
|
||||
if (startIndex >= blockList.size()) break;
|
||||
|
||||
int innerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
Block block = blockList.get(i);
|
||||
if (!block.getType().isAir()) {
|
||||
block.setType(Material.AIR);
|
||||
}
|
||||
}
|
||||
},wave * 2).getTaskId();
|
||||
|
||||
result.addScheduledTask(innerTask);
|
||||
}
|
||||
},destructionDelayTicks).getTaskId();
|
||||
|
||||
result.addScheduledTask(outerTask);
|
||||
}
|
||||
|
||||
private static void scheduleBurning(Set<Block> blocksToMaybeBurn, Map<Block, Float> blocksHeatMap,
|
||||
BlockBurner burner, Location center, ExplosionOptions options,
|
||||
ExplosionResult result) {
|
||||
if (blocksToMaybeBurn.isEmpty()) return;
|
||||
|
||||
long burnDelayTicks = (long) (options.getBurnDelay() * 20);
|
||||
int outerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
List<Block> blockList = new ArrayList<>(blocksToMaybeBurn);
|
||||
Collections.shuffle(blockList);
|
||||
|
||||
Map<Integer, List<Block>> burnWaves = new HashMap<>();
|
||||
|
||||
for (Block block : blockList) {
|
||||
double distance = block.getLocation().distance(center);
|
||||
int waveIndex = (int) (distance / 2.0);
|
||||
|
||||
burnWaves.computeIfAbsent(waveIndex, k -> new ArrayList<>()).add(block);
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, List<Block>> waveEntry : burnWaves.entrySet()) {
|
||||
int waveDelay = waveEntry.getKey() * 3;
|
||||
List<Block> waveBlocks = waveEntry.getValue();
|
||||
|
||||
int middleTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
for (Block block : waveBlocks) {
|
||||
if (burner.isClosed()) continue;
|
||||
|
||||
float heat = blocksHeatMap.getOrDefault(block, 0.1f);
|
||||
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
int randomDelay = random.nextInt(0, 10);
|
||||
|
||||
int innerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
if (!burner.isClosed() && !block.getType().isAir()) {
|
||||
burner.burn(block, heat);
|
||||
}
|
||||
},randomDelay).getTaskId();
|
||||
|
||||
result.addScheduledTask(innerTask);
|
||||
}
|
||||
},waveDelay).getTaskId();
|
||||
|
||||
result.addScheduledTask(middleTask);
|
||||
}
|
||||
},burnDelayTicks).getTaskId();
|
||||
|
||||
result.addScheduledTask(outerTask);
|
||||
}
|
||||
|
||||
private static void createExplosionEffects(Location center, ExplosionOptions options) {
|
||||
World world = center.getWorld();
|
||||
if (world == null) return;
|
||||
|
||||
if (options.isPlaySound()) {
|
||||
world.playSound(center, Sound.ENTITY_GENERIC_EXPLODE, 1.0f, 0.8f);
|
||||
world.playSound(center, Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 0.5f, 1.2f);
|
||||
}
|
||||
|
||||
if (options.isCreateParticles()) {
|
||||
world.spawnParticle(Particle.EXPLOSION_EMITTER, center, 3);
|
||||
world.spawnParticle(Particle.EXPLOSION, center, 20, 2, 2, 2, 0.1);
|
||||
world.spawnParticle(Particle.LARGE_SMOKE, center, 15, 1, 1, 1, 0.05);
|
||||
world.spawnParticle(Particle.FLAME, center, 30, 3, 3, 3, 0.1);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
|
||||
world.spawnParticle(Particle.SMOKE, center, 50, 4, 4, 4, 0.02);
|
||||
},20);
|
||||
}
|
||||
}
|
||||
|
||||
public static ExplosionResult createExplosion(Location center) {
|
||||
return createExplosion(center, new ExplosionOptions());
|
||||
}
|
||||
|
||||
public static ExplosionResult createExplosion(Location center, double coreRadius, double falloffRadius, double maxBurnRadius) {
|
||||
ExplosionOptions options = new ExplosionOptions();
|
||||
options.setCoreRadius(coreRadius);
|
||||
options.setFalloffRadius(falloffRadius);
|
||||
options.setMaxBurnRadius(maxBurnRadius);
|
||||
options.setBurnDelay(0);
|
||||
options.setDestructionDelay(0);
|
||||
return createExplosion(center, options);
|
||||
}
|
||||
}
|
||||
262
src/main/java/me/trouper/alias/utils/ItemBuilder.java
Normal file
262
src/main/java/me/trouper/alias/utils/ItemBuilder.java
Normal file
@@ -0,0 +1,262 @@
|
||||
package me.trouper.alias.utils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeModifier;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.components.CustomModelDataComponent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ItemBuilder {
|
||||
private ItemStack stack;
|
||||
private ItemMeta meta;
|
||||
private CustomModelDataComponent modelData;
|
||||
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||
|
||||
public ItemBuilder() {
|
||||
this(new ItemStack(Material.STONE));
|
||||
}
|
||||
|
||||
public ItemBuilder(ItemStack stack) {
|
||||
this.stack = stack.clone();
|
||||
this.meta = this.stack.getItemMeta();
|
||||
if (this.meta == null) {
|
||||
throw new IllegalArgumentException("ItemStack must have ItemMeta");
|
||||
}
|
||||
}
|
||||
|
||||
public ItemBuilder(Material material) {
|
||||
this(new ItemStack(material));
|
||||
}
|
||||
|
||||
public ItemBuilder(Material material, int amount) {
|
||||
this(new ItemStack(material, amount));
|
||||
}
|
||||
|
||||
public ItemBuilder material(Material material) {
|
||||
this.stack = this.stack.withType(material);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder amount(int amount) {
|
||||
this.stack.setAmount(Math.max(1, Math.min(64, amount)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder displayName(Component name) {
|
||||
this.meta.displayName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder displayName(String miniMessageText) {
|
||||
Component component = miniMessage.deserialize(miniMessageText)
|
||||
.decoration(TextDecoration.ITALIC, false);
|
||||
return displayName(component);
|
||||
}
|
||||
|
||||
public ItemBuilder displayNameRaw(String text) {
|
||||
Component component = Component.text(text)
|
||||
.decoration(TextDecoration.ITALIC, false);
|
||||
return displayName(component);
|
||||
}
|
||||
|
||||
public ItemBuilder loreComponent(Component line) {
|
||||
List<Component> lore = this.meta.hasLore() ? this.meta.lore() : new ArrayList<>();
|
||||
if (lore == null) lore = new ArrayList<>();
|
||||
lore.add(line.decoration(TextDecoration.ITALIC, false));
|
||||
this.meta.lore(lore);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder loreComponent(List<Component> lines) {
|
||||
List<Component> processedLore = new ArrayList<>();
|
||||
for (Component line : lines) {
|
||||
processedLore.add(line.decoration(TextDecoration.ITALIC, false));
|
||||
}
|
||||
this.meta.lore(processedLore);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder loreComponent(Component... lines) {
|
||||
return loreComponent(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
public ItemBuilder loreMiniMessage(String line) {
|
||||
Component component = miniMessage.deserialize(line)
|
||||
.decoration(TextDecoration.ITALIC, false);
|
||||
return loreComponent(component);
|
||||
}
|
||||
|
||||
public ItemBuilder loreMiniMessage(List<String> lines) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
components.add(miniMessage.deserialize(line)
|
||||
.decoration(TextDecoration.ITALIC, false));
|
||||
}
|
||||
return loreComponent(components);
|
||||
}
|
||||
|
||||
public ItemBuilder loreMiniMessage(String... lines) {
|
||||
return loreMiniMessage(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
public ItemBuilder loreRaw(String line) {
|
||||
Component component = Component.text(line)
|
||||
.decoration(TextDecoration.ITALIC, false);
|
||||
return loreComponent(component);
|
||||
}
|
||||
|
||||
public ItemBuilder loreRaw(List<String> lines) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
components.add(Component.text(line)
|
||||
.decoration(TextDecoration.ITALIC, false));
|
||||
}
|
||||
return loreComponent(components);
|
||||
}
|
||||
|
||||
public ItemBuilder loreRaw(String... lines) {
|
||||
return loreRaw(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
public ItemBuilder clearLore() {
|
||||
this.meta.lore(new ArrayList<>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder enchant(Enchantment enchantment, int level) {
|
||||
this.meta.addEnchant(enchantment, level, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder enchant(Enchantment enchantment) {
|
||||
return enchant(enchantment, 1);
|
||||
}
|
||||
|
||||
public ItemBuilder removeEnchant(Enchantment enchantment) {
|
||||
this.meta.removeEnchant(enchantment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder clearEnchants() {
|
||||
this.meta.getEnchants().keySet().forEach(this.meta::removeEnchant);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder flags(ItemFlag... flags) {
|
||||
this.meta.addItemFlags(flags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder removeFlags(ItemFlag... flags) {
|
||||
this.meta.removeItemFlags(flags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder hideAllFlags() {
|
||||
return flags(ItemFlag.values());
|
||||
}
|
||||
|
||||
public ItemBuilder attribute(Attribute attribute, AttributeModifier modifier) {
|
||||
this.meta.addAttributeModifier(attribute, modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder removeAttribute(Attribute attribute) {
|
||||
this.meta.removeAttributeModifier(attribute);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder unbreakable(boolean unbreakable) {
|
||||
this.meta.setUnbreakable(unbreakable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder unbreakable() {
|
||||
return unbreakable(true);
|
||||
}
|
||||
|
||||
public ItemBuilder customModelData(CustomModelDataComponent data) {
|
||||
this.modelData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder clearCustomModelData() {
|
||||
this.modelData = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder modifyStack(Function<ItemStack, ItemStack> modifier) {
|
||||
this.stack = modifier.apply(this.build());
|
||||
this.meta = this.stack.getItemMeta();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemBuilder modifyMeta(Function<ItemMeta, ItemMeta> modifier) {
|
||||
this.meta = modifier.apply(this.meta);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T extends ItemMeta> ItemBuilder modifyMeta(Class<T> metaClass, Function<T, T> modifier) {
|
||||
if (metaClass.isInstance(this.meta)) {
|
||||
this.meta = modifier.apply(metaClass.cast(this.meta));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemStack build() {
|
||||
this.meta.setCustomModelDataComponent(this.modelData);
|
||||
this.stack.setItemMeta(this.meta);
|
||||
return this.stack.clone();
|
||||
}
|
||||
|
||||
public ItemStack buildAndGet() {
|
||||
return build();
|
||||
}
|
||||
|
||||
public ItemBuilder clone() {
|
||||
return new ItemBuilder(this.build());
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return this.stack.getType();
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return this.stack.getAmount();
|
||||
}
|
||||
|
||||
public static ItemBuilder create() {
|
||||
return new ItemBuilder();
|
||||
}
|
||||
|
||||
public static ItemBuilder create(ItemStack stack) {
|
||||
return new ItemBuilder(stack);
|
||||
}
|
||||
|
||||
public static ItemBuilder create(Material material) {
|
||||
return new ItemBuilder(material);
|
||||
}
|
||||
|
||||
public static ItemBuilder create(Material material, int amount) {
|
||||
return new ItemBuilder(material, amount);
|
||||
}
|
||||
|
||||
public static ItemBuilder of(Material material) {
|
||||
return create(material);
|
||||
}
|
||||
|
||||
public static ItemBuilder of(ItemStack stack) {
|
||||
return create(stack);
|
||||
}
|
||||
}
|
||||
285
src/main/java/me/trouper/alias/utils/SoundPlayer.java
Executable file → Normal file
285
src/main/java/me/trouper/alias/utils/SoundPlayer.java
Executable file → Normal file
@@ -1,266 +1,75 @@
|
||||
package me.trouper.alias.utils;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.SoundCategory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class SoundPlayer implements Main {
|
||||
import java.util.Collection;
|
||||
|
||||
private Location location;
|
||||
private Sound sound;
|
||||
private float volume;
|
||||
private float pitch;
|
||||
public class SoundPlayer {
|
||||
private final Sound sound;
|
||||
private final float volume;
|
||||
private final float pitch;
|
||||
private final SoundCategory category;
|
||||
|
||||
/**
|
||||
* Constructs a new sound, this aims to add more methods to
|
||||
* the Bukkit APIs Sound class, as they don't have many
|
||||
* methods to use.
|
||||
*
|
||||
* @param location Location
|
||||
* @param sound Sound
|
||||
* @param volume float
|
||||
* @param pitch float
|
||||
*/
|
||||
public SoundPlayer(Location location, Sound sound, float volume, float pitch) {
|
||||
this.location = location;
|
||||
public SoundPlayer(Sound sound) {
|
||||
this(sound, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
public SoundPlayer(Sound sound, float volume, float pitch) {
|
||||
this(sound, volume, pitch, SoundCategory.MASTER);
|
||||
}
|
||||
|
||||
public SoundPlayer(Sound sound, float volume, float pitch, SoundCategory category) {
|
||||
this.sound = sound;
|
||||
this.pitch = pitch;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Plays a sound to a player but at the store location
|
||||
*
|
||||
* @param player Player
|
||||
*/
|
||||
public void play(Player player) {
|
||||
player.playSound(this.location,this.sound,this.volume,this.pitch);
|
||||
public void playTo(Player player) {
|
||||
player.playSound(player.getLocation(), sound, category, volume, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a sound to a player but at the player's location
|
||||
*
|
||||
* @param player Player
|
||||
*/
|
||||
public void playIndividually(Player player) {
|
||||
player.playSound(player.getLocation(),this.sound,this.volume,this.pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the sound to all players within a distance, but at the stored location.
|
||||
*
|
||||
* @param distance double
|
||||
*/
|
||||
public void playWithin(double distance) {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p != null && p.getWorld() == this.location.getWorld() && p.getLocation().distance(this.location) < distance) {
|
||||
p.playSound(this.location,this.sound,this.volume,this.pitch);
|
||||
}
|
||||
public void playTo(Collection<? extends Player> players) {
|
||||
for (Player player : players) {
|
||||
playTo(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the sound to all players within a distance, but at the players' location.
|
||||
*
|
||||
* @param distance double
|
||||
*/
|
||||
public void playWithinIndividually(double distance) {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p != null && p.getWorld() == this.location.getWorld() && p.getLocation().distance(this.location) < distance) {
|
||||
p.playSound(p.getLocation(),this.sound,this.volume,this.pitch);
|
||||
}
|
||||
public void playAt(Location location, double radius) {
|
||||
Collection<? extends Player> nearby = location.getWorld().getPlayers().stream()
|
||||
.filter(player -> player.getLocation().distanceSquared(location) <= radius * radius)
|
||||
.toList();
|
||||
|
||||
for (Player player : nearby) {
|
||||
player.playSound(location, sound, category, volume, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Plays the sound to all players on the server, but at the stored location.
|
||||
*/
|
||||
public void playAll() {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) p.playSound(this.location,this.sound,this.volume,this.pitch);
|
||||
public static void play(Player player, Sound sound) {
|
||||
player.playSound(player.getLocation(), sound, SoundCategory.MASTER, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the sound to all players on the server, but at the players' location.
|
||||
*/
|
||||
public void playAllIndividually() {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) p.playSound(p.getLocation(),this.sound,this.volume,this.pitch);
|
||||
public static void play(Player player, Sound sound, float volume, float pitch) {
|
||||
player.playSound(player.getLocation(), sound, SoundCategory.MASTER, volume, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to a player, but at the stored location.
|
||||
*
|
||||
* @param player Player
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeat(Player player, int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
play(player);
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
public static void play(Location location, Sound sound, float volume, float pitch, double radius) {
|
||||
Collection<? extends Player> nearby = location.getWorld().getPlayers().stream()
|
||||
.filter(player -> player.getLocation().distanceSquared(location) <= radius * radius)
|
||||
.toList();
|
||||
|
||||
for (Player player : nearby) {
|
||||
player.playSound(location, sound, SoundCategory.MASTER, volume, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to a player, but at the player's location.
|
||||
*
|
||||
* @param player Player
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeatIndividually(Player player, int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
playIndividually(player);
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to all players on the server, but at the stored location.
|
||||
*
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeatAll(int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
playAll();
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to all players on the server, but at the players' location.
|
||||
*
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeatAllIndividually(int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
playAllIndividually();
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to all players within a radius, but at the stored location.
|
||||
*
|
||||
* @param radius double
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeatAll(double radius,int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
playWithin(radius);
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats a sound to all players within a radius, but at the players' location.
|
||||
*
|
||||
* @param distance double
|
||||
* @param times int
|
||||
* @param tickDelay int
|
||||
*/
|
||||
public void repeatAllIndividually(double distance, int times, int tickDelay) {
|
||||
new BukkitRunnable() {
|
||||
int i = 0;
|
||||
@Override
|
||||
public void run() {
|
||||
if (i < times) {
|
||||
playWithinIndividually(distance);
|
||||
i ++;
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(getPlugin(),0,tickDelay);
|
||||
}
|
||||
|
||||
public Sound getSound() {
|
||||
return sound;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public float getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setPitch(float pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public void setVolume(float volume) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public void setSound(Sound sound) {
|
||||
this.sound = sound;
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void changePlayer(Location location, Sound sound, float volume, float pitch) {
|
||||
this.location = location;
|
||||
this.sound = sound;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public void changePlayer(Sound sound, float volume, float pitch) {
|
||||
changePlayer(location, sound, volume, pitch);
|
||||
public static void play(Collection<? extends Player> players, Sound sound, float volume, float pitch) {
|
||||
for (Player player : players) {
|
||||
player.playSound(player.getLocation(), sound, SoundCategory.MASTER, volume, pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -225,8 +226,8 @@ public class TargetingUtils {
|
||||
* @return An {@link Optional} containing the {@link LivingEntity} closest to the aim vector,
|
||||
* or an empty Optional if no suitable entity is found or world is null.
|
||||
*/
|
||||
public static Optional<LivingEntity> getLivingEntityClosestToVector(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians) {
|
||||
return getLivingEntityClosestToVector(originEyeLocation, direction, maxDistance, maxAngleRadians, entity -> true);
|
||||
public static Optional<LivingEntity> livingClosestAngle(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians) {
|
||||
return livingClosestAngle(originEyeLocation, direction, maxDistance, maxAngleRadians, entity -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +243,7 @@ public class TargetingUtils {
|
||||
* @return An {@link Optional} containing the {@link LivingEntity} closest to the aim vector and matching the filter,
|
||||
* or an empty Optional if no suitable entity is found or world is null.
|
||||
*/
|
||||
public static Optional<LivingEntity> getLivingEntityClosestToVector(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians, Predicate<LivingEntity> filter) {
|
||||
public static Optional<LivingEntity> livingClosestAngle(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians, Predicate<LivingEntity> filter) {
|
||||
if (originEyeLocation == null || originEyeLocation.getWorld() == null || direction == null || filter == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -279,4 +280,15 @@ public class TargetingUtils {
|
||||
}
|
||||
return Optional.ofNullable(bestTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all living entities inside the bounding box.
|
||||
* @param w World to check.
|
||||
* @param box Box inside the world, entities must be colliding with it.
|
||||
* @param filter A predicate applied to each living entity. Return true to accept.
|
||||
* @return A list of living entities colliding with the box.
|
||||
*/
|
||||
public static List<LivingEntity> livingInBound(World w, BoundingBox box, Predicate<LivingEntity> filter) {
|
||||
return w.getNearbyEntities(box).stream().filter(entity -> entity instanceof LivingEntity liv && filter.test(liv)).map(entity -> (LivingEntity) entity).toList();
|
||||
}
|
||||
}
|
||||
|
||||
76
src/main/java/me/trouper/alias/utils/misc/Cooldown.java
Executable file → Normal file
76
src/main/java/me/trouper/alias/utils/misc/Cooldown.java
Executable file → Normal file
@@ -2,41 +2,81 @@ package me.trouper.alias.utils.misc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Cooldown<T> {
|
||||
|
||||
private final Map<T, Long> timer;
|
||||
private final Map<T, Long> cooldowns = new HashMap<>();
|
||||
|
||||
public Cooldown() {
|
||||
this.timer = new HashMap<>();
|
||||
public void setCooldown(T holder, long duration) {
|
||||
cooldowns.put(holder, System.currentTimeMillis() + duration);
|
||||
}
|
||||
|
||||
private <O> O getOrDefault(O value, O def) {
|
||||
return value != null ? value : def;
|
||||
public boolean isOnCooldown(T holder) {
|
||||
return getRemaining(holder) > 0;
|
||||
}
|
||||
|
||||
public long getCooldown(T obj) {
|
||||
return Math.max(getOrDefault(timer.get(obj), 0L) - System.currentTimeMillis(), 0L);
|
||||
public long getRemaining(T holder) {
|
||||
return Math.max(0, cooldowns.getOrDefault(holder, 0L) - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public double getCooldownSec(T obj) {
|
||||
final long cooldown = this.getCooldown(obj);
|
||||
return Math.floor(cooldown / 10.0) / 100.0;
|
||||
public void clearCooldown(T holder) {
|
||||
cooldowns.remove(holder);
|
||||
}
|
||||
|
||||
public boolean isOnCooldown(T obj) {
|
||||
return getCooldown(obj) > 0L;
|
||||
public String formatShort(T holder) {
|
||||
return formatShort(getRemaining(holder));
|
||||
}
|
||||
|
||||
public void setCooldown(T obj, long millis) {
|
||||
timer.put(obj, System.currentTimeMillis() + millis);
|
||||
public String formatLong(T holder) {
|
||||
return formatLong(getRemaining(holder));
|
||||
}
|
||||
|
||||
public void addCooldown(T obj, long millis) {
|
||||
setCooldown(obj, getCooldown(obj) + millis);
|
||||
public String formatColon(T holder) {
|
||||
return formatColon(getRemaining(holder));
|
||||
}
|
||||
|
||||
public void removeCooldown(T obj) {
|
||||
timer.remove(obj);
|
||||
public static String formatShort(long ms) {
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(ms);
|
||||
long minutes = seconds / 60;
|
||||
seconds %= 60;
|
||||
|
||||
long hours = minutes / 60;
|
||||
minutes %= 60;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (hours > 0) sb.append(hours).append("h ");
|
||||
if (minutes > 0 || hours > 0) sb.append(minutes).append("m ");
|
||||
sb.append(seconds).append("s");
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
public static String formatLong(long ms) {
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(ms);
|
||||
long minutes = seconds / 60;
|
||||
seconds %= 60;
|
||||
|
||||
long hours = minutes / 60;
|
||||
minutes %= 60;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (hours > 0) sb.append(hours).append(" hour").append(hours == 1 ? "" : "s").append(", ");
|
||||
if (minutes > 0) sb.append(minutes).append(" minute").append(minutes == 1 ? "" : "s").append(", ");
|
||||
sb.append(seconds).append(" second").append(seconds == 1 ? "" : "s");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatColon(long ms) {
|
||||
long totalSeconds = TimeUnit.MILLISECONDS.toSeconds(ms);
|
||||
long hours = totalSeconds / 3600;
|
||||
long minutes = (totalSeconds % 3600) / 60;
|
||||
long seconds = totalSeconds % 60;
|
||||
|
||||
if (hours > 0)
|
||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
else
|
||||
return String.format("%02d:%02d", minutes, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.trouper.alias.utils.misc;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
public static Set<Class<?>> getClassesInPackage(JavaPlugin plugin, String pkg) {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
String path = pkg.replace('.', '/');
|
||||
try {
|
||||
File file = new File(plugin.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
try (JarFile jar = new JarFile(file)) {
|
||||
for (JarEntry entry : jar.stream().toList()) {
|
||||
String name = entry.getName();
|
||||
if (!name.endsWith(".class") || !name.startsWith(path)) continue;
|
||||
|
||||
String className = name.replace('/', '.').replace(".class", "");
|
||||
Class<?> clazz = Class.forName(className);
|
||||
classes.add(clazz);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Failed to scan package: " + pkg);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
||||
89
src/main/java/me/trouper/alias/utils/misc/Timer.java
Normal file
89
src/main/java/me/trouper/alias/utils/misc/Timer.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package me.trouper.alias.utils.misc;
|
||||
|
||||
public class Timer {
|
||||
|
||||
public static final long MILLIS_IN_SECOND = 1000L;
|
||||
public static final long MILLIS_IN_MINUTE = MILLIS_IN_SECOND * 60L;
|
||||
public static final long MILLIS_IN_HOUR = MILLIS_IN_MINUTE * 60L;
|
||||
public static final long MILLIS_IN_DAY = MILLIS_IN_HOUR * 24L;
|
||||
private long start;
|
||||
|
||||
private Timer() {
|
||||
this.start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static Timer start() {
|
||||
return new Timer();
|
||||
}
|
||||
|
||||
public static End zero() {
|
||||
return new End(0);
|
||||
}
|
||||
|
||||
public End end() {
|
||||
return new End(start);
|
||||
}
|
||||
|
||||
|
||||
public static class End {
|
||||
|
||||
private final long start;
|
||||
private final long end;
|
||||
|
||||
private End(long start) {
|
||||
this.end = System.currentTimeMillis();
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public long timePassed() {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
public String getStamp(boolean day, boolean hr, boolean min, boolean sec, boolean ms) {
|
||||
long time = timePassed();
|
||||
String stamp = "";
|
||||
|
||||
if (day) {
|
||||
long l = (long)Math.floor((double)time / (double)MILLIS_IN_DAY);
|
||||
time -= l * MILLIS_IN_DAY;
|
||||
if (l > 0L) stamp += l + "d";
|
||||
}
|
||||
if (hr) {
|
||||
long l = (long)Math.floor((double)time / (double)MILLIS_IN_HOUR);
|
||||
time -= l * MILLIS_IN_HOUR;
|
||||
if (l > 0L) stamp += " " + l + "hr";
|
||||
}
|
||||
if (min) {
|
||||
long l = (long)Math.floor((double)time / (double)MILLIS_IN_MINUTE);
|
||||
time -= l * MILLIS_IN_MINUTE;
|
||||
if (l > 0L) stamp += " " + l + "min";
|
||||
}
|
||||
if (sec) {
|
||||
long l = (long)Math.floor((double)time / (double)MILLIS_IN_SECOND);
|
||||
time -= l * MILLIS_IN_SECOND;
|
||||
if (l > 0L) stamp += " " + l + "sec";
|
||||
}
|
||||
if (ms) {
|
||||
if (time > 0L) stamp += " " + time + "ms";
|
||||
}
|
||||
|
||||
return stamp.trim();
|
||||
}
|
||||
|
||||
public String getStampStandard() {
|
||||
return getStamp(false, true, true, false, false);
|
||||
}
|
||||
|
||||
public String getStampLogger() {
|
||||
return getStamp(false, true, true, true, false);
|
||||
}
|
||||
|
||||
public String getStampPrecise() {
|
||||
return getStamp(false, false, true, true, true);
|
||||
}
|
||||
|
||||
public String getStampFull() {
|
||||
return getStamp(true, true, true, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user