Initial Commit

This commit is contained in:
wolf
2025-06-24 20:24:26 -04:00
commit ef1790b3fc
72 changed files with 6564 additions and 0 deletions

119
.gitignore vendored Normal file
View File

@@ -0,0 +1,119 @@
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Cache of project
.gradletasknamecache
**/build/
# Common working directory
run/
runs/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

78
build.gradle Normal file
View File

@@ -0,0 +1,78 @@
plugins {
id 'java'
id("xyz.jpenilla.run-paper") version "2.3.1"
id 'com.gradleup.shadow' version '9.0.0-beta10'
}
group = 'me.trouper'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
mavenLocal()
maven {
name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
maven {
url = uri("https://repo.codemc.io/repository/maven-releases/")
}
maven {
url = uri("https://repo.codemc.io/repository/maven-snapshots/")
}
maven{
url = uri("https://jitpack.io")
}
maven {
name = "CodeMC"
url = uri("https://repo.codemc.io/repository/maven-public/")
}
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
compileOnly("com.github.retrooper:packetevents-spigot:2.8.0")
compileOnly("com.gitlab.ruany:LiteBansAPI:0.6.1")
compileOnly("de.tr7zw:item-nbt-api-plugin:2.15.0")
implementation("me.trouper:alias:1.0-1.21.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.4")
}
tasks {
runServer {
minecraftVersion("1.21.5")
}
}
def targetJavaVersion = 21
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release.set(targetJavaVersion)
}
}
processResources {
def props = [version: version]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
}
shadowJar {
minimize()
}

0
gradle.properties Normal file
View File

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

249
gradlew vendored Executable file
View File

@@ -0,0 +1,249 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
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.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
settings.gradle Normal file
View File

@@ -0,0 +1 @@
rootProject.name = 'CloneDupeCore'

View File

@@ -0,0 +1,59 @@
package me.trouper.clonedupecore;
import com.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import me.trouper.clonedupecore.server.Manager;
import org.bukkit.NamespacedKey;
import org.bukkit.plugin.java.JavaPlugin;
public final class CloneDupeCore extends JavaPlugin {
private static CloneDupeCore instance;
private Manager manager;
@Override
public void onLoad() {
getLogger().info("Setting PacketEvents API");
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load();
getLogger().info("Instantiating Plugin");
instance = this;
}
@Override
public void onEnable() {
getLogger().info("Initializing PacketEvents");
PacketEvents.getAPI().init();
getLogger().info("Instantiating Manager");
manager = new Manager(instance);
getLogger().info("Initializing Manager");
manager.init();
getLogger().info("Successfully enabled CloneDupeCore.");
}
@Override
public void onDisable() {
getLogger().info("Cleaning up...");
manager.cleanup();
getLogger().info("Saved all IO files.");
manager.io.saveAll();
PacketEvents.getAPI().terminate();
}
public static CloneDupeCore getInstance() {
return instance;
}
public NamespacedKey getNameSpace() {
return new NamespacedKey(getInstance(),"clone_dupe_core");
}
public Manager getManager() {
return manager;
}
}

View File

@@ -0,0 +1,24 @@
package me.trouper.clonedupecore.data;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.data.io.Config;
import me.trouper.clonedupecore.data.io.NBTConfig;
import me.trouper.clonedupecore.data.io.Storage;
public interface Data {
default Config getConfig() {
return CloneDupeCore.getInstance().getManager().io.config;
}
default Storage getStorage() {
return CloneDupeCore.getInstance().getManager().io.storage;
}
default NBTConfig getNBT() {
return CloneDupeCore.getInstance().getManager().io.nbtConfig;
}
default IO getIO() {
return CloneDupeCore.getInstance().getManager().io;
}
}

View File

@@ -0,0 +1,48 @@
package me.trouper.clonedupecore.data;
import me.trouper.alias.data.JsonSerializable;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.data.io.Config;
import me.trouper.clonedupecore.data.io.NBTConfig;
import me.trouper.clonedupecore.data.io.Storage;
import java.io.File;
public class IO {
public final File DATA_FOLDER;
public final File CONFIG_FILE;
public final File STORAGE_FILE;
public final File NBT_FILE;
public Config config;
public Storage storage;
public NBTConfig nbtConfig;
public IO(File dataFolder) {
DATA_FOLDER = dataFolder;
CONFIG_FILE = new File(DATA_FOLDER,"/config.json");
STORAGE_FILE = new File(DATA_FOLDER, "/storage.json");
NBT_FILE = new File(DATA_FOLDER, "/enchants.json");
config = new Config();
storage = new Storage();
nbtConfig = new NBTConfig();
}
public void loadAll() {
CloneDupeCore.getInstance().getLogger().info("Loading all IO Files");
config = JsonSerializable.load(CONFIG_FILE,Config.class,new Config());
storage = JsonSerializable.load(STORAGE_FILE,Storage.class,new Storage());
nbtConfig = JsonSerializable.load(NBT_FILE, NBTConfig.class,new NBTConfig());
saveAll();
}
public void saveAll() {
CloneDupeCore.getInstance().getLogger().info("Saving all IO Files");
if (config == null) config = new Config();
if (storage == null) storage = new Storage();
if (nbtConfig == null) nbtConfig = new NBTConfig();
config.save();
storage.save();
nbtConfig.save();
}
}

View File

@@ -0,0 +1,56 @@
package me.trouper.clonedupecore.data.io;
import me.trouper.alias.data.JsonSerializable;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.CloneDupeCore;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Config implements JsonSerializable<Config> {
public boolean debugMode = false;
public List<String> debuggerExclusions = new ArrayList<>();
@Override
public File getFile() {
return CloneDupeCore.getInstance().getManager().io.CONFIG_FILE;
}
@Override
public void save() {
CloneDupeCore.getInstance().getLogger().info("Saving Config...");
JsonSerializable.super.save();
}
public Messages messages = new Messages();
public class Messages {
public int mainColor = 0xFF88AA;
public int secondaryColor = 0xFFDDDD;
public String flatPrefix = "§c§lCloneDupe §8» §7";
public String pluginName = "CloneDupe";
}
public List<String> nbtWhitelist = new ArrayList<>(List.of(
"8447c822-6175-4081-b5aa-13fc6c6e69d2",
"9ccc7b49-7695-40f8-9990-f7436645e4ca",
"049460f7-21cb-42f5-8059-d42752bf406f",
"299a9071-ec6d-4d2c-8c7b-d7bba934fbe5"
));
public String banWebhook = "https://discord.com/api/webhooks/1386722424585719809/d8hFffcvqlPEpPSSnOPzGJ3TKgcM4nPJnTsE976-yRsHYgqB9IgyUf2nBJKIepxV_sr6";
public List<String> banTemplates = new ArrayList<>(List.of(
"hacking",
"bugs",
"dupes",
"evasion",
"screen"
));
public List<String> getBanTemplateNames() {
return new ArrayList<>(banTemplates);
}
}

View File

@@ -0,0 +1,85 @@
package me.trouper.clonedupecore.data.io;
import me.trouper.alias.data.JsonSerializable;
import me.trouper.clonedupecore.CloneDupeCore;
import java.io.File;
import java.util.List;
public class NBTConfig implements JsonSerializable<NBTConfig> {
@Override
public File getFile() {
return CloneDupeCore.getInstance().getManager().io.NBT_FILE;
}
@Override
public void save() {
CloneDupeCore.getInstance().getLogger().info("Saving Storage...");
JsonSerializable.super.save();
}
public RateLimit rateLimit = new RateLimit();
public class RateLimit {
public int maxOverhead = 32768;
public int rateLimitBytes = 16348;
public int byteDecay = 1024; // Every Minute
public int rateLimitItems = 10;
public int itemDecay = 5; // Every 10 seconds
public List<String> punishmentCommands = List.of("kick %player% Internal Exception: io.netty.handler.codec.DecoderException: java.lang.RuntimeException: Tried to read NBT tag that was too big; tried to allocate %s bytes where max allowed: %s");
}
public boolean allowName = true;
public boolean allowLore = true;
public boolean allowAttributes = false;
public boolean allowPotions = false;
public boolean allowCustomConsumables = false;
public boolean allowCustomTools = false;
public boolean allowBooks = false;
public boolean allowRecursion = true;
public int maxCustomData = 64;
public int globalMaxEnchant = 5;
public int maxMending = 1;
public int maxUnbreaking = 3;
public int maxCurseOfVanishing = 1;
public int maxAquaAffinity = 1;
public int maxBlastProtection = 4;
public int maxCurseOfBinding = 1;
public int maxDepthStrider = 3;
public int maxFeatherFalling = 4;
public int maxFireProtection = 4;
public int maxFrostWalker = 2;
public int maxProjectileProtection = 4;
public int maxProtection = 4;
public int maxRespiration = 3;
public int maxSoulSpeed = 3;
public int maxThorns = 3;
public int maxSwiftSneak = 3;
public int maxBaneOfArthropods = 5;
public int maxEfficiency = 5;
public int maxFireAspect = 2;
public int maxLooting = 3;
public int maxImpaling = 5;
public int maxKnockback = 2;
public int maxSharpness = 5;
public int maxSmite = 5;
public int maxSweepingEdge = 3;
public int maxChanneling = 1;
public int maxFlame = 1;
public int maxInfinity = 1;
public int maxLoyalty = 3;
public int maxRiptide = 3;
public int maxMultishot = 1;
public int maxPiercing = 4;
public int maxPower = 5;
public int maxPunch = 2;
public int maxQuickCharge = 3;
public int maxFortune = 3;
public int maxLuckOfTheSea = 3;
public int maxLure = 3;
public int maxSilkTouch = 1;
public int maxBreach = 4;
public int maxDensity = 5;
public int maxWindBurst = 3;
}

View File

@@ -0,0 +1,26 @@
package me.trouper.clonedupecore.data.io;
import me.trouper.alias.data.JsonSerializable;
import me.trouper.clonedupecore.CloneDupeCore;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Storage implements JsonSerializable<Storage> {
@Override
public File getFile() {
return CloneDupeCore.getInstance().getManager().io.STORAGE_FILE;
}
@Override
public void save() {
CloneDupeCore.getInstance().getLogger().info("Saving Storage...");
JsonSerializable.super.save();
}
public Set<String> disabledOwnParticles = new HashSet<>();
public Set<String> disabledGlobalParticles = new HashSet<>();
}

View File

@@ -0,0 +1,58 @@
package me.trouper.clonedupecore.server;
import me.trouper.alias.Alias;
import me.trouper.alias.data.Common;
import me.trouper.alias.server.systems.tracing.BlockDisplayRaytracer;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.data.IO;
import me.trouper.clonedupecore.server.trims.TrimManager;
import me.trouper.clonedupecore.server.trims.animations.*;
import org.bukkit.plugin.java.JavaPlugin;
public class Manager {
public IO io;
public Common common;
public TrimManager trimManager;
public Manager(JavaPlugin instance) {
io = new IO(instance.getDataFolder());
common = new Common(instance.getClass().getPackageName(),0xFF00AA,0xFFDDDD,"CloneDupeCore","CloneDupe> ",false);
trimManager = new TrimManager();
}
public void init() {
io.loadAll();
setCommon();
Alias.register(CloneDupeCore.getInstance(),common);
cleanup();
trimManager.register(new AmethystAnimation());
trimManager.register(new AmethystAnimation());
trimManager.register(new CopperAnimation());
trimManager.register(new DiamondAnimation());
trimManager.register(new EmeraldAnimation());
trimManager.register(new GoldAnimation());
trimManager.register(new IronAnimation());
trimManager.register(new LapisAnimation());
trimManager.register(new NetheriteAnimation());
trimManager.register(new QuartzAnimation());
trimManager.register(new RedstoneAnimation());
trimManager.startTicking();
}
public void setCommon() {
common.setMainColor(io.config.messages.mainColor);
common.setSecondaryColor(io.config.messages.secondaryColor);
common.setFlatPrefix(io.config.messages.flatPrefix);
common.setPluginName(io.config.messages.pluginName);
common.setDebugMode(io.config.debugMode);
io.config.debuggerExclusions.forEach(exclusion -> common.addDebuggerExclusion(exclusion));
}
public void cleanup() {
BlockDisplayRaytracer.cleanup();
}
}

View File

@@ -0,0 +1,199 @@
package me.trouper.clonedupecore.server.commands;
import me.trouper.alias.server.commands.Args;
import me.trouper.alias.server.commands.CommandRegistry;
import me.trouper.alias.server.commands.Permission;
import me.trouper.alias.server.commands.QuickCommand;
import me.trouper.alias.server.commands.completions.CompletionBuilder;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.utils.FormatUtils;
import me.trouper.alias.utils.SoundPlayer;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.data.Data;
import me.trouper.clonedupecore.server.trims.ValidArmorType;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Sound;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ArmorMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.trim.ArmorTrim;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import java.util.List;
@CommandRegistry(value = "clonedupecore",permission = @Permission("clonedupe.admin"))
public class AdminCommand implements QuickCommand, Data {
@Override
public void handleCommand(CommandSender commandSender, Command command, String s, Args args) {
if (args.getSize() < 1) {
errorAny(commandSender,"You must choose an argument.");
}
switch (args.get(0).toString()) {
case "debug" -> handleDebug(commandSender,args);
case "trim" -> handleTrim(commandSender,args);
case "reload" -> handleReload(commandSender,args);
}
}
@Override
public void handleCompletion(CommandSender commandSender, Command command, String s, Args args, CompletionBuilder b) {
b.then(
b.arg("debug")
.then(
b.arg("toggle")
)
.then(
b.arg("exclude")
.then(
b.arg("Class.method")))
.then(
b.arg("include")
.then(
b.arg(getConfig().debuggerExclusions)
)
)
).then(
b.arg("trim")
.then(
b.argEnum(ValidMaterial.class)
.then(
b.argEnum(ValidArmorType.class)
)
)
);
}
private void handleReload(CommandSender sender, Args args) {
successAny(sender,"Reloading IO and common...");
getIO().loadAll();
CloneDupeCore.getInstance().getManager().setCommon();
}
private void handleTrim(CommandSender sender, Args args) {
if (args.getSize() < 2) {
errorAny(sender, "Usage: /clonedupecore trim [material] [armortype]");
return;
}
if (!(sender instanceof Player p)) {
errorAny(sender,"Only players can use this sub-command.");
return;
}
ValidMaterial validMaterial = main.randomizer().getRandomElement(ValidMaterial.values());
ValidArmorType validArmorType = main.randomizer().getRandomElement(ValidArmorType.values());
if (args.getSize() >= 2) validMaterial = args.get(1).toEnum(ValidMaterial.class);
if (args.getSize() >= 3) validArmorType = args.get(2).toEnum(ValidArmorType.class);
TrimPattern pattern = TrimPattern.SILENCE;
TrimMaterial material = validMaterial.getCanonical();
if (material == null) material = main.randomizer().getRandomElement(ValidMaterial.values()).getCanonical();
p.getEquipment().setHelmet(createTestArmor(new ItemStack(validArmorType.getHelmet()),pattern,material));
p.getEquipment().setChestplate(createTestArmor(new ItemStack(validArmorType.getChestplate()),pattern,material));
p.getEquipment().setLeggings(createTestArmor(new ItemStack(validArmorType.getLeggings()),pattern,material));
p.getEquipment().setBoots(createTestArmor(new ItemStack(validArmorType.getBoots()),pattern,material));
for (int i = 0; i < 10; i++) {
double finalI = i;
Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
DisplayUtils.ring(p.getLocation().clone().add(0, finalI / 5D,0),0.5, Color.RED,0.5F);
},i);
}
for (int i = 0; i < 10; i++) {
double finalI = i;
Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
DisplayUtils.ring(p.getLocation().clone().add(0,2,0).subtract(0,finalI / 5D,0),0.5,Color.RED,0.5F);
},10 + i);
}
new SoundPlayer(Sound.BLOCK_BEACON_ACTIVATE).playTo(p);
infoAny(sender,"You now are wearing {0} with {1} {2} trim.", "Netherite",FormatUtils.formatEnum(validMaterial),"Silence");
}
private ItemStack createTestArmor(ItemStack piece, TrimPattern trimPattern, TrimMaterial trimMaterial) {
ItemMeta meta = piece.getItemMeta();
if (!(meta instanceof ArmorMeta armor)) {
throw new IllegalArgumentException("You must input armor ONLY");
}
armor.displayName(Text.color("&eTesting Armor").decoration(TextDecoration.ITALIC,false));
armor.lore(List.of(
Text.color("&8&l| &7%s".formatted(FormatUtils.formatEnum(ValidMaterial.validate(trimMaterial)))).decoration(TextDecoration.ITALIC,false),
Text.color("&8&l| &7Won't Break").decoration(TextDecoration.ITALIC,false),
Text.color("&8&l| &7Vanishes on death").decoration(TextDecoration.ITALIC,false),
Text.color("&8&l| &7This armor is for testing purposes &c&lONLY&7!").decoration(TextDecoration.ITALIC,false)
));
armor.addEnchant(Enchantment.VANISHING_CURSE,1,true);
armor.addEnchant(Enchantment.PROTECTION, 4, true);
armor.setUnbreakable(true);
armor.addItemFlags(ItemFlag.HIDE_ENCHANTS);
armor.addItemFlags(ItemFlag.HIDE_UNBREAKABLE);
armor.addItemFlags(ItemFlag.HIDE_ARMOR_TRIM);
armor.setTrim(new ArmorTrim(trimMaterial,trimPattern));
piece.setItemMeta(armor);
return piece;
}
private void handleDebug(CommandSender sender, Args args) {
if (args.getSize() < 2) {
errorAny(sender, "Usage: /clonedupecore debug <toggle|include|exclude>");
return;
}
final String sub = args.get(1).toString();
switch (sub) {
case "toggle" -> {
boolean result = false;
getConfig().debugMode = result = !getConfig().debugMode;
getConfig().save();
CloneDupeCore.getInstance().getManager().common.setDebugMode(result);
successAny(sender,"Toggled debug mode {0}.",result ? "on" : "off");
}
case "exclude" -> {
if (args.getSize() < 3) {
errorAny(sender, "Usage: /clonedupecore debug exclude <method>");
return;
}
final String exclusion = args.get(2).toString();
getConfig().debuggerExclusions.add(exclusion);
getConfig().save();
CloneDupeCore.getInstance().getManager().common.addDebuggerExclusion(exclusion);
successAny(sender, "Excluded {0} from the debugger.", exclusion);
}
case "include" -> {
if (args.getSize() < 3) {
errorAny(sender, "Usage: /clonedupecore debug include <method>");
return;
}
final String exclusion = args.get(2).toString();
getConfig().debuggerExclusions.remove(exclusion);
getConfig().save();
CloneDupeCore.getInstance().getManager().common.removeDebuggerExclusion(exclusion);
successAny(sender, "Removed exclusion for {0} on the debugger.", exclusion);
}
}
}
}

View File

@@ -0,0 +1,198 @@
package me.trouper.clonedupecore.server.commands;
import club.minnced.discord.webhook.WebhookClient;
import club.minnced.discord.webhook.WebhookClientBuilder;
import club.minnced.discord.webhook.send.WebhookEmbed;
import club.minnced.discord.webhook.send.WebhookEmbedBuilder;
import me.trouper.alias.server.commands.Args;
import me.trouper.alias.server.commands.CommandRegistry;
import me.trouper.alias.server.commands.Permission;
import me.trouper.alias.server.commands.QuickCommand;
import me.trouper.alias.server.commands.completions.CompletionBuilder;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.utils.misc.TimeUtils;
import me.trouper.clonedupecore.data.Data;
import me.trouper.clonedupecore.server.punishment.LiteBansManager;
import me.trouper.clonedupecore.server.punishment.WrappedEntry;
import me.trouper.clonedupecore.server.punishment.animations.*;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.List;
@CommandRegistry(
value = "offend",
usage = "/offend <player> <query|punish> [template]",
permission = @Permission("clonedupe.offend"),
printStackTrace = true
)
public class OffendCommand implements QuickCommand, Data {
private final LiteBansManager liteBansManager = new LiteBansManager();
@Override
public void handleCommand(CommandSender sender, Command command, String label, Args args) {
if (args.getSize() < 2) {
errorAny(sender, "Incomplete command. Correct usage: ", getRegistry().usage());
return;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args.get(0).toString());
if (target == null || !target.hasPlayedBefore()) {
errorAny(sender, "That player has never joined before.");
return;
}
String sub = args.get(1).toString();
String template = args.getSize() >= 3 ? args.get(2).toString() : null;
if ("query".equals(sub)) {
Bukkit.getScheduler().runTaskAsynchronously(main.getPlugin(), ()->{
handleQuery(sender, target, template);
});
} else if ("punish".equals(sub)) {
if (template == null) {
errorAny(sender, "You must specify a ban template!");
return;
}
if (!getConfig().banTemplates.contains(template)) {
errorAny(sender, "That template is invalid! Available templates: " +
String.join(", ", getConfig().banTemplates));
return;
}
Bukkit.getScheduler().runTaskAsynchronously(main.getPlugin(), ()->{
handlePunish(sender, target, template);
});
}
}
@Override
public void handleCompletion(CommandSender commandSender, Command command, String label, Args args, CompletionBuilder b) {
b.then(
b.argOnlinePlayers()
.then(
b.arg("query", "punish")
.then(
b.arg(getConfig().getBanTemplateNames())
)
)
);
}
private void handleQuery(CommandSender sender, OfflinePlayer target, String template) {
if (template == null) {
List<WrappedEntry> allBans = liteBansManager.getPlayerBans(target);
Component historyMessage = Text.format(Text.Pallet.INFO, "All bans for {0}: ", target.getName())
.appendNewline();
if (allBans.isEmpty()) {
historyMessage = historyMessage.append(Text.format(Text.Pallet.LOCATION, "No bans found."));
} else {
for (WrappedEntry ban : allBans) {
String status = ban.isActive() ? "Active" : "Expired/Unbanned";
String duration = ban.isPermanent() ? "Permanent" : TimeUtils.formatTime(ban.getDateEnd());
historyMessage = historyMessage
.append(Text.format(Text.Pallet.LOCATION,
"{0} - {1} - {2} - {3}",
TimeUtils.formatTime(ban.getDateStart()),
ban.getReason(),
duration,
status))
.appendNewline();
}
}
sender.sendMessage(historyMessage);
return;
}
short index = (short) getConfig().banTemplates.indexOf(template);
List<WrappedEntry> templateBans = liteBansManager.getPlayerBansByTemplate(target, index);
Component historyMessage = Text.format(Text.Pallet.INFO,
"{0} template bans for {1}: ", template, target.getName()).appendNewline();
if (templateBans.isEmpty()) {
historyMessage = historyMessage.append(Text.format(Text.Pallet.LOCATION, "No bans found for this template."));
} else {
for (WrappedEntry ban : templateBans) {
String status = ban.isActive() ? "Active" : "Expired/Unbanned";
String duration = ban.isPermanent() ? "Permanent" : TimeUtils.formatTime(ban.getDateEnd());
historyMessage = historyMessage
.append(Text.format(Text.Pallet.LOCATION,
"{0} - {1} - {2}",
TimeUtils.formatTime(ban.getDateStart()),
duration,
status))
.appendNewline();
}
}
sender.sendMessage(historyMessage);
}
private void handlePunish(CommandSender sender, OfflinePlayer target, String template) {
if (liteBansManager.isPlayerBanned(target)) {
WrappedEntry activeBan = liteBansManager.getActiveBan(target);
if (activeBan != null) {
sender.sendMessage(Text.format(Text.Pallet.ERROR,
"{0} is already banned for: {1}", target.getName(), activeBan.getReason()));
return;
}
}
short index = (short) getConfig().banTemplates.indexOf(template);
int currentBans = liteBansManager.getBanCountByTemplate(target, index);
sender.sendMessage(Text.format(Text.Pallet.INFO,
"Executing ban for {0} using template {1} (Previous offenses: {2})",
target.getName(), template, currentBans));
Runnable banPlayer = () -> {
Verbose.send("Executing ban due to animation finishing.");
liteBansManager.executeBan(target, template, sender);
WebhookClientBuilder builder = new WebhookClientBuilder(getConfig().banWebhook);
WebhookEmbedBuilder embedBuilder = new WebhookEmbedBuilder();
embedBuilder.setAuthor(new WebhookEmbed.EmbedAuthor("New Ban","https://www.pngmart.com/files/23/Ban-Hammer-PNG-File-200x200.png","https://clonedupe.fun"));
embedBuilder.setTitle(new WebhookEmbed.EmbedTitle("Template: %s".formatted(template), null));
embedBuilder.addField(new WebhookEmbed.EmbedField(true,"Moderator",sender.getName()));
embedBuilder.addField(new WebhookEmbed.EmbedField(true,"Offender",target.getName() + " "));
try (WebhookClient c = builder.build()) {
c.send(embedBuilder.build());
}
sender.sendMessage(Text.format(Text.Pallet.SUCCESS, "Successfully banned {0} using template {1}", target.getName(), template));
};
Verbose.send("Banning with animation...");
PunishmentAnimation animation = main.randomizer().getRandomElement(ANIMATION_FACTORIES).create(main.getPlugin(), target.getPlayer(), banPlayer);
try {
animation.run();
} catch (Exception e) {
Verbose.send("Animation generated an exception before it could run! Banning player...");
e.printStackTrace();
banPlayer.run();
}
}
private static final List<PunishmentAnimation.AnimationFactory> ANIMATION_FACTORIES = Arrays.asList(
MatrixAnimation::new,
AnvilAnimation::new,
GwenAnimation::new,
LaserAnimation::new,
LightningAnimation::new
);
}

View File

@@ -0,0 +1,70 @@
package me.trouper.clonedupecore.server.commands;
import me.trouper.alias.server.commands.Args;
import me.trouper.alias.server.commands.CommandRegistry;
import me.trouper.alias.server.commands.Permission;
import me.trouper.alias.server.commands.QuickCommand;
import me.trouper.alias.server.commands.completions.CompletionBuilder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
@CommandRegistry(value = "ping", permission = @Permission("clonedupe.ping"),printStackTrace = true)
public class PingCommand implements QuickCommand {
@Override
public void handleCommand(CommandSender sender, Command command, String label, Args args) {
if (args.isEmpty() && sender instanceof Player target) {
displayPing(sender,target);
return;
} else if (sender instanceof ConsoleCommandSender) {
success(sender,Component.text("The console will always have {0} ping."),Component.text(0));
return;
}
String argument = args.get(0).toString();
Player target = Bukkit.getPlayer(argument);
if (target == null) {
error(sender,Component.text("Could not find {0} online."),Component.text(argument));
return;
}
displayPing(sender,target);
}
@Override
public void handleCompletion(CommandSender commandSender, Command command, String s, Args args, CompletionBuilder b) {
b.then(
b.argOnlinePlayers()
);
}
private void displayPing(CommandSender sender, Player target) {
final int ping = target.getPing();
if (sender instanceof Player player && player.equals(target)) {
target.sendActionBar(
Component.text("Your ping is", TextColor.color(0xFFAAAA))
.append(Component.text(": ", NamedTextColor.WHITE))
.append(Component.text(ping,NamedTextColor.AQUA))
.append(Component.text("ms",TextColor.color(0xFFDDDD)))
);
success(sender,Component.text("You have {0}ms of latency to the server."),Component.text(ping));
return;
}
if (sender instanceof Player player) {
player.sendActionBar(
Component.text("Server <-> %s ping".formatted(target.getName()), TextColor.color(0xFFAAAA))
.append(Component.text(": ", NamedTextColor.WHITE))
.append(Component.text(ping,NamedTextColor.AQUA))
.append(Component.text("ms",TextColor.color(0xFFDDDD)))
);
}
success(sender,Component.text("{0} has {1}ms of latency to the server."),target.name(),Component.text(ping));
}
}

View File

@@ -0,0 +1,47 @@
package me.trouper.clonedupecore.server.commands;
import me.trouper.alias.server.commands.Args;
import me.trouper.alias.server.commands.CommandRegistry;
import me.trouper.alias.server.commands.QuickCommand;
import me.trouper.alias.server.commands.completions.CompletionBuilder;
import me.trouper.clonedupecore.data.Data;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@CommandRegistry(
value = "trimeffect", usage = "/trimeffect <global|self>",
consoleAllowed = false,
blocksAllowed = false
)
public class TrimEffectCommand implements QuickCommand, Data {
@Override
public void handleCommand(CommandSender commandSender, Command command, String s, Args args) {
Player p = (Player) commandSender;
if (args.getSize() != 1) {
errorAny(commandSender,"Correct Usage: ", getRegistry().usage());
return;
}
if ("global".equals(args.get(0).toString())) {
if (getStorage().disabledGlobalParticles.add(p.getUniqueId().toString())) {
successAny(commandSender,"Disabled global Trim effects.");
} else {
getStorage().disabledGlobalParticles.remove(p.getUniqueId().toString());
successAny(commandSender,"Enabled global Trim effects.");
}
} else {
if (getStorage().disabledOwnParticles.add(p.getUniqueId().toString())) {
successAny(commandSender,"Disabled your own Trim effects.");
} else {
getStorage().disabledOwnParticles.remove(p.getUniqueId().toString());
successAny(commandSender,"Enabled your own Trim effects.");
}
}
}
@Override
public void handleCompletion(CommandSender commandSender, Command command, String s, Args args, CompletionBuilder b) {
b.then(b.arg("global","self"));
}
}

View File

@@ -0,0 +1,137 @@
package me.trouper.clonedupecore.server.commands;
import me.trouper.alias.server.commands.Args;
import me.trouper.alias.server.commands.CommandRegistry;
import me.trouper.alias.server.commands.Permission;
import me.trouper.alias.server.commands.QuickCommand;
import me.trouper.alias.server.commands.completions.CompletionBuilder;
import me.trouper.alias.server.systems.AbstractWand;
import me.trouper.alias.server.systems.Text;
import me.trouper.clonedupecore.server.trolls.*;
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.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@CommandRegistry(value = "clonetroll",permission = @Permission("clonedupe.troll"),printStackTrace = true)
public class TrollCommand implements QuickCommand {
public final List<TrollFeature> trollRegistry = new ArrayList<>();
public TrollCommand() {
trollRegistry.addAll(List.of(
new DragTrollWand(),
new FlickerTroll(),
new LiarTroll(),
new LSDTroll(),
new MatrixTroll(),
new VoidTrollWand(),
new OrbitalTrollWand(),
new CameraTroll(),
new TestTrollWand()
));
}
@Override
public void handleCommand(CommandSender sender, Command command, String label, Args args) {
Component helpMessage = Component.empty()
.append(Component.text("CloneDupe Troll Guide", NamedTextColor.RED).decorate(TextDecoration.BOLD)).appendNewline()
.append(Component.text("""
- These features can be used to mess with players -
SAFE trolls are purely packet based, and relogging will clear all their effects.
HARMLESS trolls are safe but not packet based. Relogging or rebooting may not clear all effects.
DAMAGING trolls may deal damage to the player's health and armor.
DESTRUCTIVE trolls may break blocks in the world, delete player items, or crash games. Some may be irreversible, so handle them carefully.
Use the *stop* argument to end or cancel a troll.
""",NamedTextColor.GRAY)).appendNewline()
.append(Component.text("Syntax",NamedTextColor.GOLD)
.append(Component.text(": ",NamedTextColor.WHITE)
.append(Component.text("/clonetroll <feature> [player] [stop]",NamedTextColor.GRAY)))).appendNewline()
.append(Component.text("Features",NamedTextColor.GOLD)
.append(Component.text(":",NamedTextColor.WHITE))).appendNewline();
for (TrollFeature troll : trollRegistry) {
helpMessage = helpMessage.append(Text.format(Text.Pallet.NEUTRAL,"[{0}] - {1}: {2}",troll.getRating(),troll.getName(),troll.getDescription())).appendNewline();
}
if (args.getSize() < 1 || (args.getSize() == 1 && Objects.equals("info",args.get(0).toString()))) {
sender.sendMessage(helpMessage);
return;
}
String choice = args.get(0).toString();
TrollFeature troll = null;
for (TrollFeature abstractTroll : trollRegistry) {
if (!abstractTroll.getName().equals(choice)) continue;
troll = abstractTroll;
break;
}
if (troll == null) {
errorAny(sender,"You must pick a valid troll. {0} does not exist!",choice);
return;
}
if (troll instanceof AbstractWand wand) {
troll.execute(sender,null);
if (!(sender instanceof Player p)) {
warningAny(sender,"Wands can only be given to a player.");
return;
}
if (!p.hasPermission(wand.getUsePermission())) {
warningAny(sender,"You lack the permission to use this wand.");
return;
}
if (!p.getInventory().addItem(wand.getWandItem().clone()).isEmpty()) {
errorAny(p, "Your inventory is full!");
} else {
success(p, Component.text("You have received a {0}"), wand.getWandItem().displayName());
}
return;
}
String target = args.get(1).toString();
Player victim = Bukkit.getPlayer(target);
if (victim == null || !victim.isOnline()) {
errorAny(sender,"You must pick an online player. {0} is not online!",target);
return;
}
if (args.getSize() >= 3 && Objects.equals("stop",args.get(2).toString())) {
troll.stop(sender,victim);
return;
}
troll.execute(sender,victim);
}
@Override
public void handleCompletion(CommandSender sender, Command command, String label, Args args, CompletionBuilder b) {
List<String> extras = new ArrayList<>();
trollRegistry.forEach(extra -> extras.add(extra.getName()));
b.then(
b.arg(extras)
.then(
b.argOnlinePlayers()
.then(
b.arg("stop")
)
)
).then(
b.arg("info")
);
}
}

View File

@@ -0,0 +1,35 @@
package me.trouper.clonedupecore.server.events;
import me.trouper.alias.server.events.QuickListener;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class CreativeHotbarEvent implements QuickListener {
@EventHandler
private void onNBT(InventoryCreativeEvent e) {
if (e.getCursor().getItemMeta() == null) return;
if (!(e.getWhoClicked() instanceof Player p)) return;
if (e.getCursor() == null) return;
ItemStack i = e.getCursor();
if (i.getItemMeta() == null) return;
if (!i.hasItemMeta()) return;
if (CloneDupeCore.getInstance().getManager().io.config.nbtWhitelist.contains(p.getUniqueId().toString())) return;
if (new ItemCheck().passes(i)) return;
e.setCancelled(true);
ItemStack replacement = new ItemStack(i.getType());
ItemMeta meta = i.getItemMeta();
e.setCurrentItem(replacement);
e.getCursor().setItemMeta(meta);
}
}

View File

@@ -0,0 +1,18 @@
package me.trouper.clonedupecore.server.events;
import me.trouper.alias.server.events.QuickListener;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
public class GamemodeEvent implements QuickListener {
@EventHandler
private void onJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
if (p.isOp()) return;
p.setGameMode(GameMode.SURVIVAL);
}
}

View File

@@ -0,0 +1,9 @@
package me.trouper.clonedupecore.server.events.hotbar;
import me.trouper.clonedupecore.data.Data;
import me.trouper.clonedupecore.data.io.NBTConfig;
public abstract class AbstractCheck<T> implements Data {
public NBTConfig config = getNBT();
public abstract boolean passes(T input);
}

View File

@@ -0,0 +1,76 @@
package me.trouper.clonedupecore.server.events.hotbar.entities;
import de.tr7zw.nbtapi.NBT;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.utils.InventoryUtils;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import me.trouper.clonedupecore.server.events.hotbar.misc.InventoryCheck;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Villager;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.MerchantRecipe;
import java.util.concurrent.atomic.AtomicBoolean;
public class EntityCheck extends AbstractCheck<Entity> {
@Override
public boolean passes(Entity entity) {
if (entity instanceof Item itemEntity) {
if (!new ItemCheck().passes(itemEntity.getItemStack())) {
Verbose.send("Entity failed check: Item not allowed.");
return false;
}
}
Inventory inv = InventoryUtils.getInventory(entity);
if (inv != null && !new InventoryCheck().passes(inv)) {
Verbose.send("Entity inventory failed check.");
return false;
}
if (entity instanceof Villager villager) {
for (MerchantRecipe recipe : villager.getRecipes()) {
if (!new ItemCheck().passes(recipe.getResult())) {
Verbose.send("Villager recipe failed check.");
return false;
}
}
}
if (entity instanceof Mob mob) {
if (!new EquipmentCheck().passes(mob)) {
Verbose.send("Mob equipment failed check.");
return false;
}
}
if (!entity.getPassengers().isEmpty()) {
if (!config.allowRecursion) {
Verbose.send("Entity recursion not allowed.");
return false;
}
for (Entity passenger : entity.getPassengers()) {
if (!passes(passenger)) {
Verbose.send("Entity passenger failed check.");
return false;
}
}
}
AtomicBoolean failsTiming = new AtomicBoolean(false);
NBT.get(entity, nbt -> {
if (nbt.hasTag("DeathTime") && nbt.getInteger("DeathTime") < 1) {
Verbose.send("Entity death time check failed.");
failsTiming.set(true);
}
if (nbt.hasTag("HurtTime") && nbt.getInteger("HurtTime") < 1) {
Verbose.send("Entity hurt time check failed.");
failsTiming.set(true);
}
});
if (failsTiming.get()) {
Verbose.send("Entity timing check failed.");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,21 @@
package me.trouper.clonedupecore.server.events.hotbar.entities;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntitySnapshot;
public class EntitySnapshotCheck extends AbstractCheck<EntitySnapshot> {
@Override
public boolean passes(EntitySnapshot input) {
Location loc = new Location(Bukkit.getWorlds().getFirst(), 0, 1000000, 0);
Entity temp = input.createEntity(loc);
boolean result = new EntityCheck().passes(temp);
Verbose.send("Temp Entity %s Entity Check", result ? "failed" : "passed");
temp.remove();
return result;
}
}

View File

@@ -0,0 +1,25 @@
package me.trouper.clonedupecore.server.events.hotbar.entities;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import org.bukkit.entity.Mob;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
public class EquipmentCheck extends AbstractCheck<Mob> {
@Override
public boolean passes(Mob mob) {
Verbose.send("Running mob check.");
for (EquipmentSlot slot : EquipmentSlot.values()) {
if (mob.getEquipment().getItem(slot).isEmpty()) continue;
ItemStack item = mob.getEquipment().getItem(slot);
if (!new ItemCheck().passes(item)) {
Verbose.send("Equipment slot did not pass.");
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,129 @@
package me.trouper.clonedupecore.server.events.hotbar.items;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Map;
import static org.bukkit.enchantments.Enchantment.MENDING;
public class EnchantmentCheck extends AbstractCheck<ItemStack> {
@Override
public boolean passes(ItemStack input) {
return !hasIllegalEnchants(input);
}
public boolean hasIllegalEnchants(ItemStack item) {
Verbose.send("Checking item for illegal enchants: ", item.getType().name());
if (item.hasItemMeta() && item.getItemMeta().hasEnchants()) {
ItemMeta meta = item.getItemMeta();
Map<Enchantment, Integer> enchantments = meta.getEnchants();
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
Enchantment enchantment = entry.getKey();
int level = entry.getValue();
if (level > getNBT().globalMaxEnchant || isOverLimit(enchantment, level)) {
return true;
}
}
}
return false;
}
public boolean isOverLimit(Enchantment enchantment, int level) {
int maxLevel = getNBT().globalMaxEnchant;
if (enchantment.equals(MENDING)) {
maxLevel = getNBT().maxMending;
} else if (enchantment.equals(Enchantment.UNBREAKING)) {
maxLevel = getNBT().maxUnbreaking;
} else if (enchantment.equals(Enchantment.VANISHING_CURSE)) {
maxLevel = getNBT().maxCurseOfVanishing;
} else if (enchantment.equals(Enchantment.BINDING_CURSE)) {
maxLevel = getNBT().maxCurseOfBinding;
} else if (enchantment.equals(Enchantment.AQUA_AFFINITY)) {
maxLevel = getNBT().maxAquaAffinity;
} else if (enchantment.equals(Enchantment.PROTECTION)) {
maxLevel = getNBT().maxProtection;
} else if (enchantment.equals(Enchantment.BLAST_PROTECTION)) {
maxLevel = getNBT().maxBlastProtection;
} else if (enchantment.equals(Enchantment.DEPTH_STRIDER)) {
maxLevel = getNBT().maxDepthStrider;
} else if (enchantment.equals(Enchantment.FEATHER_FALLING)) {
maxLevel = getNBT().maxFeatherFalling;
} else if (enchantment.equals(Enchantment.FIRE_PROTECTION)) {
maxLevel = getNBT().maxFireProtection;
} else if (enchantment.equals(Enchantment.FROST_WALKER)) {
maxLevel = getNBT().maxFrostWalker;
} else if (enchantment.equals(Enchantment.PROJECTILE_PROTECTION)) {
maxLevel = getNBT().maxProjectileProtection;
} else if (enchantment.equals(Enchantment.RESPIRATION)) {
maxLevel = getNBT().maxRespiration;
} else if (enchantment.equals(Enchantment.SOUL_SPEED)) {
maxLevel = getNBT().maxSoulSpeed;
} else if (enchantment.equals(Enchantment.THORNS)) {
maxLevel = getNBT().maxThorns;
} else if (enchantment.equals(Enchantment.SWEEPING_EDGE)) {
maxLevel = getNBT().maxSweepingEdge;
} else if (enchantment.equals(Enchantment.SWIFT_SNEAK)) {
maxLevel = getNBT().maxSwiftSneak;
} else if (enchantment.equals(Enchantment.BANE_OF_ARTHROPODS)) {
maxLevel = getNBT().maxBaneOfArthropods;
} else if (enchantment.equals(Enchantment.FIRE_ASPECT)) {
maxLevel = getNBT().maxFireAspect;
} else if (enchantment.equals(Enchantment.LOOTING)) {
maxLevel = getNBT().maxLooting;
} else if (enchantment.equals(Enchantment.IMPALING)) {
maxLevel = getNBT().maxImpaling;
} else if (enchantment.equals(Enchantment.KNOCKBACK)) {
maxLevel = getNBT().maxKnockback;
} else if (enchantment.equals(Enchantment.SHARPNESS)) {
maxLevel = getNBT().maxSharpness;
} else if (enchantment.equals(Enchantment.SMITE)) {
maxLevel = getNBT().maxSmite;
} else if (enchantment.equals(Enchantment.CHANNELING)) {
maxLevel = getNBT().maxChanneling;
} else if (enchantment.equals(Enchantment.FLAME)) {
maxLevel = getNBT().maxFlame;
} else if (enchantment.equals(Enchantment.INFINITY)) {
maxLevel = getNBT().maxInfinity;
} else if (enchantment.equals(Enchantment.LOYALTY)) {
maxLevel = getNBT().maxLoyalty;
} else if (enchantment.equals(Enchantment.RIPTIDE)) {
maxLevel = getNBT().maxRiptide;
} else if (enchantment.equals(Enchantment.MULTISHOT)) {
maxLevel = getNBT().maxMultishot;
} else if (enchantment.equals(Enchantment.PIERCING)) {
maxLevel = getNBT().maxPiercing;
} else if (enchantment.equals(Enchantment.POWER)) {
maxLevel = getNBT().maxPower;
} else if (enchantment.equals(Enchantment.PUNCH)) {
maxLevel = getNBT().maxPunch;
} else if (enchantment.equals(Enchantment.QUICK_CHARGE)) {
maxLevel = getNBT().maxQuickCharge;
} else if (enchantment.equals(Enchantment.EFFICIENCY)) {
maxLevel = getNBT().maxEfficiency;
} else if (enchantment.equals(Enchantment.FORTUNE)) {
maxLevel = getNBT().maxFortune;
} else if (enchantment.equals(Enchantment.LUCK_OF_THE_SEA)) {
maxLevel = getNBT().maxLuckOfTheSea;
} else if (enchantment.equals(Enchantment.LURE)) {
maxLevel = getNBT().maxLure;
} else if (enchantment.equals(Enchantment.SILK_TOUCH)) {
maxLevel = getNBT().maxSilkTouch;
} else if (enchantment.equals(Enchantment.BREACH)) {
maxLevel = getNBT().maxBreach;
} else if (enchantment.equals(Enchantment.DENSITY)) {
maxLevel = getNBT().maxDensity;
} else if (enchantment.equals(Enchantment.WIND_BURST)) {
maxLevel = getNBT().maxWindBurst;
}
return level > maxLevel;
}
}

View File

@@ -0,0 +1,80 @@
package me.trouper.clonedupecore.server.events.hotbar.items;
import de.tr7zw.nbtapi.NBT;
import de.tr7zw.nbtapi.iface.ReadWriteNBT;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.utils.InventoryUtils;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.misc.BlockStateCheck;
import me.trouper.clonedupecore.server.events.hotbar.misc.InventoryCheck;
import me.trouper.clonedupecore.server.events.hotbar.nbt.ComponentCheck;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
public class ItemCheck extends AbstractCheck<ItemStack> {
@Override
public boolean passes(ItemStack item) {
try {
return scan(item);
} catch (Exception ex) {
CloneDupeCore.getInstance().getLogger().warning("Caught an exception while handling an item check: " + Arrays.toString(ex.getStackTrace()));
return false;
}
}
private boolean scan(ItemStack item) {
Verbose.send("Checking item: " + item.getType().name());
// No metadata? Nothing to check.
if (item.getItemMeta() == null) {
Verbose.send("Item passes because it has no metadata.");
return true;
}
if (!new MetaCheck().passes(item)) {
Verbose.send("Item failed metadata check.");
return false;
}
// NBT-based checks
ReadWriteNBT nbt = NBT.itemStackToNBT(item);
ReadWriteNBT components = nbt.getCompound("components");
if (components != null) {
if (!new ComponentCheck().passes(components)) {
Verbose.send("Components check failed.");
return false;
}
}
// Spawn egg checks.
if (!new SpawnEggCheck().passes(item)) {
Verbose.send("Spawn egg check failed.");
return false;
}
if (!new BlockStateCheck().passes(item)) {
Verbose.send("Block State check failed.");
return false;
}
// Check for an inventory inside the item.
Inventory inv = InventoryUtils.getInventory(item);
if (inv != null) {
Verbose.send("Item contains an inventory: " + inv);
if (!new InventoryCheck().passes(inv)) {
Verbose.send("Item failed inventory check.");
return false;
}
}
Verbose.send("Item passed all checks.");
return true;
}
}

View File

@@ -0,0 +1,55 @@
package me.trouper.clonedupecore.server.events.hotbar.items;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.BundleMeta;
import org.bukkit.inventory.meta.ItemMeta;
public class MetaCheck extends AbstractCheck<ItemStack> {
@Override
public boolean passes(ItemStack item) {
ItemMeta meta = item.getItemMeta();
// Name, lore, potion, attribute and enchantment checks.
if (!config.allowName && meta.hasDisplayName()) {
Verbose.send("Custom names not allowed.");
return false;
}
if (!config.allowLore && meta.hasLore()) {
Verbose.send("Custom lore not allowed.");
return false;
}
if (!config.allowBooks && meta instanceof BookMeta) {
Verbose.send("Item failed book check.");
return false;
}
if (!config.allowPotions &&
(item.getType().equals(Material.POTION) ||
item.getType().equals(Material.SPLASH_POTION) ||
item.getType().equals(Material.LINGERING_POTION))) {
Verbose.send("Potions not allowed.");
return false;
}
if (!config.allowAttributes && meta.hasAttributeModifiers()) {
Verbose.send("Attribute modifiers not allowed.");
return false;
}
if (config.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) {
Verbose.send("Illegal enchantments found.");
return false;
}
// Bundle check recursively check the contained items.
if (item.getType().name().contains("_BUNDLE") && meta instanceof BundleMeta bm) {
for (ItemStack bundleItem : bm.getItems()) {
if (!passes(bundleItem)) return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,79 @@
package me.trouper.clonedupecore.server.events.hotbar.items;
import de.tr7zw.nbtapi.NBT;
import kotlin.Pair;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.CloneDupeCore;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class RateLimitCheck extends AbstractCheck<Pair<Player,ItemStack>> {
public static Map<UUID, Integer> dataUsed = new HashMap<>();
public static Map<UUID, Integer> itemsUsed = new HashMap<>();
@Override
public boolean passes(Pair<Player,ItemStack> input) {
Player player = input.getFirst();
UUID uuid = player.getUniqueId();
ItemStack item = input.getSecond();
return itemLimit(uuid,item) && dataLimit(uuid,item);
}
private boolean itemLimit(UUID uuid, ItemStack item) {
int currentUsed = itemsUsed.getOrDefault(uuid,0);
Verbose.send("Current Player used items: " + currentUsed);
currentUsed++;
itemsUsed.put(uuid,currentUsed);
return currentUsed <= config.rateLimit.rateLimitItems;
}
private boolean dataLimit(UUID uuid, ItemStack item) {
int currentData = dataUsed.getOrDefault(uuid,0);
Verbose.send("Current Player used data: " + currentData);
try {
int itemData = NBT.readNbt(item).toString().length();
Verbose.send("Item data: " + itemData);
if (currentData < config.rateLimit.maxOverhead) currentData += itemData;
} catch (Exception e) {
CloneDupeCore.getInstance().getLogger().warning("Could not determine size of item. Blocking.");
CloneDupeCore.getInstance().getLogger().warning(Arrays.toString(e.getStackTrace()));
return false;
}
dataUsed.put(uuid,currentData);
Verbose.send("New Player used data: " + currentData);
return currentData <= config.rateLimit.rateLimitBytes;
}
public void decayData() {
for (UUID uuid : dataUsed.keySet()) {
int currentData = dataUsed.get(uuid);
if (currentData > 0) {
currentData -= config.rateLimit.byteDecay;
dataUsed.put(uuid, Math.max(0, currentData));
}
}
}
public void decayItems() {
for (UUID uuid : itemsUsed.keySet()) {
int currentItems = itemsUsed.get(uuid);
if (currentItems > 0) {
currentItems -= config.rateLimit.itemDecay;
itemsUsed.put(uuid, Math.max(0, currentItems));
}
}
}
}

View File

@@ -0,0 +1,50 @@
package me.trouper.clonedupecore.server.events.hotbar.items;
import de.tr7zw.nbtapi.NBT;
import de.tr7zw.nbtapi.iface.ReadWriteNBT;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.entities.EntitySnapshotCheck;
import me.trouper.clonedupecore.server.events.hotbar.nbt.EntityDataCheck;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SpawnEggMeta;
public class SpawnEggCheck extends AbstractCheck<ItemStack> {
@Override
public boolean passes(ItemStack item) {
Verbose.send("Running spawn egg checks on item: ",item.getType().name());
if (!item.getType().name().toLowerCase().contains("spawn_egg")) return true;
if (!SpawnEggCheck.entityMatches(item)) {
Verbose.send("Spawn egg entity doesn't match item type.");
return false;
}
ReadWriteNBT nbt = NBT.itemStackToNBT(item);
ReadWriteNBT components = nbt.getCompound("components");
if (components != null) {
var entityData = components.getCompound("minecraft:entity_data");
if (!new EntityDataCheck().passes(entityData)) {
Verbose.send("Spawn egg entity data check failed.");
return false;
}
}
if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) {
if (sem.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(sem.getSpawnedEntity())) {
Verbose.send("Spawn egg entity snapshot check failed.");
return false;
}
}
return true;
}
public static boolean entityMatches(ItemStack item) {
if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) {
String eggEntityName = item.getType().name().replace("_SPAWN_EGG", "");
return sem.getSpawnedEntity() != null &&
sem.getSpawnedEntity().getEntityType().name().equals(eggEntityName);
}
return false;
}
}

View File

@@ -0,0 +1,100 @@
package me.trouper.clonedupecore.server.events.hotbar.misc;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.entities.EntitySnapshotCheck;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import org.bukkit.Material;
import org.bukkit.block.*;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.spawner.TrialSpawnerConfiguration;
public class BlockStateCheck extends AbstractCheck<ItemStack> {
@Override
public boolean passes(ItemStack item) {
ItemMeta meta = item.getItemMeta();
if (!(meta instanceof BlockStateMeta blockStateMeta)) {
Verbose.send("Item passes due to not being a block state meta");
return true;
}
if (item.getType().name().contains("CAMPFIRE") ) {
BlockState bs = blockStateMeta.getBlockState();
if (bs instanceof Campfire campfire) {
for (int slot = 0; slot < 4; slot++) {
ItemStack campfireItem = campfire.getItem(slot);
if (campfireItem != null && !new ItemCheck().passes(campfireItem)) {
Verbose.send("Campfire item failed check.");
return false;
}
}
}
}
// Lectern and Chiseled Bookshelf check (by validating their inventories).
if (item.getType().equals(Material.LECTERN)) {
BlockState bs = blockStateMeta.getBlockState();
if (bs instanceof Lectern lectern) {
if (!new InventoryCheck().passes(lectern.getInventory())) {
Verbose.send("Lectern inventory failed check.");
return false;
}
}
}
if (item.getType().equals(Material.CHISELED_BOOKSHELF)) {
BlockState bs = blockStateMeta.getBlockState();
if (bs instanceof ChiseledBookshelf bookshelf) {
if (!new InventoryCheck().passes(bookshelf.getInventory())) {
Verbose.send("Chiseled bookshelf inventory failed check.");
return false;
}
}
}
// Spawner check.
if (item.getType().equals(Material.SPAWNER)) {
BlockState bs = blockStateMeta.getBlockState();
if (bs instanceof CreatureSpawner spawner) {
if (spawner.getSpawnedEntity() != null) {
if (spawner.getSpawnedEntity().getEntityType().equals(EntityType.FALLING_BLOCK) ||
spawner.getSpawnedEntity().getEntityType().equals(EntityType.COMMAND_BLOCK_MINECART)) {
Verbose.send("Spawner contains disallowed entity type.");
return false;
}
if (!new EntitySnapshotCheck().passes(spawner.getSpawnedEntity())) {
Verbose.send("Spawner entity snapshot check failed.");
return false;
}
}
}
}
// Trial Spawner check.
if (item.getType() == Material.TRIAL_SPAWNER) {
BlockState bs = blockStateMeta.getBlockState();
if (bs instanceof TrialSpawner spawner) {
Verbose.send("Running trial spawner check.");
if (spawner.getNormalConfiguration() != null) {
TrialSpawnerConfiguration config = spawner.getNormalConfiguration();
if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) {
Verbose.send("Trial Spawner failed check: Normal entity snapshot not allowed.");
return false;
}
}
if (spawner.getOminousConfiguration() != null) {
TrialSpawnerConfiguration config = spawner.getOminousConfiguration();
if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) {
Verbose.send("Trial Spawner failed check: Ominous entity snapshot not allowed.");
return false;
}
}
}
}
return true;
}
}

View File

@@ -0,0 +1,35 @@
package me.trouper.clonedupecore.server.events.hotbar.misc;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.utils.InventoryUtils;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class InventoryCheck extends AbstractCheck<Inventory> {
@Override
public boolean passes(Inventory inv) {
Verbose.send("Running Inventory Check");
for (ItemStack i : inv.getContents()) {
if (i == null || i.getType().isAir()) continue;
if (!new ItemCheck().passes(i)) {
Verbose.send("Inventory item failed check.");
return false;
}
Inventory subInventory = InventoryUtils.getInventory(i);
if (subInventory != null && !config.allowRecursion) {
Verbose.send("Recursion is disabled. Failing check.");
return false;
}
if (subInventory != null && !passes(subInventory)) {
Verbose.send("Sub-inventory failed check.");
return false;
}
}
Verbose.send("Inventory passed all checks.");
return true;
}
}

View File

@@ -0,0 +1,32 @@
package me.trouper.clonedupecore.server.events.hotbar.nbt;
import de.tr7zw.nbtapi.iface.ReadWriteNBT;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
public class ComponentCheck extends AbstractCheck<ReadWriteNBT> {
@Override
public boolean passes(ReadWriteNBT components) {
Verbose.send("Checking Consumable & tool");
if (!config.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) {
Verbose.send("Item is consumable and not allowed.");
return false;
}
if (!config.allowCustomTools && components.getCompound("minecraft:tool") != null) {
Verbose.send("Item is custom tool and not allowed.");
return false;
}
Verbose.send("Checking Entity data");
ReadWriteNBT entityData = components.getCompound("minecraft:entity_data");
if (!new EntityDataCheck().passes(entityData)) {
Verbose.send("Entity Data Check Failed.");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,40 @@
package me.trouper.clonedupecore.server.events.hotbar.nbt;
import de.tr7zw.nbtapi.NBT;
import de.tr7zw.nbtapi.iface.ReadWriteNBT;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.clonedupecore.server.events.hotbar.AbstractCheck;
import me.trouper.clonedupecore.server.events.hotbar.items.ItemCheck;
import org.bukkit.inventory.ItemStack;
public class EntityDataCheck extends AbstractCheck<ReadWriteNBT> {
@Override
public boolean passes(ReadWriteNBT entityData) {
if (entityData == null) {
Verbose.send("Entity Data check passed. There was no data.");
return true;
}
ReadWriteNBT itemData = entityData.getCompound("Item");
if (itemData != null) {
Verbose.send("Entity data holds an item");
ItemStack heldItem = NBT.itemStackFromNBT(itemData);
if (heldItem != null && !new ItemCheck().passes(heldItem)) {
Verbose.send("Item contents failed check.");
return false;
}
}
if (entityData.hasTag("DeathTime") && entityData.getInteger("DeathTime") < 1) {
Verbose.send("Death time check failed.");
return false;
}
if (entityData.hasTag("HurtTime") && entityData.getInteger("HurtTime") < 1) {
Verbose.send("Hurt time check failed.");
return false;
}
Verbose.send("Entity Data check passed. There was no flagging.");
return true;
}
}

View File

@@ -0,0 +1,113 @@
package me.trouper.clonedupecore.server.punishment;
import me.trouper.alias.server.Main;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Freeze implements Main {
private static final Map<UUID, FrozenPlayer> FROZEN_PLAYERS = new HashMap<>();
public static void freezePlayer(OfflinePlayer player) {
UUID uuid = player.getUniqueId();
if (!FROZEN_PLAYERS.containsKey(uuid)) {
FrozenPlayer frozen = new FrozenPlayer(main.getPlugin(), uuid);
FROZEN_PLAYERS.put(uuid, frozen);
}
}
public static void thawPlayer(OfflinePlayer player) {
UUID uuid = player.getUniqueId();
FrozenPlayer frozen = FROZEN_PLAYERS.remove(uuid);
if (frozen != null) {
frozen.thaw();
}
}
public static class FrozenPlayer {
private final UUID uuid;
private final Listener listener;
private boolean frozen;
public FrozenPlayer(JavaPlugin plugin, UUID uuid) {
this.uuid = uuid;
this.frozen = true;
this.listener = new Listener() {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (!event.getPlayer().getUniqueId().equals(uuid)) return;
if (!frozen) return;
if (!event.getFrom().toVector().equals(event.getTo().toVector())) {
event.setTo(event.getFrom());
}
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!event.getPlayer().getUniqueId().equals(uuid)) return;
if (!frozen) return;
event.setCancelled(true);
}
@EventHandler
public void onPlayerInteract(PlayerCommandPreprocessEvent event) {
if (!event.getPlayer().getUniqueId().equals(uuid)) return;
if (!frozen) return;
event.setCancelled(true);
}
@EventHandler
public void onPlayerDamage(EntityDamageByEntityEvent event) {
if (!event.getEntity().getUniqueId().equals(uuid)) return;
if (!frozen) return;
event.setCancelled(true);
}
@EventHandler
public void onPlayerDamage(EntityDamageByBlockEvent event) {
if (!event.getEntity().getUniqueId().equals(uuid)) return;
if (!frozen) return;
event.setCancelled(true);
}
};
Bukkit.getPluginManager().registerEvents(listener, plugin);
}
public void thaw() {
this.frozen = false;
HandlerList.unregisterAll(listener);
}
public boolean isFrozen() {
return frozen;
}
public UUID getUuid() {
return uuid;
}
}
}

View File

@@ -0,0 +1,139 @@
package me.trouper.clonedupecore.server.punishment;
import club.minnced.discord.webhook.WebhookClient;
import club.minnced.discord.webhook.WebhookClientBuilder;
import club.minnced.discord.webhook.send.WebhookEmbed;
import club.minnced.discord.webhook.send.WebhookEmbedBuilder;
import litebans.api.Database;
import litebans.api.Entry;
import me.trouper.alias.server.Main;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class LiteBansManager {
/**
* Get all ban entries for a specific player
*/
public List<WrappedEntry> getPlayerBans(OfflinePlayer player) {
List<WrappedEntry> bans = new ArrayList<>();
try {
PreparedStatement statement = Database.get().prepareStatement(
"SELECT * FROM {bans} WHERE uuid = ? ORDER BY time DESC"
);
statement.setString(1, player.getUniqueId().toString());
ResultSet rs = statement.executeQuery();
while (rs.next()) {
bans.add(new WrappedEntry(rs));
}
rs.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
return bans;
}
/**
* Get ban entries for a specific player and template
* @param template a short representing where in the template.yml list the item is.
*/
public List<WrappedEntry> getPlayerBansByTemplate(OfflinePlayer player, short template) {
List<WrappedEntry> bans = new ArrayList<>();
try {
PreparedStatement statement = Database.get().prepareStatement(
"SELECT * FROM {bans} WHERE uuid = ? AND template = ? ORDER BY time DESC"
);
statement.setString(1, player.getUniqueId().toString());
statement.setShort(2, template);
ResultSet result = statement.executeQuery();
while (result.next()) {
bans.add(new WrappedEntry(result));
}
result.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
return bans;
}
/**
* Get the count of bans for a specific template
* @param template a short representing where in the template.yml list the item is.
*/
public int getBanCountByTemplate(OfflinePlayer player, short template) {
return getPlayerBansByTemplate(player, template).size();
}
/**
* Check if player is currently banned
*/
public boolean isPlayerBanned(OfflinePlayer player) {
try {
PreparedStatement statement = Database.get().prepareStatement(
"SELECT COUNT(*) FROM {bans} WHERE uuid = ? AND active = 1"
);
statement.setString(1, player.getUniqueId().toString());
ResultSet result = statement.executeQuery();
if (result.next()) {
return result.getInt(1) > 0;
}
result.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
/**
* Get active ban for player
*/
public WrappedEntry getActiveBan(OfflinePlayer player) {
try {
PreparedStatement statement = Database.get().prepareStatement(
"SELECT * FROM {bans} WHERE uuid = ? AND active = 1 ORDER BY time DESC LIMIT 1"
);
statement.setString(1, player.getUniqueId().toString());
ResultSet result = statement.executeQuery();
if (result.next()) {
WrappedEntry entry = new WrappedEntry(result);
result.close();
statement.close();
return entry;
}
result.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* Execute a ban using LiteBans template system
*/
public void executeBan(OfflinePlayer player, String template, CommandSender executor) {
Bukkit.getScheduler().runTask(Main.main.getPlugin(),()-> {
Bukkit.dispatchCommand(executor,
String.format("litebans:ban %s %s -s", player.getName(), template));
});
}
}

View File

@@ -0,0 +1,61 @@
package me.trouper.clonedupecore.server.punishment;
import me.trouper.clonedupecore.data.Data;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
public class WrappedEntry implements Data {
private final long dateStart;
private final long dateEnd;
private final String reason;
private final String template;
private final boolean active;
private final boolean permanent;
public WrappedEntry(long dateStart, long dateEnd, String reason, boolean active, boolean permanent) {
this.dateStart = dateStart;
this.dateEnd = dateEnd;
this.reason = reason;
this.active = active;
this.permanent = permanent;
this.template = "none!";
}
public WrappedEntry(ResultSet rs) throws SQLException {
String reason = rs.getString("reason");
long time = rs.getLong("time");
long until = rs.getLong("until");
boolean active = rs.getBoolean("active");
short template = rs.getShort("template");
this.reason = reason;
this.dateStart = time;
this.dateEnd = until;
this.active = active;
this.permanent = until <= 0;
this.template = getConfig().banTemplates.get(template);
}
public long getDateStart() {
return dateStart;
}
public long getDateEnd() {
return dateEnd;
}
public String getReason() {
return reason;
}
public boolean isActive() {
return active;
}
public boolean isPermanent() {
return permanent;
}
}

View File

@@ -0,0 +1,102 @@
package me.trouper.clonedupecore.server.punishment.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.punishment.Freeze;
import org.bukkit.*;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class AnvilAnimation extends PunishmentAnimation {
private final Set<FallingBlock> spawnedAnvils = new HashSet<>();
private boolean spawned = false;
private boolean white = false;
public AnvilAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
super(plugin, player, finishTask);
}
@Override
protected void tick(int ticksElapsed) {
Freeze.freezePlayer(player);
if (!spawned) {
spawnAnvilRing();
DisplayUtils.ring(player.getLocation().add(0,10,0),5,1,(point)->{
ParticleUtils.builder()
.type(Particle.CLOUD)
.speed(0.07F)
.count(10)
.offset(0.5,0.2,0.5)
.spawn(point);
});
spawned = true;
return;
}
for (int i = 0; i < 5; i++) {
int finalI = i;
DisplayUtils.ring(player.getLocation().add(0,0.1,0), (double)i/2D,0.5,(point)->{
ParticleUtils.builder()
.type(Particle.DUST)
.data(new Particle.DustOptions(finalI % 2 == 0 ? Color.RED : Color.WHITE,2F))
.spawn(point);
});
}
spawnedAnvils.removeIf(block -> block.isDead() || block.isOnGround());
if (ticksElapsed > 20 && (spawnedAnvils.isEmpty() || ticksElapsed > 300)) {
close();
}
}
private void spawnAnvilRing() {
Location center = player.getLocation().add(0, 10, 0);
World world = player.getWorld();
int amount = 12;
for (int i = 0; i < amount; i++) {
int finalI = i;
Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
double angle = 2 * Math.PI * finalI / amount;
int RADIUS = 5;
double x = center.getX() + RADIUS * Math.cos(angle);
double z = center.getZ() + RADIUS * Math.sin(angle);
Location spawnLoc = new Location(world, x, center.getY(), z);
FallingBlock anvil = world.spawn(spawnLoc, FallingBlock.class);
anvil.setBlockData(Material.ANVIL.createBlockData());
anvil.setCancelDrop(true);
anvil.setHurtEntities(false);
Vector velocity = player.getLocation().toVector().subtract(spawnLoc.toVector()).normalize().multiply(0.7);
velocity.setY(-0.3);
anvil.setVelocity(velocity);
spawnedAnvils.add(anvil);
},i*2);
}
}
@Override
protected void cleanup() {
Freeze.thawPlayer(player);
for (FallingBlock block : spawnedAnvils) {
if (!block.isDead()) {
block.remove();
}
}
spawnedAnvils.clear();
}
}

View File

@@ -0,0 +1,98 @@
package me.trouper.clonedupecore.server.punishment.animations;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.utils.VectorUtils;
import me.trouper.clonedupecore.server.punishment.Freeze;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Guardian;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
public class GwenAnimation extends PunishmentAnimation {
private final List<Entity> guardians = new ArrayList<>();
int guardianCount = 4;
double animationTicks = 20 * 8;
public GwenAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
super(plugin, player, finishTask);
}
@Override
protected void tick(int ticksElapsed) {
Freeze.freezePlayer(player);
Location head = player.getEyeLocation();
Location center = head.clone().add(0, 3, 0);
World w = head.getWorld();
if (ticksElapsed > animationTicks) {
head.getWorld().spawnParticle(Particle.EXPLOSION_EMITTER,head,10,1,1,1);
head.getWorld().strikeLightningEffect(head);
Verbose.send("Closing G.W.E.N. animation");
close();
return;
}
double animationPrecent = ticksElapsed / animationTicks;
double radius = 4 - 3 * animationPrecent;
double angleStep = 2 * Math.PI / guardianCount;
double rotationSpeed = animationPrecent / 2;
if (ticksElapsed == 1) {
for (int i = 0; i < guardianCount; i++) {
double angle = i * angleStep;
double x = Math.cos(angle) * radius;
double z = Math.sin(angle) * radius;
Location loc = center.clone().add(x, 0, z);
Vector look = center.toVector().subtract(loc.toVector());
float[] angles = VectorUtils.toAngles(look);
w.spawn(loc, Guardian.class, (guardian) -> {
guardians.add(guardian);
guardian.setRotation(angles[0], angles[1]);
guardian.setTarget(player);
guardian.setAI(false);
guardian.setInvulnerable(true);
guardian.setLaser(true);
});
}
}
for (int i = 0; i < guardians.size(); i++) {
Entity entity = guardians.get(i);
if (entity == null || entity.isDead()) continue;
double angle = i * angleStep + rotationSpeed * ticksElapsed;
double x = Math.cos(angle) * radius;
double z = Math.sin(angle) * radius;
Location newLoc = center.clone().add(x, 0, z);
Vector look = center.toVector().subtract(newLoc.toVector());
float[] angles = VectorUtils.toAngles(look);
entity.teleport(newLoc);
entity.setRotation(angles[0], angles[1]);
}
}
@Override
protected void cleanup() {
Freeze.thawPlayer(player);
for (Entity entity : guardians) {
if (entity == null) continue;
entity.remove();
}
guardians.clear();
}
}

View File

@@ -0,0 +1,64 @@
package me.trouper.clonedupecore.server.punishment.animations;
import me.trouper.alias.server.systems.tracing.BlockDisplayRaytracer;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.clonedupecore.server.punishment.Freeze;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
public class LaserAnimation extends PunishmentAnimation {
public LaserAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
super(plugin, player, finishTask);
}
@Override
protected void tick(int ticksElapsed) {
Freeze.freezePlayer(player);
Location target = player.getEyeLocation();
Location center = player.getLocation();
if (ticksElapsed > 20 * 2) {
close();
return;
}
if (ticksElapsed == 1) {
DisplayUtils.sphereWave(target,8,1,1,(point)->{
point.getWorld().spawnParticle(Particle.SMOKE,point,2,0.5,0.5,0.5,0.01);
point.getWorld().spawnParticle(Particle.FLAME,point,2,0.5,0.5,0.5,0.01);
});
DisplayUtils.wave(player.getLocation().clone().add(0,0.5,0),5,point -> {
Block block = point.getWorld().getBlockAt(point.clone().subtract(0,2,0));
point.getWorld().spawn(point, FallingBlock.class,(launched)->{
launched.setVelocity(point.toVector().subtract(center.toVector()).normalize().multiply(0.4).setY(1));
launched.setBlockData(block.getBlockData());
launched.setBlockState(block.getState());
launched.setCancelDrop(true);
launched.setVisualFire(true);
});
point.getWorld().spawnParticle(Particle.EXPLOSION,point,1);
point.getWorld().spawnParticle(Particle.BLOCK,point,1,block.getBlockData());
},1);
}
Location orbit = player.getLocation().clone().add(200-ticksElapsed*2,0,100);
orbit.setY(315);
Bukkit.getScheduler().runTask(main.getPlugin(),()->{
BlockDisplayRaytracer.trace(Material.SMOOTH_QUARTZ,target,orbit,0.5,2);
BlockDisplayRaytracer.trace(Material.RED_STAINED_GLASS,target,orbit,1 + main.random().nextDouble(),2);
});
}
@Override
protected void cleanup() {
Freeze.thawPlayer(player);
}
}

View File

@@ -0,0 +1,61 @@
package me.trouper.clonedupecore.server.punishment.animations;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.alias.utils.SoundPlayer;
import me.trouper.clonedupecore.server.punishment.Freeze;
import me.trouper.clonedupecore.server.trolls.DragTrollWand;
import me.trouper.clonedupecore.utils.PlayerUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
public class LightningAnimation extends PunishmentAnimation {
public LightningAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
super(plugin, player, finishTask);
}
@Override
protected void tick(int ticksElapsed) {
Freeze.freezePlayer(player);
Location cloudCenter = player.getLocation().clone().add(0,10,0);
ParticleUtils.builder()
.type(Particle.CLOUD)
.offset(3, 0.8, 3)
.count(200)
.spawn(cloudCenter);
ParticleUtils.builder()
.type(Particle.ELECTRIC_SPARK)
.count(10)
.offset(0.3,1,0.3)
.spawn(player.getLocation().clone().add(0,1,0));
double x = main.randomizer().getRandomDouble(-4,4);
double z = main.randomizer().getRandomDouble(-4,4);
Location lightningLoc = cloudCenter.clone().add(x,0,z);
if (ticksElapsed > 40) {
PlayerUtils.instantTrueDamage(player, DamageSource.builder(DamageType.LIGHTNING_BOLT).build(),0.001);
if (ticksElapsed % 15 == 0) {
new SoundPlayer(Sound.ENTITY_LIGHTNING_BOLT_IMPACT,1,1).playAt(player.getLocation(),30);
new SoundPlayer(Sound.ENTITY_LIGHTNING_BOLT_THUNDER,1,1).playAt(lightningLoc,30);
for (int i = 0; i < 10; i++) {
DragTrollWand.drawLightning(lightningLoc,player.getEyeLocation(), Material.BLUE_ICE, Material.LIGHT_BLUE_STAINED_GLASS);
}
}
}
if (ticksElapsed > 120) {
close();
}
}
@Override
protected void cleanup() {
Freeze.thawPlayer(player);
}
}

View File

@@ -0,0 +1,98 @@
package me.trouper.clonedupecore.server.punishment.animations;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.clonedupecore.server.punishment.Freeze;
import me.trouper.clonedupecore.server.trolls.DragTrollWand;
import me.trouper.clonedupecore.server.trolls.MatrixTroll;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Display;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
public class MatrixAnimation extends PunishmentAnimation{
public MatrixAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
super(plugin, player, finishTask);
}
private final Map<Entity, ScheduledTask> cleanupList = new HashMap<>();
@Override
protected void tick(int ticksElapsed) {
if (ticksElapsed > 60) {
player.getWorld().strikeLightningEffect(player.getLocation());
close();
}
Location center = player.getLocation().clone().add(0,1,0);
center.setYaw(0);
center.setPitch(0);
ThreadLocalRandom random = ThreadLocalRandom.current();
DragTrollWand.drawLightning(center,center.clone().add(
random.nextDouble(-8,8),
random.nextDouble(0,8),
random.nextDouble(-8,8)
), Material.LIME_CONCRETE,Material.LIME_STAINED_GLASS);
if (ticksElapsed != 1) return;
Freeze.freezePlayer(player);
DisplayUtils.sphereWave(center,5,0.5,2,(point)->{
TextDisplay display = point.getWorld().spawn(point,TextDisplay.class,(td)->{
td.text(MatrixTroll.generateMatrix(2,2));
td.setBillboard(Display.Billboard.FIXED);
float yawDegrees = random.nextFloat() * 360;
float yawRadians = (float) Math.toRadians(yawDegrees);
AxisAngle4f angle = new AxisAngle4f(yawRadians,0,1,0);
td.setTransformation(new Transformation(
new Vector3f(0,0,0),
angle,
new Vector3f(3,3,3),
new AxisAngle4f(0,0,0,0)
));
td.setBackgroundColor(Color.fromARGB(0xFF000000));
td.setBrightness(new Display.Brightness(15,15));
});
ScheduledTask task = display.getScheduler().runAtFixedRate(main.getPlugin(),(t)->{
float yawDegrees = random.nextFloat() * 360;
float yawRadians = (float) Math.toRadians(yawDegrees);
AxisAngle4f angle = new AxisAngle4f(yawRadians,0,1,0);
display.setTransformation(new Transformation(
new Vector3f(0,0,0),
angle,
new Vector3f(1,1,1),
new AxisAngle4f(0,0,0,0)
));
display.setInterpolationDuration(4);
display.text(MatrixTroll.generateMatrix(9,5));
},null,1,5);
cleanupList.put(display,task);
});
}
@Override
protected void cleanup() {
Freeze.thawPlayer(player);
for (Map.Entry<Entity, ScheduledTask> entry : cleanupList.entrySet()) {
entry.getValue().cancel();
entry.getKey().remove();
}
cleanupList.clear();
}
}

View File

@@ -0,0 +1,93 @@
package me.trouper.clonedupecore.server.punishment.animations;
import me.trouper.alias.server.Main;
import me.trouper.alias.server.systems.Verbose;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.Closeable;
public abstract class PunishmentAnimation extends BukkitRunnable implements Closeable, Main {
protected final JavaPlugin plugin;
protected final String playerName;
protected Player player;
protected int ticksElapsed = 0;
private boolean finished = false;
private final Runnable finishTask;
public PunishmentAnimation(JavaPlugin plugin, Player player, Runnable finishTask) {
if (player == null || !player.isOnline()) {
throw new IllegalStateException("Player must be online and non-null to start animation.");
}
this.plugin = plugin;
this.player = player;
this.playerName = player.getName();
this.finishTask = finishTask;
Verbose.send("Animation created, running task timer...");
runTaskTimer(plugin, 0L, 1L);
}
@FunctionalInterface
public interface AnimationFactory {
PunishmentAnimation create(JavaPlugin plugin, Player player, Runnable onFinish);
}
@Override
public void run() {
if (isFinished()) return;
this.player = Bukkit.getPlayerExact(playerName);
if (this.player == null || !this.player.isOnline()) {
Verbose.send("Finishing animation due to player logging off!");
finishAnimation();
return;
}
try {
Bukkit.getScheduler().runTask(main.getPlugin(),()->{
tick(ticksElapsed++);
});
} catch (Exception e) {
Verbose.send("An animation experienced an unexpected runtime exception while ticking.");
e.printStackTrace();
Bukkit.getScheduler().runTask(main.getPlugin(), this::finishAnimation);
}
}
protected abstract void tick(int ticksElapsed);
protected abstract void cleanup();
private void finishAnimation() {
if (isFinished()) return;
finished = true;
try {
cancel();
} catch (IllegalStateException ignored) {}
cleanup();
if (finishTask != null) {
try {
finishTask.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void close() {
finishAnimation();
}
public boolean isFinished() {
return finished;
}
}

View File

@@ -0,0 +1,28 @@
package me.trouper.clonedupecore.server.trims;
import me.trouper.alias.server.Main;
import org.bukkit.entity.Player;
import java.util.Set;
public abstract class MaterialAnimation implements Main {
private final ValidMaterial material;
private final long loopDuration;
public MaterialAnimation(ValidMaterial material, long loopDuration) {
this.material = material;
this.loopDuration = loopDuration;
}
public ValidMaterial getMaterial() {
return material;
}
public long getLoopDuration() {
return loopDuration;
}
public abstract void tickMoving(Player player, Set<Player> viewers, long loopTime);
public abstract void tickStationary(Player player, Set<Player> viewers, long loopTime);
public abstract void onRemove(Player player);
}

View File

@@ -0,0 +1,125 @@
package me.trouper.clonedupecore.server.trims;
import me.trouper.alias.server.Main;
import me.trouper.clonedupecore.data.Data;
import me.trouper.clonedupecore.utils.ArmorUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ArmorMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.trim.ArmorTrim;
import java.util.*;
import java.util.stream.Collectors;
public class TrimManager implements Main, Data {
private final Map<ValidMaterial, MaterialAnimation> animations = new EnumMap<>(ValidMaterial.class);
private final Map<UUID, ActiveTrim> activePlayers = new HashMap<>();
public void register(MaterialAnimation animation) {
animations.put(animation.getMaterial(), animation);
}
public void tickPlayer(Player player) {
if (shouldHide(player)) return;
UUID uuid = player.getUniqueId();
ValidMaterial currentMaterial = getActiveMaterial(player);
ActiveTrim active = activePlayers.get(uuid);
Location currentLocation = player.getLocation();
if (currentMaterial == null) {
if (activePlayers.containsKey(uuid)) {
animations.get(activePlayers.get(uuid).material).onRemove(player);
activePlayers.remove(uuid);
}
return;
}
if (active != null && active.material == currentMaterial) {
active.ticks++;
if (active.lastCheckedLocation != null && active.lastCheckedLocation.getWorld().equals(currentLocation.getWorld()) && currentLocation.distanceSquared(active.lastCheckedLocation) > 0.001) {
active.lastMoveTimestamp = System.currentTimeMillis();
}
active.lastCheckedLocation = currentLocation;
} else {
active = new ActiveTrim(currentMaterial, currentLocation);
if (activePlayers.containsKey(uuid)) {
animations.get(activePlayers.get(uuid).material).onRemove(player);
}
activePlayers.put(uuid, active);
}
MaterialAnimation animation = animations.get(currentMaterial);
if (animation != null) {
long loopTime = active.ticks % animation.getLoopDuration();
long timeSinceLastMove = System.currentTimeMillis() - active.lastMoveTimestamp;
if (timeSinceLastMove < 1000) {
animation.onRemove(player);
animation.tickMoving(player, getViewers(player), loopTime);
} else {
animation.tickStationary(player, getViewers(player), loopTime);
}
}
}
public void startTicking() {
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), () -> Bukkit.getOnlinePlayers().forEach(this::tickPlayer),0,1);
}
private ValidMaterial getActiveMaterial(Player player) {
ItemStack[] armor = player.getInventory().getArmorContents();
if (armor.length != 4) return null;
ValidMaterial found = null;
for (ItemStack piece : armor) {
if (!ArmorUtils.isArmor(piece)) return null;
ItemMeta meta = piece.getItemMeta();
if (!(meta instanceof ArmorMeta armorMeta)) return null;
if (!armorMeta.hasTrim()) return null;
ArmorTrim trim = armorMeta.getTrim();
if (trim == null) return null;
ValidMaterial vm = ValidMaterial.validate(trim.getMaterial());
if (vm == null) return null;
if (found == null) {
found = vm;
} else if (found != vm) {
return null;
}
}
return found;
}
private boolean shouldHide(Player player) {
return getStorage().disabledOwnParticles.contains(player.getUniqueId().toString());
}
private Set<Player> getViewers(Player player) {
if (shouldHide(player)) return new HashSet<>();
return Bukkit.getOnlinePlayers().stream().filter(viewer-> !getStorage().disabledGlobalParticles.contains(viewer.getUniqueId().toString())).collect(Collectors.toSet());
}
private static class ActiveTrim {
final ValidMaterial material;
long ticks;
long lastMoveTimestamp;
Location lastCheckedLocation;
ActiveTrim(ValidMaterial material, Location initialLocation) {
this.material = material;
this.ticks = 0;
this.lastMoveTimestamp = System.currentTimeMillis();
this.lastCheckedLocation = initialLocation;
}
}
}

View File

@@ -0,0 +1,47 @@
package me.trouper.clonedupecore.server.trims;
import org.bukkit.Material;
public enum ValidArmorType {
LEATHER(Material.LEATHER_HELMET,Material.LEATHER_CHESTPLATE,Material.LEATHER_LEGGINGS,Material.LEATHER_BOOTS),
GOLDEN(Material.GOLDEN_HELMET,Material.GOLDEN_CHESTPLATE,Material.GOLDEN_LEGGINGS,Material.GOLDEN_BOOTS),
CHAINMAIL(Material.CHAINMAIL_HELMET,Material.CHAINMAIL_CHESTPLATE,Material.CHAINMAIL_LEGGINGS,Material.CHAINMAIL_BOOTS),
IRON(Material.IRON_HELMET,Material.IRON_CHESTPLATE,Material.IRON_LEGGINGS,Material.IRON_BOOTS),
DIAMOND(Material.DIAMOND_HELMET,Material.DIAMOND_CHESTPLATE,Material.DIAMOND_LEGGINGS,Material.DIAMOND_BOOTS),
NETHERITE(Material.NETHERITE_HELMET,Material.NETHERITE_CHESTPLATE,Material.NETHERITE_LEGGINGS,Material.NETHERITE_BOOTS);
private final Material helmet;
private final Material chestplate;
private final Material leggings;
private final Material boots;
ValidArmorType(Material helmet, Material chestplate, Material leggings, Material boots) {
this.helmet = helmet;
this.chestplate = chestplate;
this.leggings = leggings;
this.boots = boots;
}
public Material getHelmet() {
return helmet;
}
public Material getChestplate() {
return chestplate;
}
public Material getLeggings() {
return leggings;
}
public Material getBoots() {
return boots;
}
public static ValidArmorType validate(Material armor) {
for (ValidArmorType value : values()) {
if (value.helmet.equals(armor) || value.chestplate.equals(armor) || value.leggings.equals(armor) || value.boots.equals(armor)) return value;
}
return null;
}
}

View File

@@ -0,0 +1,39 @@
package me.trouper.clonedupecore.server.trims;
import org.bukkit.inventory.meta.trim.TrimMaterial;
public enum ValidMaterial {
AMETHYST(TrimMaterial.AMETHYST),
COPPER(TrimMaterial.COPPER),
DIAMOND(TrimMaterial.DIAMOND),
EMERALD(TrimMaterial.EMERALD),
GOLD(TrimMaterial.GOLD),
IRON(TrimMaterial.IRON),
LAPIS(TrimMaterial.LAPIS),
NETHERITE(TrimMaterial.NETHERITE),
QUARTZ(TrimMaterial.QUARTZ),
REDSTONE(TrimMaterial.REDSTONE);
private final TrimMaterial canonical;
ValidMaterial(TrimMaterial canonical) {
this.canonical = canonical;
}
public TrimMaterial getCanonical() {
return canonical;
}
public static ValidMaterial validate(TrimMaterial material) {
for (ValidMaterial value : ValidMaterial.values()) {
if (!value.getCanonical().equals(material)) continue;
return value;
}
return null;
}
public static TrimMaterial validate(String name) {
name = name.toUpperCase();
return ValidMaterial.valueOf(name).getCanonical();
}
}

View File

@@ -0,0 +1,77 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.Set;
public class AmethystAnimation extends MaterialAnimation {
private static final Color AMETHYST_COLOR = Color.fromRGB(139, 69, 190);
private static final long LOOP_DURATION = 120L;
public AmethystAnimation() {
super(ValidMaterial.AMETHYST, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.AMETHYST_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation().add(0, 0, 0);
double phase = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
double radius = 1.5 + 0.5 * Math.sin(phase * 2);
DisplayUtils.ring(playerLoc, radius, (point)->{
ParticleUtils.builder()
.type(Particle.DUST)
.data(new Particle.DustOptions(AMETHYST_COLOR,1F))
.viewers(viewers)
.spawn(point);
});
if (loopTime % 5 == 0) {
for (int i = 0; i < 3; i++) {
Location particleLoc = playerLoc.clone().add(
Math.random() * 2 - 1,
Math.random() * 2,
Math.random() * 2 - 1
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ENCHANT)
.spawn(particleLoc);
}
}
if (loopTime % 20 == 0) {
DisplayUtils.helix(playerLoc.clone().subtract(0, 0.5, 0), 0.8, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(AMETHYST_COLOR,0.5F))
.spawn(loc);
}, 0.2, 3);
}
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,124 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.*;
public class CopperAnimation extends MaterialAnimation {
private static final long LOOP_DURATION = 100L;
private final Map<UUID, List<BlockDisplay>> activeDisplays = new HashMap<>();
public CopperAnimation() {
super(ValidMaterial.COPPER, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.COPPER_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
UUID playerId = player.getUniqueId();
Location playerLoc = player.getLocation();
double angle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
List<BlockDisplay> displays = activeDisplays.computeIfAbsent(playerId, id -> {
List<BlockDisplay> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Location loc = player.getLocation().add(0, 1, 0);
BlockDisplay display = player.getWorld().spawn(loc, BlockDisplay.class,disp->{
disp.setVisibleByDefault(false);
viewers.forEach(viewer->{
viewer.showEntity(main.getPlugin(),disp);
});
});
display.setBlock(Material.COPPER_BLOCK.createBlockData());
display.addScoreboardTag(main.getCommon().getTempTag());
display.setInterpolationDuration(1);
display.setInterpolationDelay(0);
list.add(display);
}
return list;
});
for (int i = 0; i < displays.size(); i++) {
BlockDisplay display = displays.get(i);
double offsetAngle = angle + (i * Math.PI / 2);
double x = Math.cos(offsetAngle) * 2.0;
double z = Math.sin(offsetAngle) * 2.0;
double y = Math.sin(angle * 2) * 0.5;
Location blockLoc = playerLoc.clone().add(x, y + 1, z);
display.teleport(blockLoc);
ParticleUtils.builder()
.type(Particle.ELECTRIC_SPARK)
.viewers(viewers)
.spawn(blockLoc);
float scale = 0.3f + 0.1f * (float) Math.sin(angle * 3);
Transformation transform = new Transformation(
new Vector3f(0, 0, 0),
new AxisAngle4f((float) angle, 0, 1, 0),
new Vector3f(scale, scale, scale),
new AxisAngle4f(0, 0, 0, 1)
);
display.setTransformation(transform);
}
if (loopTime % 25 == 0) {
Location groundLoc = player.getLocation();
DisplayUtils.wave(groundLoc, 4.0, 1.2f, 0.5, (point) -> {
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.BLOCK)
.data(Material.COPPER_BLOCK.createBlockData())
.spawn(point);
});
}
if (loopTime % 15 == 0) {
Location groundLoc = player.getLocation();
DisplayUtils.wave(groundLoc, 2.0, 0.4f, 0.2, (point) -> {
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.WAX_ON)
.spawn(point);
});
}
}
@Override
public void onRemove(Player player) {
List<BlockDisplay> displays = activeDisplays.remove(player.getUniqueId());
if (displays != null) {
for (BlockDisplay display : displays) {
if (!display.isDead()) {
display.remove();
}
}
}
}
}

View File

@@ -0,0 +1,126 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.*;
public class DiamondAnimation extends MaterialAnimation {
private static final Color DIAMOND_COLOR = Color.fromRGB(185, 242, 255);
private static final long LOOP_DURATION = 160L;
private final Map<UUID, List<BlockDisplay>> activeDisplays = new HashMap<>();
public DiamondAnimation() {
super(ValidMaterial.DIAMOND, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.DIAMOND_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
UUID playerId = player.getUniqueId();
Location playerLoc = player.getLocation().add(0, 1.5, 0);
double mainAngle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
List<BlockDisplay> displays = activeDisplays.computeIfAbsent(playerId, id -> {
List<BlockDisplay> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Location initialLoc = player.getLocation().add(0, 1, 0);
BlockDisplay display = player.getWorld().spawn(initialLoc, BlockDisplay.class,disp->{
disp.setVisibleByDefault(false);
viewers.forEach(viewer->{
viewer.showEntity(main.getPlugin(),disp);
});
});
display.setBlock(Material.DIAMOND_BLOCK.createBlockData());
display.addScoreboardTag(main.getCommon().getTempTag());
display.setInterpolationDuration(1);
display.setInterpolationDelay(0);
list.add(display);
}
return list;
});
for (int orbit = 0; orbit < displays.size(); orbit++) {
BlockDisplay display = displays.get(orbit);
double orbitAngle = mainAngle * (1 + orbit * 0.3) + orbit * Math.PI * 2 / 3;
double radius = 1.8 - orbit * 0.2;
double height = Math.sin(orbitAngle * 2) * 0.8;
double x = Math.cos(orbitAngle) * radius;
double z = Math.sin(orbitAngle) * radius;
Location diamondLoc = playerLoc.clone().add(x, height, z);
display.teleport(diamondLoc);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(DIAMOND_COLOR,2F))
.speed(0.1F)
.spawn(diamondLoc);
float rotationAngle = (float)(orbitAngle * 2);
float scale = 0.25f + 0.05f * (float)Math.sin(mainAngle * 4);
Transformation transform = new Transformation(
new Vector3f(0, 0, 0),
new AxisAngle4f(rotationAngle, 1, 1, 1),
new Vector3f(scale, scale, scale),
new AxisAngle4f(0, 0, 0, 1)
);
display.setTransformation(transform);
}
if (loopTime % 3 == 0) {
for (int i = 0; i < 5; i++) {
Location sparkLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 4,
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 4
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ELECTRIC_SPARK)
.speed(0.1F)
.spawn(sparkLoc);
}
}
}
@Override
public void onRemove(Player player) {
List<BlockDisplay> displays = activeDisplays.remove(player.getUniqueId());
if (displays != null) {
for (BlockDisplay display : displays) {
if (!display.isDead()) {
display.remove();
}
}
}
}
}

View File

@@ -0,0 +1,85 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.Set;
public class EmeraldAnimation extends MaterialAnimation {
private static final Color EMERALD_COLOR = Color.fromRGB(80, 220, 92);
private static final long LOOP_DURATION = 140L;
public EmeraldAnimation() {
super(ValidMaterial.EMERALD, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.EMERALD_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation();
double time = loopTime / (double) LOOP_DURATION;
double spiralAngle = time * 4 * Math.PI;
for (int i = 0; i < 6; i++) {
double heightOffset = (i / 6.0) * 3 - 1.5;
double radius = 1.2 + 0.3 * Math.sin(spiralAngle + i);
double angle = spiralAngle + i * Math.PI / 3;
double x = Math.cos(angle) * radius;
double z = Math.sin(angle) * radius;
Location spiralLoc = playerLoc.clone().add(x, heightOffset + 1, z);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(EMERALD_COLOR,0.5F))
.spawn(spiralLoc);
}
double pulsePhase = (loopTime % 30) / 30.0 * 2 * Math.PI;
double pulseRadius = 2.0 + 0.5 * Math.sin(pulsePhase);
DisplayUtils.ring(playerLoc, pulseRadius,0.9,(loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.HAPPY_VILLAGER)
.spawn(loc);
});
if (loopTime % 8 == 0) {
for (int i = 0; i < 3; i++) {
Location natureLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 3,
Math.random() * 2,
(Math.random() - 0.5) * 3
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.COMPOSTER)
.spawn(natureLoc);
}
}
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,105 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.Set;
public class GoldAnimation extends MaterialAnimation {
private static final Color GOLD_COLOR = Color.fromRGB(255, 215, 0);
private static final long LOOP_DURATION = 80L;
public GoldAnimation() {
super(ValidMaterial.GOLD, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
DisplayUtils.ring(player.getLocation().clone().add(0, 2.1, 0), 0.3, 0.5f,(point)->{
ParticleUtils.builder()
.type(Particle.DUST)
.data(new Particle.DustOptions(GOLD_COLOR,0.3F))
.viewers(viewers)
.spawn(point);
});
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.GOLD_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation();
double crownAngle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
DisplayUtils.ring(player.getLocation().clone().add(0, 2.1, 0), 0.3, 0.5f,(point)->{
ParticleUtils.builder()
.type(Particle.DUST)
.data(new Particle.DustOptions(GOLD_COLOR,0.5F))
.viewers(viewers)
.spawn(point);
});
if (loopTime % 4 == 0) {
for (int i = 0; i < 4; i++) {
Location fallLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 4,
Math.random() * 3 + 2,
(Math.random() - 0.5) * 4
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.BLOCK)
.data(Material.GOLD_BLOCK.createBlockData())
.offset(0.2,0.2,0.2)
.speed(0.01F)
.spawn(fallLoc);
}
}
double wealthPhase = (loopTime % 60) / 60.0 * 2 * Math.PI;
for (int ring = 0; ring < 3; ring++) {
double ringRadius = 0.5 + ring * 0.5;
double ringPhase = wealthPhase + ring * Math.PI / 2;
double ringHeight = Math.sin(ringPhase) * 0.3;
DisplayUtils.ring(playerLoc.clone().add(0, ringHeight + 0.6, 0), ringRadius, 0.8f,(loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(GOLD_COLOR,0.5F))
.spawn(loc);
});
}
if (loopTime % 6 == 0) {
Location sparkleLoc = playerLoc.clone().add(
Math.cos(crownAngle * 3) * 2,
Math.sin(crownAngle * 2) * 1.5,
Math.sin(crownAngle * 3) * 2
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ELECTRIC_SPARK)
.offset(0.2,0.2,0.2)
.speed(0.3F)
.spawn(sparkleLoc);
}
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,150 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.*;
public class IronAnimation extends MaterialAnimation {
private static final Color IRON_COLOR = Color.fromRGB(145, 145, 145);
private static final long LOOP_DURATION = 60L;
private static final List<Material> IRON_ITEMS = List.of(
Material.IRON_AXE,
Material.IRON_SWORD,
Material.IRON_PICKAXE,
Material.IRON_SHOVEL,
Material.IRON_HOE,
Material.IRON_INGOT
);
private final Map<UUID, List<ItemDisplay>> activeDisplays = new HashMap<>();
public IronAnimation() {
super(ValidMaterial.IRON, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.IRON_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
UUID playerId = player.getUniqueId();
Location playerLoc = player.getLocation();
double shieldAngle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
List<ItemDisplay> items = activeDisplays.computeIfAbsent(playerId, id -> {
List<ItemDisplay> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Location initialLoc = player.getLocation().add(0, 1, 0);
ItemDisplay shield = player.getWorld().spawn(initialLoc, ItemDisplay.class,disp->{
disp.setVisibleByDefault(false);
viewers.forEach(viewer->{
viewer.showEntity(main.getPlugin(),disp);
});
});
shield.setItemStack(new ItemStack(main.randomizer().getRandomElement(IRON_ITEMS)));
shield.addScoreboardTag(main.getCommon().getTempTag());
shield.setInterpolationDuration(1);
shield.setInterpolationDelay(0);
list.add(shield);
}
return list;
});
for (int i = 0; i < items.size(); i++) {
ItemDisplay item = items.get(i);
double angle = shieldAngle + (i * Math.PI / 2);
double distance = 2.2 + 0.3 * Math.sin(shieldAngle * 3);
double x = Math.cos(angle) * distance;
double z = Math.sin(angle) * distance;
double y = 1.0 + Math.sin(shieldAngle * 2 + i) * 0.3;
Location shieldLoc = playerLoc.clone().add(x, y, z);
item.teleport(shieldLoc);
float rotY = (float) angle;
float scale = 0.8f;
Transformation transform = new Transformation(
new Vector3f(0, 0, 0),
new AxisAngle4f(rotY, 0, 1, 0),
new Vector3f(scale, scale, scale),
new AxisAngle4f(0, 0, 0, 1)
);
item.setTransformation(transform);
}
if (loopTime % 10 == 0) {
for (int i = 0; i < 6; i++) {
Location firworkLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 3,
Math.random() * 2.5,
(Math.random() - 0.5) * 3
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.FIREWORK)
.speed(0.1F)
.spawn(firworkLoc);
}
}
if (loopTime % 30 == 0) {
DisplayUtils.wave(playerLoc, 3.5, loc->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(IRON_COLOR,1F))
.spawn(loc);
}, 0.6);
}
if (loopTime % 5 == 0) {
Location dustLoc = playerLoc.clone().add(0, 0.1, 0);
DisplayUtils.ring(dustLoc, 1.8, 0.18,(loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.BLOCK)
.data(Material.IRON_BLOCK)
.spawn(loc);
});
}
}
@Override
public void onRemove(Player player) {
List<ItemDisplay> displays = activeDisplays.remove(player.getUniqueId());
if (displays != null) {
for (ItemDisplay display : displays) {
if (!display.isDead()) {
display.remove();
}
}
}
}
}

View File

@@ -0,0 +1,139 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.Set;
public class LapisAnimation extends MaterialAnimation {
private static final Color LAPIS_COLOR = Color.fromRGB(31, 64, 181);
private static final long LOOP_DURATION = 120L;
public LapisAnimation() {
super(ValidMaterial.LAPIS, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
DisplayUtils.ring(player.getLocation().clone().add(0, 2.1, 0), 0.3, 0.5f,(point)->{
ParticleUtils.builder()
.type(Particle.ENCHANT)
.viewers(viewers)
.spawn(point);
});
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.LAPIS_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().clone().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation();
double magicAngle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
DisplayUtils.ring(player.getLocation().clone().add(0, 2.1, 0), 0.3, 0.5f,(point)->{
ParticleUtils.builder()
.type(Particle.ENCHANT)
.viewers(viewers)
.spawn(point);
});
for (int circle = 0; circle < 3; circle++) {
double circleHeight = circle * 0.8 - 0.8;
double circleAngle = magicAngle * (1 + circle * 0.5);
double circleRadius = 1.5 - circle * 0.2;
Location circleCenter = playerLoc.clone().add(0, circleHeight, 0);
for (int segment = 0; segment < 6; segment++) {
int startAngle = (int)(Math.toDegrees(circleAngle) + segment * 60);
int endAngle = startAngle + 30;
DisplayUtils.arc(circleCenter, circleRadius, startAngle, endAngle, 0.2, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(LAPIS_COLOR,0.5F))
.spawn(loc);
});
}
}
if (loopTime % 8 == 0) {
for (int i = 0; i < 4; i++) {
double runeAngle = magicAngle + i * Math.PI / 2;
Location runeLoc = playerLoc.clone().add(
Math.cos(runeAngle) * 2.5,
Math.sin(magicAngle * 2) * 0.5 + 1,
Math.sin(runeAngle) * 2.5
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ENCHANT)
.count(3)
.offset(0.1,0.1,0.1)
.spawn(runeLoc);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ENCHANTED_HIT)
.count(3)
.offset(0.1,0.1,0.1)
.spawn(runeLoc);
}
}
if (loopTime % 15 == 0) {
DisplayUtils.vortex(playerLoc.clone().subtract(0, 0.5, 0), 0.8,(loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(LAPIS_COLOR,0.5F))
.spawn(loc);
},
0.05, 0.25, 4);
}
if (loopTime % 12 == 0) {
for (int i = 0; i < 5; i++) {
Location sparkLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 3,
Math.random() * 2,
(Math.random() - 0.5) * 3
);
sparkLoc.getWorld().spawnParticle(Particle.ELECTRIC_SPARK, sparkLoc, 1, 0, 0, 0, 0);
}
}
double pulsePhase = (loopTime % 40) / 40.0 * 2 * Math.PI;
double pulseIntensity = 0.5 + 0.5 * Math.sin(pulsePhase);
Color pulseLapis = Color.fromRGB(
(int)(31 * pulseIntensity + 100 * (1 - pulseIntensity)),
(int)(64 * pulseIntensity + 150 * (1 - pulseIntensity)),
(int)(181 * pulseIntensity + 255 * (1 - pulseIntensity))
);
DisplayUtils.ring(playerLoc, 2.2, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(pulseLapis,0.5F))
.spawn(loc);
});
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,147 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.*;
public class NetheriteAnimation extends MaterialAnimation {
private static final Color NETHERITE_COLOR = Color.fromRGB(68, 58, 59);
private static final long LOOP_DURATION = 200L;
private final Map<UUID, List<BlockDisplay>> activeDisplays = new HashMap<>();
public NetheriteAnimation() {
super(ValidMaterial.NETHERITE, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.LAVA)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().clone().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
UUID playerId = player.getUniqueId();
Location playerLoc = player.getLocation();
double angle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
List<BlockDisplay> displays = activeDisplays.computeIfAbsent(playerId, id -> {
List<BlockDisplay> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Location initialLoc = player.getLocation().add(0, 1, 0);
BlockDisplay display = player.getWorld().spawn(initialLoc, BlockDisplay.class,disp->{
disp.setVisibleByDefault(false);
viewers.forEach(viewer->{
viewer.showEntity(main.getPlugin(),disp);
});
});
display.setBlock(Material.NETHERITE_BLOCK.createBlockData());
display.addScoreboardTag(main.getCommon().getTempTag());
display.setInterpolationDuration(1);
display.setInterpolationDelay(0);
list.add(display);
}
return list;
});
for (int i = 0; i < displays.size(); i++) {
BlockDisplay display = displays.get(i);
double offsetAngle = angle + (i * Math.PI / 2);
double x = Math.cos(offsetAngle) * 2.0;
double z = Math.sin(offsetAngle) * 2.0;
double y = Math.sin(angle * 2) * 0.5;
Location blockLoc = playerLoc.clone().add(x, y + 1, z);
display.teleport(blockLoc);
float scale = 0.5f + 0.1f * (float) Math.sin(angle * 3);
Transformation transform = new Transformation(
new Vector3f(0, 0, 0),
new AxisAngle4f((float) angle, 0, 1, 0),
new Vector3f(scale, scale, scale),
new AxisAngle4f(0, 0, 0, 1)
);
display.setTransformation(transform);
}
if (loopTime % 6 == 0) {
for (int i = 0; i < 8; i++) {
Location fireLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 6,
Math.random() * 0.5,
(Math.random() - 0.5) * 6
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.SOUL_FIRE_FLAME)
.spawn(fireLoc);
}
}
if (loopTime % 50 == 0) {
DisplayUtils.fanWaveRandom(playerLoc, 4.5, 8, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(NETHERITE_COLOR,0.8F))
.spawn(loc);
}, 0.3);
}
double auraPhase = (loopTime % 60) / 60.0 * 2 * Math.PI;
double auraRadius = 2.8 + 0.7 * Math.sin(auraPhase * 2);
DisplayUtils.ring(playerLoc.clone().add(0, 0.2, 0), auraRadius,0.8, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.FLAME)
.spawn(loc);
});
if (loopTime % 10 == 0) {
for (int i = 0; i < 6; i++) {
Location emberLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 5,
Math.random() * 3 + 1,
(Math.random() - 0.5) * 5
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.LAVA)
.spawn(emberLoc);
}
}
}
@Override
public void onRemove(Player player) {
List<BlockDisplay> displays = activeDisplays.remove(player.getUniqueId());
if (displays != null) {
for (BlockDisplay display : displays) {
if (!display.isDead()) {
display.remove();
}
}
}
}
}

View File

@@ -0,0 +1,87 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.TaskManager;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.*;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.Set;
public class QuartzAnimation extends MaterialAnimation {
private static final Color QUARTZ_COLOR = Color.fromRGB(255, 252, 245);
private static final long LOOP_DURATION = 100L;
public QuartzAnimation() {
super(ValidMaterial.QUARTZ, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.QUARTZ_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().clone().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation();
double angle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
for (int i = 0; i < 4; i++) {
double offsetAngle = angle + (i * Math.PI / 2);
double x = Math.cos(offsetAngle) * 2.0;
double z = Math.sin(offsetAngle) * 2.0;
double y = Math.sin(angle * 2) * 0.5;
Location particleOrbit = playerLoc.clone().add(x, y + 1, z);
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.QUARTZ_BLOCK)
.count(1)
.speed(0)
.spawn(particleOrbit);
}
if (loopTime % 4 == 0) {
for (int i = 0; i < 4; i++) {
Location sparkleLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 4,
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 4
);
sparkleLoc.getWorld().spawnParticle(Particle.END_ROD, sparkleLoc, 1, 0, 0, 0, 0.02);
}
}
double purifyPhase = (loopTime % 50) / 50.0 * 2 * Math.PI;
for (int ring = 0; ring < 2; ring++) {
double ringRadius = 1.8 + ring * 0.8;
double ringPhase = purifyPhase + ring * Math.PI;
double ringHeight = Math.sin(ringPhase) * 0.4;
DisplayUtils.ring(playerLoc.clone().add(0, ringHeight, 0), ringRadius,(point)->{
ParticleUtils.builder()
.type(Particle.DUST)
.data(new Particle.DustOptions(QUARTZ_COLOR,1F))
.viewers(viewers)
.spawn(point);
});
}
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,128 @@
package me.trouper.clonedupecore.server.trims.animations;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.server.systems.visual.ParticleUtils;
import me.trouper.clonedupecore.server.trims.MaterialAnimation;
import me.trouper.clonedupecore.server.trims.ValidMaterial;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.Set;
public class RedstoneAnimation extends MaterialAnimation {
private static final Color REDSTONE_COLOR = Color.fromRGB(255, 0, 0);
private static final Color REDSTONE_DIM = Color.fromRGB(120, 0, 0);
private static final long LOOP_DURATION = 80L; // 4 seconds
public RedstoneAnimation() {
super(ValidMaterial.REDSTONE, LOOP_DURATION);
}
@Override
public void tickMoving(Player player, Set<Player> viewers, long loopTime) {
ParticleUtils.builder()
.type(Particle.BLOCK)
.data(Material.REDSTONE_BLOCK)
.viewers(viewers)
.offset(0.3,0.3,0.3)
.count(5)
.speed(0.05F)
.spawn(player.getLocation().clone().add(0,0.1,0));
}
@Override
public void tickStationary(Player player, Set<Player> viewers, long loopTime) {
Location playerLoc = player.getLocation();
double circuitPhase = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI;
boolean isPowered = Math.sin(circuitPhase * 4) > 0;
Color currentColor = isPowered ? REDSTONE_COLOR : REDSTONE_DIM;
double pathAngle = circuitPhase * 0.5;
for (int path = 0; path < 8; path++) {
double lineAngle = pathAngle + path * Math.PI / 4;
double startRadius = 0.8;
double endRadius = 2.5;
for (double r = startRadius; r <= endRadius; r += 0.2) {
double x = Math.cos(lineAngle) * r;
double z = Math.sin(lineAngle) * r;
Location lineLoc = playerLoc.clone().add(x, 0, z);
boolean sectionPowered = isPowered && ((int)(r * 5) % 3 != 0);
Color sectionColor = sectionPowered ? REDSTONE_COLOR : REDSTONE_DIM;
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(sectionColor,0.5F))
.spawn(lineLoc);
}
}
if (isPowered && loopTime % 8 == 0) {
for (int torch = 0; torch < 4; torch++) {
double torchAngle = circuitPhase + torch * Math.PI / 2;
Location torchLoc = playerLoc.clone().add(
Math.cos(torchAngle) * 2.2,
0.5,
Math.sin(torchAngle) * 2.2
);
DisplayUtils.beam(torchLoc,
(loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.PORTAL)
.spawn(loc);
},
0.1, 1);
}
}
if (loopTime % 30 == 0) {
DisplayUtils.wave(playerLoc.clone(), 3.0, 0.8f, 0.4, (loc)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.ENCHANTED_HIT)
.spawn(loc);
});
}
if (loopTime % 6 == 0) {
for (int i = 0; i < 6; i++) {
Location dustLoc = playerLoc.clone().add(
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 3
);
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(currentColor,0.5F))
.spawn(dustLoc);
}
}
double coreIntensity = isPowered ? 1.0 : 0.3;
Color coreColor = Color.fromRGB(
(int)(255 * coreIntensity),
(int)(50 * coreIntensity),
(int)(50 * coreIntensity)
);
DisplayUtils.ring(playerLoc, 1.2, 0.2f,(point)->{
ParticleUtils.builder()
.viewers(viewers)
.type(Particle.DUST)
.data(new Particle.DustOptions(coreColor,0.5F))
.spawn(point);
});
}
@Override
public void onRemove(Player player) {}
}

View File

@@ -0,0 +1,72 @@
package me.trouper.clonedupecore.server.trolls;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.Difficulty;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCamera;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import me.trouper.clonedupecore.utils.PacketUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.UUID;
public class CameraTroll implements TrollFeature {
@Override
public String getName() {
return "camera";
}
@Override
public Rating getRating() {
return Rating.SAFE;
}
@Override
public String getDescription() {
return "Sets the player's camera to a random mob.";
}
@Override
public void execute(CommandSender sender, Player target) {
User user = PacketUtils.getUser(target);
int entityID = target.getWorld().getEntityCount()+1;
WrapperPlayServerSpawnEntity spawnEntity = new WrapperPlayServerSpawnEntity(
entityID, UUID.randomUUID(), EntityTypes.ENDERMAN, SpigotConversionUtil.fromBukkitLocation(target.getLocation()),0,0,new Vector3d()
);
WrapperPlayServerCamera camera = new WrapperPlayServerCamera(entityID);
Difficulty diff = Difficulty.valueOf(target.getWorld().getDifficulty().toString());
GameMode gm = GameMode.valueOf(target.getGameMode().toString());
WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn(
DimensionTypes.THE_END,
target.getWorld().getName(),
diff,
1L,
gm,
null,
false,
false,
true,
null,
null,
null
);
WrapperPlayServerUpdateHealth health = new WrapperPlayServerUpdateHealth(Float.parseFloat("" + target.getHealth()),target.getFoodLevel(),target.getSaturation());
user.sendPacket(spawnEntity);
user.sendPacket(camera);
user.sendPacket(health);
}
@Override
public void stop(CommandSender sender, Player target) {
}
}

View File

@@ -0,0 +1,267 @@
package me.trouper.clonedupecore.server.trolls;
import me.trouper.alias.server.systems.AbstractWand;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.server.systems.tracing.BlockDisplayRaytracer;
import me.trouper.alias.server.systems.tracing.CustomDisplayRaytracer;
import me.trouper.alias.server.systems.tracing.Point;
import me.trouper.alias.server.systems.visual.DisplayUtils;
import me.trouper.alias.utils.ItemBuilder;
import me.trouper.alias.utils.SoundPlayer;
import me.trouper.clonedupecore.utils.PlayerUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
public class DragTrollWand extends AbstractWand implements TrollFeature {
@Override
public String getName() {
return "drag";
}
@Override
public Rating getRating() {
return Rating.DAMAGING;
}
@Override
public String getDescription() {
return "Gets a wand which you can use to controll players";
}
public static final ItemStack DRAG_WAND = ItemBuilder.of(Material.BREEZE_ROD)
.displayName(Component.text("Drag Wand", NamedTextColor.DARK_AQUA).decoration(TextDecoration.ITALIC,false).decoration(TextDecoration.BOLD,true))
.loreComponent(
Text.color("&1▪ &bRight-Click:&7 Select Player"),
Text.color("&1▪ &bLeft-Click:&7 Throw"),
Text.color("&1▪ &bSneak Left-Click:&7 Electrocute")
)
.build();
public DragTrollWand() {
super("clonedupe.troll", DRAG_WAND);
}
@Override
public void execute(CommandSender sender, Player target) {}
@Override
public void stop(CommandSender sender, Player target) {}
private final Map<UUID, DragTask> playerDragMap = new HashMap<>();
@Override
protected void onRightClick(Player player) {
handleDragPlayer(player);
}
@Override
protected void onLeftClick(Player player) {
handleThrowPlayer(player);
}
@Override
protected void onLeftClickSneak(Player player) {
handleElectrocute(player);
}
@Override
protected void onScrollUp(Player p) {
if (!playerDragMap.containsKey(p.getUniqueId())) return;
DragTask task = playerDragMap.get(p.getUniqueId());
task.setDistance(task.getDistance() + 0.5);
SoundPlayer.play(p,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 0.8F);
}
@Override
protected void onScrollDown(Player p) {
if (!playerDragMap.containsKey(p.getUniqueId())) return;
DragTask task = playerDragMap.get(p.getUniqueId());
if (task.getDistance() < 1.5) {
task.setDistance(2D);
SoundPlayer.play(p,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 1.3F);
return;
}
task.setDistance(task.getDistance() - 0.5);
SoundPlayer.play(p,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 1.7F);
}
private void handleDragPlayer(Player user) {
if (playerDragMap.containsKey(user.getUniqueId())) {
DragTask task = playerDragMap.remove(user.getUniqueId());
Entity target = task.drop();
if (target == null) return;
success(user,Component.text("You are no longer holding {0}."),target.name());
return;
}
CustomDisplayRaytracer.trace(user.getEyeLocation(),user.getEyeLocation().getDirection(),30,0.5, point -> {
List<Entity> targets = point.getNearbyEntities(user,1, entity -> entity instanceof LivingEntity);
if (targets.isEmpty()) return false;
LivingEntity target = (LivingEntity) targets.getFirst();
playerDragMap.put(user.getUniqueId(),startDragging(user,target));
target.setGlowing(true);
if (target instanceof Player t) t.setAllowFlight(true);
DisplayUtils.helix(target.getLocation(),0.6,(helix)->{
user.getWorld().spawnParticle(Particle.SOUL_FIRE_FLAME,helix,1,0,0,0,0);
},0.02,2);
success(user,Component.text("You are now holding {0}."),target.name());
return true;
});
}
private DragTask startDragging(Player user, LivingEntity victim) {
return new DragTask(victim.getUniqueId(), Bukkit.getScheduler().runTaskTimer(main.getPlugin(), () -> {
if (victim == null) {
playerDragMap.remove(user.getUniqueId()).drop();
return;
}
if (victim instanceof Player v && !v.isOnline()) {
playerDragMap.remove(user.getUniqueId()).drop();
return;
}
DragTask task = playerDragMap.get(user.getUniqueId());
Point point = CustomDisplayRaytracer.blocksInFrontOf(user.getEyeLocation(), user.getEyeLocation().getDirection(),task.getDistance(), false);
victim.teleport(point.getLoc().clone().subtract(0, 1, 0));
user.sendActionBar(Component.text("You ", NamedTextColor.GREEN).append(Component.text("",NamedTextColor.WHITE)).append(Component.text(victim.getName(),NamedTextColor.AQUA)).append(Component.text(" | ",NamedTextColor.GRAY)).append(Component.text(Math.round(task.getDistance()),NamedTextColor.DARK_GREEN)));
}, 0, 1),user.getLocation().distance(victim.getLocation()));
}
private void handleThrowPlayer(Player user) {
if (!playerDragMap.containsKey(user.getUniqueId())) return;
DragTask task = playerDragMap.remove(user.getUniqueId());
Entity target = task.launch(user);
if (target == null) return;
success(user,Component.text("You have thrown {0}."),target.name());
}
private void handleElectrocute(Player user) {
if (!playerDragMap.containsKey(user.getUniqueId())) return;
DragTask task = playerDragMap.get(user.getUniqueId());
Entity target = Bukkit.getEntity(task.dragged);
if (target == null) {
task.drop();
return;
}
AtomicInteger ticksRemaining = new AtomicInteger(60);
Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(shockTask)->{
if (ticksRemaining.getAndDecrement() <= 0 || target == null) {
shockTask.cancel();
return;
}
PlayerUtils.instantTrueDamage((LivingEntity) target, DamageSource.builder(DamageType.INDIRECT_MAGIC).build(),0.1);
drawLightning(user.getLocation().clone().add(0,1,0),target.getLocation(),Material.AMETHYST_BLOCK,Material.PURPLE_STAINED_GLASS);
},0,1);
}
public static void drawLightning(Location start, Location end, Material blockInner, Material blockOuter) {
int segments = 10;
double maxOffset = 0.5;
long stayTime = 10L;
double thickness = 0.02;
double thicknessOut = 0.04;
List<Player> viewers = new ArrayList<>(start.getWorld().getPlayers());
Location current = start.clone();
Vector direction = end.clone().subtract(start).toVector();
double segmentLength = direction.length() / segments;
direction.normalize().multiply(segmentLength);
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
for (int i = 0; i < segments; i++) {
Vector offset = new Vector(
(localRandom.nextDouble() - 0.5) * maxOffset,
(localRandom.nextDouble() - 0.5) * maxOffset,
(localRandom.nextDouble() - 0.5) * maxOffset
);
Location next = current.clone().add(direction).add(offset);
BlockDisplayRaytracer.trace(blockInner, current, next, thickness, stayTime, viewers);
BlockDisplayRaytracer.trace(blockOuter, current, next, thicknessOut, stayTime, viewers);
current = next;
}
BlockDisplayRaytracer.trace(blockInner, current, end, thickness, stayTime, viewers);
BlockDisplayRaytracer.trace(blockOuter, current, end, thicknessOut, stayTime, viewers);
end.getWorld().spawnParticle(Particle.FLASH,end,0,0,0,0,0);
}
private static class DragTask {
private final UUID dragged;
private final BukkitTask task;
private double distance;
private DragTask(UUID dragged, BukkitTask task, double distance) {
this.dragged = dragged;
this.task = task;
if (Double.isNaN(distance)) distance = 5;
this.distance = distance;
}
public UUID getDragged() {
return dragged;
}
public BukkitTask getTask() {
return task;
}
public double getDistance() {
return distance;
}
public void setDistance(double distance) {
this.distance = distance;
}
public Entity drop() {
this.getTask().cancel();
Entity target = Bukkit.getEntity(this.getDragged());
if (target == null) return null;
target.setGlowing(false);
if (target instanceof Player t) t.setAllowFlight(false);
return target;
}
public Entity launch(Player thrower) {
Entity target = this.drop();
if (target == null) return null;
target.setVelocity(target.getVelocity().add(thrower.getEyeLocation().getDirection().normalize().multiply(2)));
return target;
}
};
}

View File

@@ -0,0 +1,48 @@
package me.trouper.clonedupecore.server.trolls;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class FlickerTroll implements TrollFeature {
@Override
public String getName() {
return "flicker";
}
@Override
public String getDescription() {
return "Makes the target's screen flicker with the wake up animation.";
}
@Override
public Rating getRating() {
return Rating.SAFE;
}
private final List<UUID> eepyPlayers = new ArrayList<>();
@Override
public void execute(CommandSender sender, Player target) {
var player = PacketEvents.getAPI().getPlayerManager().getUser(target);
eepyPlayers.add(target.getUniqueId());
Bukkit.getScheduler().runTaskTimerAsynchronously(main.getPlugin(), (t) -> {
if (!eepyPlayers.contains(target.getUniqueId())) t.cancel();
player.sendPacket(new WrapperPlayServerEntityAnimation(target.getEntityId(), WrapperPlayServerEntityAnimation.EntityAnimationType.WAKE_UP));
}, 1, 1);
successAny(sender,"{0}'s screen is now flickering",target.getName());
}
@Override
public void stop(CommandSender sender, Player target) {
if (eepyPlayers.remove(target.getUniqueId())) successAny(sender,"Stopped flickering {0}'s screen.",target.getName());
else errorAny(sender,"{0}'s screen is not flickering.",target.getName());
}
}

View File

@@ -0,0 +1,133 @@
package me.trouper.clonedupecore.server.trolls;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.User;
import me.trouper.alias.utils.SoundPlayer;
import me.trouper.clonedupecore.utils.PacketUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class LSDTroll implements TrollFeature {
@Override
public String getName() {
return "lsd";
}
@Override
public String getDescription() {
return "Makes the player hallucinate.";
}
@Override
public Rating getRating() {
return Rating.SAFE;
}
private final List<Material> RAINBOW_BLOCKS = new ArrayList<>(
List.of(
Material.RED_WOOL,
Material.ORANGE_WOOL,
Material.YELLOW_WOOL,
Material.LIME_WOOL,
Material.LIGHT_BLUE_WOOL,
Material.MAGENTA_WOOL,
Material.PURPLE_WOOL
)
);
private final List<Material> RAINBOW_GLASS = new ArrayList<>(
List.of(
Material.RED_STAINED_GLASS,
Material.ORANGE_STAINED_GLASS,
Material.YELLOW_STAINED_GLASS,
Material.LIME_STAINED_GLASS,
Material.LIGHT_BLUE_STAINED_GLASS,
Material.MAGENTA_STAINED_GLASS,
Material.PURPLE_STAINED_GLASS
)
);
private final Map<UUID,BukkitTask> lsdTasks = new HashMap<>();
@Override
public void execute(CommandSender sender, Player target) {
if (lsdTasks.containsKey(target.getUniqueId())) {
error(sender,Component.text("{0} is already hallucinating!"),target.name());
return;
}
User v = PacketEvents.getAPI().getPlayerManager().getUser(target);
final int radius = 8;
AtomicInteger ticksPassed = new AtomicInteger(0);
lsdTasks.put(target.getUniqueId(),Bukkit.getScheduler().runTaskTimer(main.getPlugin(),()->{
if (!target.isOnline()) {
try {
lsdTasks.remove(target.getUniqueId()).cancel();
} catch (Exception ignored) {}
return;
}
int x = target.getLocation().getBlockX();
int y = target.getLocation().getBlockY();
int z = target.getLocation().getBlockZ();
World w = target.getWorld();
Location center = target.getLocation();
for (int xLoc = x - radius; xLoc < x + radius; xLoc++) {
for (int yLoc = y - radius; yLoc < y + radius; yLoc++) {
for (int zLoc = z - radius; zLoc < z + radius; zLoc++) {
Location loc = new Location(w,xLoc,yLoc,zLoc);
if (loc.distanceSquared(center) >= radius*radius) continue;
Block block = loc.getBlock();
if (block.isPassable()) {
if (loc.distanceSquared(center) <= (radius-3)*(radius-3)) {
PacketUtils.sendGhostBlock(v,loc,block.getType());
continue;
}
double distance = loc.distance(center);
int waveIndex = (int) ((ticksPassed.get() / 2.0 - distance) % RAINBOW_GLASS.size());
if (waveIndex < 0) waveIndex += RAINBOW_GLASS.size();
Material waveMaterial = RAINBOW_GLASS.get(waveIndex);
PacketUtils.sendGhostBlock(v, loc, waveMaterial);
continue;
}
double distance = loc.distance(center);
int waveIndex = (int) ((ticksPassed.get() / 2.0 - distance) % RAINBOW_BLOCKS.size());
if (waveIndex < 0) waveIndex += RAINBOW_BLOCKS.size();
Material waveMaterial = RAINBOW_BLOCKS.get(waveIndex);
PacketUtils.sendGhostBlock(v, loc, waveMaterial);
}
}
}
float pitch = ((float) (ticksPassed.get() % 5)) / 10;
SoundPlayer.play(target,Sound.BLOCK_NOTE_BLOCK_CHIME,10,pitch + 0.7F);
ticksPassed.incrementAndGet();
},0,2));
}
@Override
public void stop(CommandSender sender, Player target) {
try {
lsdTasks.remove(target.getUniqueId()).cancel();
success(sender, Component.text("Stopped {0}'s hallucinations."),target.name());
} catch (NullPointerException ex) {
error(sender, Component.text("{0} is not hallucinating."),target.name());
}
}
}

View File

@@ -0,0 +1,114 @@
package me.trouper.clonedupecore.server.trolls;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetPlayerInventory;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.utils.ItemBuilder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.title.Title;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class LiarTroll implements TrollFeature {
@Override
public String getName() {
return "liar";
}
@Override
public String getDescription() {
return "Liar, liar, pants on trinitrotoluene!";
}
@Override
public Rating getRating() {
return Rating.DAMAGING;
}
private final Map<UUID,ItemStack> activeLiars = new HashMap<>();
@Override
public void execute(CommandSender sender, Player target) {
User t = PacketEvents.getAPI().getPlayerManager().getUser(target);
activeLiars.put(target.getUniqueId(),target.getEquipment().getLeggings());
AtomicBoolean inverse = new AtomicBoolean(false);
AtomicInteger timeRemaining = new AtomicInteger(30 * 20);
Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task)->{
if (!activeLiars.containsKey(target.getUniqueId())) {
task.cancel();
return;
}
if (timeRemaining.get() <= 0) {
WrapperPlayServerSetPlayerInventory packet = new WrapperPlayServerSetPlayerInventory(37,SpigotConversionUtil.fromBukkitItemStack(activeLiars.get(target.getUniqueId())));
t.sendPacket(packet);
target.getWorld().createExplosion(target.getLocation(),1,false,false);
task.cancel();
return;
}
ItemStack displayItem = ItemBuilder.of(Material.TNT)
.displayName(Component.text("Liar, Liar!", NamedTextColor.RED).decoration(TextDecoration.ITALIC,false).decorate(TextDecoration.BOLD))
.loreComponent(
Component.text("Pants on trinitrotoluene!",NamedTextColor.GRAY),
Component.text("Explodes in ", NamedTextColor.GRAY).append(Component.text(timeRemaining.get() / 20,NamedTextColor.DARK_RED)).append(Component.text(" seconds.",NamedTextColor.GRAY))
)
.enchant(Enchantment.BINDING_CURSE)
.flags(ItemFlag.HIDE_ENCHANTS)
.build();
WrapperPlayServerSetPlayerInventory packet = new WrapperPlayServerSetPlayerInventory(37,SpigotConversionUtil.fromBukkitItemStack(displayItem));
Title title = Title.title(
Text.color("&c⚠ &4&lWARNING &r&c⚠"),
Text.color("&7&nLEGGINGS COMBUSTION EMINENT!"),
Title.Times.times(Duration.of(0, ChronoUnit.SECONDS),Duration.of(1, ChronoUnit.SECONDS),Duration.of(0, ChronoUnit.SECONDS))
);
if (inverse.get()) {
title = Title.title(
Text.color("&e⚠ &4&l&nWARNING&r &e⚠"),
Text.color("&7LEGGINGS COMBUSTION EMINENT!"),
Title.Times.times(Duration.of(0, ChronoUnit.SECONDS),Duration.of(1, ChronoUnit.SECONDS),Duration.of(0, ChronoUnit.SECONDS)))
;
ItemStack pants = displayItem.withType(Material.WHITE_WOOL);
target.getWorld().playSound(target.getLocation(), Sound.BLOCK_NOTE_BLOCK_BIT, SoundCategory.MASTER,10,2F);
packet.setStack(SpigotConversionUtil.fromBukkitItemStack(pants));
} else {
target.getWorld().playSound(target.getLocation(), Sound.BLOCK_NOTE_BLOCK_BIT, SoundCategory.MASTER,10,1.5F);
}
t.sendPacket(packet);
target.showTitle(title);
target.getWorld().spawnParticle(Particle.SMOKE,target.getLocation().add(0,0.5,0),1,0.1,0.1,0.1,0.05);
if (timeRemaining.get() <= 40) target.setFireTicks(2);
if (timeRemaining.getAndSet(timeRemaining.get() - 1) % 10 == 0) inverse.set(!inverse.get());
},0,1);
successAny(sender,"{0} will soon detonate!",target.getName());
}
@Override
public void stop(CommandSender sender, Player target) {
if (activeLiars.remove(target.getUniqueId()) != null) successAny(sender,"Removed the TNT from {0}'s pants.",target.getName());
}
}

View File

@@ -0,0 +1,316 @@
package me.trouper.clonedupecore.server.trolls;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChangeGameState;
import me.trouper.clonedupecore.utils.PlayerUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Display;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Transformation;
import org.bukkit.util.Vector;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
public class MatrixTroll implements TrollFeature {
@Override
public String getName() {
return "matrix";
}
@Override
public String getDescription() {
return "Make the player escape The Matrix.";
}
@Override
public Rating getRating() {
return Rating.HARMLESS;
}
private final Map<UUID, MatrixSession> matrixUsers = new ConcurrentHashMap<>();
@Override
public void execute(CommandSender sender, Player target) {
if (matrixUsers.containsKey(target.getUniqueId())) {
error(sender,Component.text("{0} is already escaping The Matrix."),target.name());
return;
}
MatrixSession session = new MatrixSession(target);
matrixUsers.put(target.getUniqueId(), session);
session.start();
success(sender,Component.text("Making {0} run from The Matrix."),target.name());
}
@Override
public void stop(CommandSender sender, Player target) {
MatrixSession session = matrixUsers.remove(target.getUniqueId());
if (session != null) {
session.stop();
success(sender,Component.text("Removed {0}'s Matirx."),target.name());
}
}
public static Component generateMatrix(int width, int height) {
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
Component matrix = Component.empty();
for (int y = 0; y < height; y++) {
Component line = Component.empty();
if (y > 0) {
line = line.appendNewline();
}
for (int x = 0; x < width; x++) {
char bit = (char) ('\u30A0' + localRandom.nextInt(96));
int greenValue = 100 + localRandom.nextInt(156);
TextColor green = TextColor.color(0, greenValue, 0);
TextColor finalColor = localRandom.nextDouble() < 0.05 ? TextColor.color(200, 255, 200) : green;
Component charComp = Component.text(bit).color(finalColor);
line = line.append(charComp);
}
matrix = matrix.append(line);
}
return matrix;
}
private class MatrixSession {
private final Player player;
private final List<MatrixColumn> columns = new ArrayList<>();
private BukkitTask spawnTask;
private BukkitTask updateTask;
private BukkitTask collisionTask;
private boolean active = true;
private int spawnRadius = 5;
private int maxColumns = 10;
public MatrixSession(Player player) {
this.player = player;
}
public void start() {
spawnTask = Bukkit.getScheduler().runTaskTimer(getPlugin(), () -> {
if (!active || !player.isOnline()) {
stop();
return;
}
spawnSingleColumn();
}, 0L, 2L);
updateTask = Bukkit.getScheduler().runTaskTimer(getPlugin(), () -> {
if (!active) return;
for (MatrixColumn column : columns) {
column.updateDisplays();
}
}, 0L, 2L);
collisionTask = Bukkit.getScheduler().runTaskTimer(getPlugin(), () -> {
if (!active || !player.isOnline()) {
stop();
return;
}
checkCollisionsAndLevitation();
}, 0L, 1L);
}
public void stop() {
active = false;
if (spawnTask != null) spawnTask.cancel();
if (updateTask != null) updateTask.cancel();
if (collisionTask != null) collisionTask.cancel();
for (MatrixColumn column : columns) {
column.remove();
}
columns.clear();
player.removePotionEffect(PotionEffectType.LEVITATION);
}
private void spawnSingleColumn() {
if (columns.size() >= maxColumns) {
columns.getFirst().remove();
columns.removeFirst();
return;
}
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
Location playerLoc = player.getLocation();
double angle = localRandom.nextDouble() * 2 * Math.PI;
double distance = localRandom.nextDouble() * spawnRadius;
double x = playerLoc.getX() + Math.cos(angle) * distance;
double z = playerLoc.getZ() + Math.sin(angle) * distance;
double y = playerLoc.getY() - 20;
Location columnLoc = new Location(playerLoc.getWorld(), x, y, z);
MatrixColumn column = new MatrixColumn(columnLoc);
columns.add(column);
column.spawn();
}
private void checkCollisionsAndLevitation() {
Location playerLoc = player.getLocation();
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
boolean inColumn = false;
MatrixColumn touchedColumn = null;
for (MatrixColumn column : columns) {
if (column.getBoundingBox().contains(playerLoc.toVector())) {
inColumn = true;
touchedColumn = column;
}
}
if (inColumn) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 10, 0, false, false));
if (localRandom.nextDouble() < 0.5) PlayerUtils.instantTrueDamage(player, DamageSource.builder(DamageType.HOT_FLOOR).build(),0.01);
if (localRandom.nextDouble() < 0.01) PacketEvents.getAPI().getPlayerManager().getUser(player).sendPacket(new WrapperPlayServerChangeGameState(WrapperPlayServerChangeGameState.Reason.PLAY_ELDER_GUARDIAN_MOB_APPEARANCE,0.0F));
List<MatrixColumn> toRemove = new ArrayList<>();
for (MatrixColumn column : columns) {
if (column != touchedColumn) {
column.remove();
toRemove.add(column);
}
}
columns.removeAll(toRemove);
}
}
}
private class MatrixColumn {
private final Location center;
private final List<TextDisplay> displays = new ArrayList<>();
private final BoundingBox boundingBox;
private final int height = 40;
public MatrixColumn(Location center) {
this.center = center.clone();
this.boundingBox = new BoundingBox(
center.getX() - 0.5, center.getY(), center.getZ() - 0.5,
center.getX() + 0.5, center.getY() + height, center.getZ() + 0.5
);
}
public void spawn() {
float[] yaws = {0f, 90f, 180f, 270f};
double offset = 0.5;
Vector[] directions = {
new Vector(0, 0, -offset),
new Vector(offset, 0, 0),
new Vector(0, 0, offset),
new Vector(-offset, 0, 0)
};
float textScale = 2.5f;
final Transformation scaleTransform = new Transformation(
new Vector3f(),
new Quaternionf(),
new Vector3f(textScale, textScale, textScale),
new Quaternionf()
);
for (int i = 0; i < 4; i++) {
Location displayLoc = center.clone().add(directions[i]);
displayLoc.add(0, height/4, 0);
displayLoc.setYaw(yaws[i]);
Component text = generateMatrix(2, height);
int finalI = i;
TextDisplay displayOut = center.getWorld().spawn(displayLoc, TextDisplay.class, CreatureSpawnEvent.SpawnReason.CUSTOM, entity -> {
entity.text(text);
entity.setBillboard(Display.Billboard.FIXED);
entity.setVisibleByDefault(false);
entity.setRotation(yaws[finalI], 0f);
entity.setBackgroundColor(Color.fromARGB(0xDD000000));
entity.setTransformation(scaleTransform);
});
TextDisplay displayIn = center.getWorld().spawn(displayLoc, TextDisplay.class, CreatureSpawnEvent.SpawnReason.CUSTOM, entity -> {
entity.text(text);
entity.setBillboard(Display.Billboard.FIXED);
entity.setVisibleByDefault(false);
entity.setRotation(yaws[finalI] + 180f, 0f);
entity.setBackgroundColor(Color.fromARGB(0xDD000000));
entity.setTransformation(scaleTransform);
});
Player target = matrixUsers.entrySet().stream()
.filter(entry -> entry.getValue().columns.stream().anyMatch(col -> col == this))
.map(entry -> Bukkit.getPlayer(entry.getKey()))
.findFirst()
.orElse(null);
if (target != null) {
target.showEntity(getPlugin(), displayIn);
target.showEntity(getPlugin(), displayOut);
}
displays.add(displayOut);
displays.add(displayIn);
}
}
public void updateDisplays() {
for (TextDisplay display : displays) {
if (!display.isDead()) {
display.text(generateMatrix(2, height));
}
}
}
public BoundingBox getBoundingBox() {
return boundingBox;
}
public boolean checkCollision(Location playerLoc) {
for (TextDisplay display : displays) {
if (display.getLocation().distance(playerLoc) < 1.0) {
return true;
}
}
return false;
}
public void remove() {
for (TextDisplay display : displays) {
display.remove();
}
displays.clear();
}
}
}

View File

@@ -0,0 +1,514 @@
package me.trouper.clonedupecore.server.trolls;
import me.trouper.alias.server.systems.AbstractWand;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.server.systems.tracing.BlockDisplayRaytracer;
import me.trouper.alias.server.systems.tracing.CustomDisplayRaytracer;
import me.trouper.alias.server.systems.tracing.Point;
import me.trouper.alias.server.systems.world.ExplosionOptions;
import me.trouper.alias.server.systems.world.ExplosionResult;
import me.trouper.alias.server.systems.world.ExplosionUtils;
import me.trouper.alias.utils.FormatUtils;
import me.trouper.alias.utils.ItemBuilder;
import me.trouper.alias.utils.SoundPlayer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.UUID;
public class OrbitalTrollWand extends AbstractWand implements TrollFeature {
private static final ItemStack ORBITAL_WAND = ItemBuilder.of(Material.TRIPWIRE_HOOK)
.displayName(Text.color("&4&lOrbital Sky Torch").decoration(TextDecoration.ITALIC,false))
.loreComponent(
Text.color("&8▪ &cRight-Click:&f Aim Cannon"),
Text.color("&8▪ &cRight-Click Again:&f Charge Ion Beam"),
Text.color("&8▪ &cLeft-Click:&f Call off shot"),
Text.color("&8▪ &cSwap-Hands:&f Undo"),
Text.color("&7"),
Text.color("&7Now Includes &oreal&r&7 orbital configurations!"),
Text.color("&7"),
Text.color("&8Thank you TheCymaera and HeroBrayden!")
)
.build();
public OrbitalTrollWand() {
super("clonedupe.orbital", ORBITAL_WAND);
}
@Override
public String getName() {
return "orbital";
}
@Override
public String getDescription() {
return "Get a wand which lets you control the server's orbital ion cannon.";
}
@Override
public Rating getRating() {
return Rating.DESTRUCTIVE;
}
@Override
public void execute(CommandSender sender, Player target) {}
@Override
public void stop(CommandSender sender, Player target) {}
private final Map<UUID, OrbitalIonCannon> cannonMap = new HashMap<>();
private final Map<UUID, Stack<ExplosionResult>> playerHistory = new HashMap<>();
private boolean undoHistory(Player player) {
Stack<ExplosionResult> history = playerHistory.getOrDefault(player.getUniqueId(),new Stack<>());
if (history == null || history.isEmpty()) return false;
try (ExplosionResult result = history.pop()) {
result.restore();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onSwapHand(Player player) {
try {
if (undoHistory(player)) {
Text.message(Text.Pallet.SUCCESS,false,player, Component.text("Restored previous explosion."));
} else {
Text.message(Text.Pallet.ERROR,false,player,Component.text("Undo stack is empty!"));
}
} catch (Exception e) {
e.printStackTrace();
Text.message(Text.Pallet.ERROR,false,player,Component.text("An exception was thrown when undoing the explosion! Check console for details."));
}
}
@Override
protected void onRightClick(Player player) {
if (cannonMap.containsKey(player.getUniqueId())) {
OrbitalIonCannon playerCannon = cannonMap.get(player.getUniqueId());
if (playerCannon.getFireTask() != null || playerCannon.getChargeTask() != null) {
Text.message(Text.Pallet.ERROR,false,player,Component.text("You are already charging the Ion Cannon!"));
cannonMap.remove(player.getUniqueId());
return;
}
playerCannon.charge();
cannonMap.remove(player.getUniqueId());
Text.message(Text.Pallet.SUCCESS,false,player, Component.text("Charging Ion Cannon..."));
return;
}
OrbitalIonCannon cannon = new OrbitalIonCannon(player, traceTargets(player));
cannonMap.put(player.getUniqueId(),cannon);
Text.message(Text.Pallet.SUCCESS,false,player, Component.text("You can now aim the Ion Cannon."));
}
@Override
protected void onLeftClick(Player player) {
if (!cannonMap.containsKey(player.getUniqueId())) {
Text.message(Text.Pallet.ERROR,false,player,Component.text("You are not controlling the Ion Cannon."));
return;
}
OrbitalIonCannon playerCannon = cannonMap.get(player.getUniqueId());
playerCannon.destroy();
cannonMap.remove(player.getUniqueId());
Text.message(Text.Pallet.SUCCESS,false,player, Component.text("Deactivated Ion Cannon Safely."));
}
@Override
protected void onScrollDown(Player player) {
if (!cannonMap.containsKey(player.getUniqueId())) {
Text.message(Text.Pallet.ERROR,false,player,Component.text("You are not controlling the Ion Cannon."));
return;
}
OrbitalIonCannon cannon = cannonMap.get(player.getUniqueId());
if (cannon.getPower() <= 1) {
SoundPlayer.play(player,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 1.3F);
cannon.setPower(2);
return;
}
cannon.setPower(Math.max(1,cannon.getPower() - 5));
SoundPlayer.play(player,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 1.7F);
player.sendActionBar(Text.format(Text.Pallet.INFO,"Set ion cannon power to {0}.",cannon.getPower()));
}
@Override
protected void onScrollUp(Player player) {
if (!cannonMap.containsKey(player.getUniqueId())) {
Text.message(Text.Pallet.ERROR,false,player,Component.text("You are not controlling the Ion Cannon."));
return;
}
OrbitalIonCannon cannon = cannonMap.get(player.getUniqueId());
if (cannon.getPower() >= 50) {
SoundPlayer.play(player,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 1.3F);
cannon.setPower(49);
return;
}
cannon.setPower(Math.min(50,cannon.getPower() + 5));
SoundPlayer.play(player,Sound.BLOCK_NOTE_BLOCK_HAT, 1, 0.8F);
player.sendActionBar(Text.format(Text.Pallet.INFO,"Set ion cannon power to {0}.",cannon.getPower()));
}
public class OrbitalIonCannon {
private final Player owner;
private final OrbitalConfiguration simulatedOrbit;
private Location skyRenderLocation;
private Location skyTraceLocation;
private Location targetLocation;
private Location groundTraced;
private BukkitTask aimTask;
private BukkitTask chargeTask;
private BukkitTask fireTask;
private int power;
private ExplosionResult explosionResult;
private final BukkitTask tickTask = new BukkitRunnable() {
@Override
public void run() {
updateOrbitLocation();
setSkyRenderLocation(getSimulatedOrbit().getRenderLocation(getTargetLocation()));
setSkyTraceLocation(CustomDisplayRaytracer.blocksInFrontOf(getTargetLocation(),getSimulatedOrbit().getNormalToSatellite(getTargetLocation().toVector()),20,false).getLoc());
if (!getAimTask().isCancelled()) {
setTargetLocation(traceTargets(getOwner()));
setGroundTraced(CustomDisplayRaytracer.trace(getSkyTraceLocation(),getTargetLocation(),5,(point)-> {
Material type = point.getLoc().getBlock().getType();
boolean collidable = type.isCollidable();
Verbose.send("Block at {0} collision: {1}, {2}",point.getLoc(),type,collidable);
return collidable;
}).getLoc());
}
}
}.runTaskTimer(main.getPlugin(), 0, 1);
private OrbitalIonCannon(Player owner, Location target) {
this.owner = owner;
this.targetLocation = target;
this.power = 20;
this.simulatedOrbit = new OrbitalConfiguration(target.toVector());
this.setAimTask(aimRunnable.runTaskTimer(main.getPlugin(), 0, 1));
}
private final BukkitRunnable fireRunnable = new BukkitRunnable() {
int ticksElapsed = 0;
@Override
public void run() {
if (ticksElapsed >= 120) {
if (getExplosionResult() != null) {
getExplosionResult().close();
}
destroy();
return;
}
if (ticksElapsed <= 40) {
BlockDisplay inner = BlockDisplayRaytracer.trace(Material.WHITE_CONCRETE_POWDER,getGroundTraced(),getSkyRenderLocation(),1,2);
inner.setGlowing(true);
inner.setGlowColorOverride(Color.fromRGB(0xFFDDDD));
BlockDisplayRaytracer.trace(Material.WHITE_STAINED_GLASS,getGroundTraced(),getSkyRenderLocation(),1.5 + main.random().nextDouble(),2);
}
if (ticksElapsed == 0) {
ExplosionOptions options = new ExplosionOptions()
.setMaxBurnRadius(getPower())
.setFalloffRadius((double) getPower() / 3)
.setCoreRadius(((double) getPower() / 3) / 1.2)
.setBaseDamage(120)
.setBurnDelay(0)
.setDestructionDelay(1);
Stack<ExplosionResult> history = playerHistory.getOrDefault(getOwner().getUniqueId(),new Stack<>());
setExplosionResult(ExplosionUtils.createExplosion(getGroundTraced(),options));
history.push(getExplosionResult());
playerHistory.put(getOwner().getUniqueId(),history);
}
ticksElapsed++;
}
};
private final BukkitRunnable chargeRunnable = new BukkitRunnable() {
int ticksElapsed = 0;
@Override
public void run() {
if (ticksElapsed >= 20) {
setFireTask(fireRunnable.runTaskTimer(main.getPlugin(),0,1));
this.cancel();
return;
}
if (ticksElapsed == 1) {
new SoundPlayer(Sound.ENTITY_WARDEN_SONIC_CHARGE,30,0.5F).playAt(getTargetLocation(),40);
new SoundPlayer(Sound.BLOCK_CONDUIT_AMBIENT,30,0.5F).playAt(getGroundTraced(),40);
}
double thickness = (double) ticksElapsed / 20;
int otherValues = ((ticksElapsed / 20 * 100) + 100);
BlockDisplay inner = BlockDisplayRaytracer.trace(Material.RED_CONCRETE, getGroundTraced(),getSkyRenderLocation(),thickness,2);
inner.setGlowing(true);
inner.setGlowColorOverride(Color.fromRGB(255,otherValues,otherValues));
BlockDisplayRaytracer.trace(Material.ORANGE_STAINED_GLASS, getGroundTraced(),getSkyRenderLocation(),thickness + 0.1 ,2);
ticksElapsed++;
}
};
private final BukkitRunnable aimRunnable = new BukkitRunnable() {
int ticksElapsed = 0;
@Override
public void run() {
if (getChargeTask() != null) {
this.cancel();
return;
}
if (ticksElapsed == 0) {
new SoundPlayer(Sound.BLOCK_RESPAWN_ANCHOR_CHARGE,1,0.5F).playAt(groundTraced,40);
}
BlockDisplay inner = BlockDisplayRaytracer.trace(Material.RED_CONCRETE_POWDER,getGroundTraced(),getSkyRenderLocation(),0.1,2);
inner.setGlowing(true);
inner.setGlowColorOverride(Color.fromRGB(0xFF0000));
BlockDisplayRaytracer.trace(Material.RED_STAINED_GLASS,getGroundTraced(),getSkyRenderLocation(),0.2,2);
ticksElapsed++;
}
};
private void updateOrbitLocation() {
if (getTargetLocation() == null || getTargetLocation().getWorld() == null) {
destroy();
return;
}
getSimulatedOrbit().tick();
}
private void destroy() {
if (getTickTask() != null) this.getTickTask().cancel();
if (getFireTask() != null) this.getFireTask().cancel();
if (getChargeTask() != null) this.getChargeTask().cancel();
if (getAimTask() != null) this.getAimTask().cancel();
}
private void charge() {
this.getAimTask().cancel();
this.setChargeTask(this.chargeRunnable.runTaskTimer(main.getPlugin(),0,1));
}
public Player getOwner() {
return owner;
}
public Location getTargetLocation() {
return targetLocation;
}
public void setTargetLocation(Location targetLocation) {
this.targetLocation = targetLocation;
}
public BukkitTask getAimTask() {
return aimTask;
}
public void setAimTask(BukkitTask aimTask) {
this.aimTask = aimTask;
}
public BukkitTask getChargeTask() {
return chargeTask;
}
public void setChargeTask(BukkitTask chargeTask) {
this.chargeTask = chargeTask;
}
public BukkitTask getFireTask() {
return fireTask;
}
public void setFireTask(BukkitTask fireTask) {
this.fireTask = fireTask;
}
public BukkitTask getTickTask() {
return tickTask;
}
public ExplosionResult getExplosionResult() {
return explosionResult;
}
public void setExplosionResult(ExplosionResult explosionResult) {
this.explosionResult = explosionResult;
}
public OrbitalConfiguration getSimulatedOrbit() {
return simulatedOrbit;
}
public Location getSkyRenderLocation() {
return skyRenderLocation;
}
public void setSkyRenderLocation(Location skyRenderLocation) {
this.skyRenderLocation = skyRenderLocation;
}
public Location getGroundTraced() {
return groundTraced;
}
public void setGroundTraced(Location groundTraced) {
this.groundTraced = groundTraced;
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
public Location getSkyTraceLocation() {
return skyTraceLocation;
}
public void setSkyTraceLocation(Location skyTraceLocation) {
this.skyTraceLocation = skyTraceLocation;
}
}
public class OrbitalConfiguration {
private Vector baseSatellite;
public OrbitalConfiguration(Vector center) {
this.baseSatellite = center.clone().setY(11_368_121);
}
public void tick() {
Vector orbitalLocation = getBaseSatellite();
if (orbitalLocation.getX() > 30_000_000 || orbitalLocation.getZ() > 30_000_000) {
setBaseSatellite(new Vector(-orbitalLocation.getX(),orbitalLocation.getY(),-orbitalLocation.getZ()));
return;
}
setBaseSatellite(orbitalLocation.clone().add(new Vector(2_500,0,762)));
}
public Location getRenderLocation(Location center) {
Vector dir = getNormalToSatellite(center.toVector());
int castDistance = 128;
return center.clone().add(dir.getX() * castDistance, dir.getY() * castDistance, dir.getZ() * castDistance);
}
public Vector getNormalToSatellite(Vector target) {
Vector closestSatellite = getClosestSatellite(target).clone();
return closestSatellite.clone().subtract(target).normalize();
}
public Vector getNormalToTarget(Vector target) {
Vector closestSatellite = getClosestSatellite(target).clone();
return target.clone().subtract(closestSatellite).normalize();
}
public Vector getClosestSatellite(Vector location) {
Vector[] satellites = new Vector[] {
getBaseSatellite(),
getPosXSat(),
getPosZSat(),
getPosXZSat(),
getNegXSat(),
getNegZSat(),
getNegXZSat(),
getPosXNegZSat(),
getNegXPosZSat()
};
Vector closest = satellites[0];
double minDistance = location.distanceSquared(closest);
for (int i = 1; i < satellites.length; i++) {
double distance = location.distanceSquared(satellites[i]);
if (distance < minDistance) {
minDistance = distance;
closest = satellites[i];
}
}
return closest;
}
public Vector getPosXSat() {
return baseSatellite.clone().add(new Vector(60_000_000,0,0));
}
public Vector getPosZSat() {
return baseSatellite.clone().add(new Vector(0,0,60_000_000));
}
public Vector getPosXZSat() {
return baseSatellite.clone().add(new Vector(60_000_000,0,60_000_000));
}
public Vector getNegXSat() {
return baseSatellite.clone().add(new Vector(-60_000_000,0,0));
}
public Vector getNegZSat() {
return baseSatellite.clone().add(new Vector(0,0,-60_000_000));
}
public Vector getNegXZSat() {
return baseSatellite.clone().add(new Vector(-60_000_000,0,60_000_000));
}
public Vector getPosXNegZSat() {
return baseSatellite.clone().add(new Vector(60_000_000,0,-60_000_000));
}
public Vector getNegXPosZSat() {
return baseSatellite.clone().add(new Vector(-60_000_000,0,60_000_000));
}
public Vector getBaseSatellite() {
return baseSatellite;
}
public void setBaseSatellite(Vector baseSatellite) {
this.baseSatellite = baseSatellite;
}
}
private static Location traceTargets(Player player) {
Point hit = CustomDisplayRaytracer.trace(player.getEyeLocation(),player.getEyeLocation().getDirection(),128,CustomDisplayRaytracer.HIT_BLOCK);
return hit.getLoc();
}
}

View File

@@ -0,0 +1,87 @@
package me.trouper.clonedupecore.server.trolls;
import me.trouper.alias.server.systems.AbstractWand;
import me.trouper.alias.server.systems.tracing.BlockDisplayRaytracer;
import me.trouper.alias.utils.ItemBuilder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class TestTrollWand extends AbstractWand implements TrollFeature{
private static final ItemStack TEST_WAND = ItemBuilder.of(Material.TRIPWIRE_HOOK)
.displayName(Component.text("Testing Wand", NamedTextColor.YELLOW))
.loreComponent(
Component.text("Prints out the action you preformed.")
)
.build();
@Override
public String getName() {
return "testwand";
}
@Override
public String getDescription() {
return "A wand for testing interfaces.";
}
@Override
public Rating getRating() {
return Rating.SAFE;
}
public TestTrollWand() {
super("clonedupe.troll", TEST_WAND);
}
@Override
public void execute(CommandSender sender, Player target) {}
@Override
public void stop(CommandSender sender, Player target) {}
@Override
protected void onScrollUp(Player player) {
infoAny(player, "You scrolled upwards");
}
@Override
protected void onScrollDown(Player player) {
infoAny(player, "You scrolled downwards");
}
@Override
protected void onSwapHand(Player player) {
infoAny(player, "You swapped hands");
}
@Override
protected void onSwapHandSneak(Player player) {
infoAny(player, "You swapped hands while sneaking");
}
@Override
protected void onLeftClick(Player player) {
infoAny(player, "You left clicked");
}
@Override
protected void onLeftClickSneak(Player player) {
infoAny(player, "You left clicked while sneaking");
}
@Override
protected void onRightClick(Player player) {
infoAny(player, "You right clicked");
}
@Override
protected void onRightClickSneak(Player player) {
infoAny(player, "You right clicked while sneaking");
}
}

View File

@@ -0,0 +1,24 @@
package me.trouper.clonedupecore.server.trolls;
import me.trouper.alias.server.events.QuickListener;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public interface TrollFeature extends QuickListener {
String getName();
String getDescription();
Rating getRating();
void execute(CommandSender sender, Player target);
void stop(CommandSender sender, Player target);
enum Rating {
SAFE,
HARMLESS,
DAMAGING,
DESTRUCTIVE
}
}

View File

@@ -0,0 +1,184 @@
package me.trouper.clonedupecore.server.trolls;
import me.trouper.alias.server.systems.AbstractWand;
import me.trouper.alias.server.systems.Text;
import me.trouper.alias.server.systems.tracing.CustomDisplayRaytracer;
import me.trouper.alias.utils.ItemBuilder;
import me.trouper.alias.utils.SoundPlayer;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.BlockDisplay;
import org.bukkit.entity.Display;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Transformation;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
public class VoidTrollWand extends AbstractWand implements TrollFeature {
private static final ItemStack VOID_WAND = ItemBuilder.of(Material.ECHO_SHARD)
.displayName(Text.color("&5&lVoid Wand").decoration(TextDecoration.ITALIC,false))
.loreComponent(
Text.color("&8▪ &dRight-Click:&f Abyss"),
Text.color("&8▪ &dLeft-Click:&f Engulf")
)
.build();
@Override
public String getName() {
return "void";
}
@Override
public String getDescription() {
return "Get a wand to harness the power of the void.";
}
@Override
public Rating getRating() {
return Rating.DAMAGING;
}
@Override
public void execute(CommandSender sender, Player target) {}
@Override
public void stop(CommandSender sender, Player target) {}
public VoidTrollWand() {
super("clonedupe.troll",VOID_WAND);
}
@Override
protected void onRightClick(Player player) {
CustomDisplayRaytracer.traceDelayed(main.getPlugin(),player.getEyeLocation(),player.getEyeLocation().getDirection(),30,1, point -> {
List<LivingEntity> targets = point.getNearbyEntities(player,1,true, entity->entity instanceof LivingEntity).stream().map(entity -> (LivingEntity) entity).toList();
if (targets.isEmpty()) {
SoundPlayer.play(point.getLoc(), Sound.BLOCK_SOUL_SAND_BREAK, 1,0.4F,10);
Particle.ASH.builder().location(point.getLoc()).offset(0.1,0.1,0.1).count(10).spawn();
return false;
}
LivingEntity target = targets.getFirst();
Location targetLoc = target.getLocation().clone();
Location originalLoc = target.getLocation().clone().add(0,0.1,0);
SoundPlayer.play(target.getLocation(), Sound.ITEM_TRIDENT_THUNDER, 1,0.4F,10);
World w = target.getWorld();
AtomicInteger counter = new AtomicInteger(0);
Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task) -> {
if (counter.getAndIncrement() > 60) {
task.cancel();
return;
}
target.teleport(targetLoc.subtract(0, 0.05, 0));
w.spawnParticle(Particle.BLOCK, originalLoc, 50, 0.5, 0, 0.5, 0, Material.BLACK_CONCRETE.createBlockData());
w.spawnParticle(Particle.SQUID_INK, originalLoc, 50, 0.5, 0, 0.5, 0);
},0,1);
return true;
});
}
@Override
protected void onLeftClick(Player player) {
CustomDisplayRaytracer.traceDelayed(main.getPlugin(),player.getEyeLocation(),player.getEyeLocation().getDirection(),30,1,point -> {
List<LivingEntity> targets = point.getNearbyEntities(player, 1, true, entity -> entity instanceof LivingEntity).stream().map(entity -> (LivingEntity) entity).toList();
if (targets.isEmpty()) {
SoundPlayer.play(point.getLoc(), Sound.BLOCK_SOUL_SAND_BREAK, 1,0.4F,10);
Particle.ASH.builder().location(point.getLoc()).offset(0.1, 0.1, 0.1).count(10).spawn();
return false;
}
LivingEntity target = targets.getFirst();
engulf(target);
return true;
});
}
public void engulf(LivingEntity target) {
List<BlockDisplay> displays = new ArrayList<>();
final double effectLength = 80;
final double maxHeight = 2.5;
final int blocksPerRing = 6;
final double ringSpacing = 0.3;
final double baseRadius = 0.8;
final double ticksPerRing = effectLength / (maxHeight / ringSpacing);
final Location targetLoc = target.getLocation();
AtomicInteger ticksElapsed = new AtomicInteger();
Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task)->{
if (ticksElapsed.getAndIncrement() > effectLength) {
target.setHealth(0);
SoundPlayer.play(targetLoc, Sound.ENTITY_DROWNED_DEATH_WATER, 1,0.4F,10);
displays.forEach(BlockDisplay::remove);
task.cancel();
return;
}
double height = (ticksElapsed.get() / ticksPerRing) * ringSpacing;
displays.addAll(voidRing(targetLoc,height,baseRadius,blocksPerRing));
target.teleport(targetLoc);
SoundPlayer.play(targetLoc, Sound.BLOCK_BUBBLE_COLUMN_BUBBLE_POP, 1,0.4F,10);
if (ticksElapsed.get() % 20 == 0) SoundPlayer.play(targetLoc, Sound.ENTITY_DROWNED_HURT, 1,0.4F,10);
},0,1);
}
public static List<BlockDisplay> voidRing(Location center, double yOffset, double radius, int blockCount) {
List<BlockDisplay> displays = new ArrayList<>();
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
double angleStep = (2 * Math.PI) / blockCount;
for (int i = 0; i < blockCount; i++) {
double angle = i * angleStep;
double randomRadius = radius + (localRandom.nextDouble() - 0.5) * 0.5;
double x = center.getX() + Math.cos(angle) * randomRadius;
double z = center.getZ() + Math.sin(angle) * randomRadius;
double y = center.getY() + yOffset;
Location blockLoc = new Location(center.getWorld(), x, y, z);
BlockDisplay display = center.getWorld().spawn(blockLoc, BlockDisplay.class);
display.setBlock(Material.BLACK_CONCRETE.createBlockData());
display.addScoreboardTag("$/CloneDupeCore/ Temp");
display.setBrightness(new Display.Brightness(0,0));
Vector3f translation = new Vector3f(0, 0, 0);
Vector3f scale = new Vector3f(
0.6f + localRandom.nextFloat() * 0.8f,
0.6f + localRandom.nextFloat() * 0.8f,
0.6f + localRandom.nextFloat() * 0.8f
);
AxisAngle4f leftRotation = new AxisAngle4f(
localRandom.nextFloat() * (float)(2 * Math.PI),
localRandom.nextFloat() - 0.5f,
localRandom.nextFloat() - 0.5f,
localRandom.nextFloat() - 0.5f
);
AxisAngle4f rightRotation = new AxisAngle4f(0, 0, 0, 0);
Transformation transformation = new Transformation(translation, leftRotation, scale, rightRotation);
display.setTransformation(transformation);
displays.add(display);
}
return displays;
}
}

View File

@@ -0,0 +1,44 @@
package me.trouper.clonedupecore.utils;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
public class ArmorUtils {
public static boolean isArmor(ItemStack i) {
if (i == null || i.isEmpty()) return false;
return isHelmet(i) || isChestplate(i) || isLeggings(i) || isBoots(i);
}
public static boolean isHelmet(ItemStack i) {
if (i == null || i.isEmpty()) return false;
Material m = i.getType();
String n = m.name();
return n.contains("HELMET");
}
public static boolean isChestplate(ItemStack i) {
if (i == null || i.isEmpty()) return false;
Material m = i.getType();
String n = m.name();
return n.contains("CHESTPLATE");
}
public static boolean isLeggings(ItemStack i) {
if (i == null || i.isEmpty()) return false;
Material m = i.getType();
String n = m.name();
return n.contains("LEGGINGS");
}
public static boolean isBoots(ItemStack i) {
if (i == null || i.isEmpty()) return false;
Material m = i.getType();
String n = m.name();
return n.contains("BOOTS");
}
}

View File

@@ -0,0 +1,29 @@
package me.trouper.clonedupecore.utils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockChange;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
public class PacketUtils {
public static void sendGhostBlock(User target, Location loc, Material material) {
if (!material.isBlock()) throw new IllegalArgumentException("Only block materials are allowed!");
Vector3i locVec = new Vector3i(loc.getBlockX(),loc.getBlockY(),loc.getBlockZ());
BlockData blockData = material.createBlockData();
WrappedBlockState data = SpigotConversionUtil.fromBukkitBlockData(blockData);
WrapperPlayServerBlockChange packet = new WrapperPlayServerBlockChange(locVec,data.getGlobalId());
packet.setBlockState(data);
target.sendPacket(packet);
}
public static User getUser(Player bukkitPlayer) {
return PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer);
}
}

View File

@@ -0,0 +1,113 @@
package me.trouper.clonedupecore.utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
import java.util.*;
public class PlayerUtils {
private static float clampPitch(float pitch) {
return Math.max(-90, Math.min(90, pitch));
}
private static float wrapYaw(float yaw) {
yaw = yaw % 360;
if (yaw < 0) yaw += 360;
return yaw;
}
private static Vector getDirection(float yaw, float pitch) {
double radYaw = Math.toRadians(yaw);
double radPitch = Math.toRadians(pitch);
double x = -Math.cos(radPitch) * Math.sin(radYaw);
double y = -Math.sin(radPitch);
double z = Math.cos(radPitch) * Math.cos(radYaw);
return new Vector(x, y, z);
}
public static Player playerClosestAngle(Player player, double range) {
Vector playerDirection = player.getEyeLocation().getDirection().normalize();
Location eyeLoc = player.getEyeLocation();
return player.getNearbyEntities(range, range, range).stream()
.filter(entity -> entity instanceof Player && !entity.equals(player))
.map(entity -> (Player) entity)
.min((p1, p2) -> {
Vector dirToP1 = p1.getEyeLocation().toVector().subtract(eyeLoc.toVector()).normalize();
Vector dirToP2 = p2.getEyeLocation().toVector().subtract(eyeLoc.toVector()).normalize();
double angle1 = playerDirection.angle(dirToP1);
double angle2 = playerDirection.angle(dirToP2);
return Double.compare(angle1, angle2);
})
.orElse(null);
}
public static void instantDamage(LivingEntity target, DamageSource source, double amount) {
int max = target.getMaximumNoDamageTicks();
int ticks = target.getNoDamageTicks();
target.setMaximumNoDamageTicks(1);
target.setNoDamageTicks(0);
target.damage(amount,source);
target.setMaximumNoDamageTicks(max);
target.setNoDamageTicks(ticks);
}
public static void instantTrueDamage(LivingEntity target, DamageSource source, double amount) {
int max = target.getMaximumNoDamageTicks();
int ticks = target.getNoDamageTicks();
target.setMaximumNoDamageTicks(1);
target.setNoDamageTicks(0);
dealTrueDamage(target,source,amount);
target.setMaximumNoDamageTicks(max);
target.setNoDamageTicks(ticks);
}
public static void dealTrueDamage(LivingEntity target, DamageSource source, double amount) {
if (source.getDirectEntity() instanceof Player a && target instanceof Player t) return;
EntityEquipment equipment = target.getEquipment();
if (equipment == null) {
target.damage(amount, source);
return;
}
ItemStack helmet = equipment.getHelmet();
ItemStack chestplate = equipment.getChestplate();
ItemStack leggings = equipment.getLeggings();
ItemStack boots = equipment.getBoots();
equipment.setHelmet(null);
equipment.setChestplate(null);
equipment.setLeggings(null);
equipment.setBoots(null);
try {
target.damage(amount, source);
} finally {
equipment.setHelmet(helmet);
equipment.setChestplate(chestplate);
equipment.setLeggings(leggings);
equipment.setBoots(boots);
}
}
}

View File

@@ -0,0 +1,46 @@
name: CloneDupeCore
version: '1.0-SNAPSHOT'
main: me.trouper.clonedupecore.CloneDupeCore
api-version: '1.21'
depend:
- packetevents
- LiteBans
- NBTAPI
load: POSTWORLD
authors: [ obvWolf ]
description: Server Utilities for CloneDupe
commands:
ping:
usage: /ping [player]
permission: clonedupe.ping
description: Display the ping of you or another player.
clonetroll:
usage: /troll <feature> [player] [stop]
permission: clonedupe.troll
description: Many features to harras players with.
aliases:
- troll
- ct
clonedupecore:
usage: /clonedupecore <subcommand>
permission: clonedupe.admin
offend:
usage: "/offend <player> <query|punish> <offense>"
permission: clonedupe.offend
aliases:
- punish
trimeffect:
usage: /trimeffect <global|self>
permissions:
clonedupe.troll:
default: op
description: Allows a player to use troll features and the troll wand.
clonedupe.ping:
default: true
description: Allows the use of the ping command. Enabled by default.
clonedupe.admin:
default: op
description: Allows the use of the admin command.
clonedupe.offend:
default: op
description: Allows punishment of players through litebans.