30 Commits
Author SHA1 Message Date
Len a92201e20d Update to 26.2 2026-07-29 14:49:39 -05:00
Len 66ef1d321c Remove Java Toolchain 2026-07-29 14:48:19 -05:00
Len 0ccec03137 Update Gradle 2026-07-29 14:48:01 -05:00
Len db5e6921ef Update Gradle stage in Jenkinsfile 2026-07-29 14:47:38 -05:00
stijn 85e5effc15 Update VillagerWorkstationEvent to remove outdated api use 2026-05-14 19:38:50 +02:00
stijn 8ad6fe2608 Merge branch 'plot_rtp'
# Conflicts:
#	src/main/java/com/alttd/playerutils/PlayerUtils.java
#	src/main/java/com/alttd/playerutils/config/Config.java
#	src/main/java/com/alttd/playerutils/config/Messages.java
2026-05-14 19:31:47 +02:00
stranzjakobandstijn 0a2e5865bf Show villager workstation feature 2026-05-14 19:23:43 +02:00
stijn eb7bd6cc95 Added random plot command by Jakob Stranz 2026-05-12 20:06:47 +02:00
stijn 18de5cebd6 Make book byte limits configurable per player permissions 2026-04-11 23:04:45 +02:00
stijn ff9133ecfa Remove player from all glow teams when glow is turned off 2026-03-31 23:26:34 +02:00
stijn 57b5b8fe84 Refactor book byte enforcement by replacing BookByteChunkLimitListener with simplified BookByteLimitListener. Adjust byte limits and improve oversized book handling logic. 2026-03-27 20:46:09 +01:00
stijn e03c51198c Add BookByteChunkLimitListener and BookWriteEvent to enforce book byte limits and prevent chunk saturation 2026-03-27 18:51:43 +01:00
stijn bd7a46c283 Fix project name in settings.gradle.kts to PlayerUtils 2026-01-21 07:18:12 +01:00
stijn 4b1c2078eb Add BlockDispenseEvent listener to ensure armor stands placed through dispensers respect the armor stand chunk limit 2026-01-21 07:10:46 +01:00
stijn f7313f8f25 Add ReCountArmorStands command and handle armorstands being removed 2026-01-21 06:59:35 +01:00
stijn cb98c3761b Update PlayerJoin to handle attribute keys and improve attribute modification logic 2025-11-01 00:56:19 +01:00
stijn df347fde7f Add PlayerJoin event listener to modify player attributes on join and update project configuration 2025-10-31 01:45:54 +01:00
stijn ab162b5094 Refactor logging by replacing custom Logger with SLF4J and adapt all usages 2025-07-24 23:04:52 +02:00
stijn c66b30ff90 Update XPCheque to use calculateTotalExperiencePoints and setExperienceLevelAndProgress to fix exp reset bug 2025-07-22 19:27:12 +02:00
stijn 7625c57b8b Refine ghast speed message to display human-readable speed names 2025-07-20 04:44:45 +02:00
stijn 7f6d4c4b36 Add ghast speed command with configurable speed levels 2025-07-20 04:36:02 +02:00
stijn d6d7269fee Fix inverted spawn egg check in BlockBlockUseEvent 2025-07-17 23:12:47 +02:00
stijn e81d532178 Restrict usage of END_PORTAL_FRAME in BlockBlockUseEvent 2025-07-17 00:04:11 +02:00
stijn c016db5969 Update BlockBlockUseEvent to use MaterialTags for spawn egg checks 2025-07-16 23:56:42 +02:00
stijn 05fd3beb9c Add BlockBlockUseEvent to restrict usage of blocked items and blocks 2025-07-15 21:25:02 +02:00
auto f80645e2fe Switch to using cosmos 2025-06-20 23:14:41 +02:00
auto 785a64b7e6 Refactor Glow command to optimize logic order.
Reorganized the "off" condition block to execute after clearing existing glow teams.
2025-03-24 21:26:56 +01:00
auto 7cfdc39f75 Refactor interact event handling and add trial spawner logic.
Renamed the event handler for clarity and added logic to handle trial spawner interactions with cooldown feedback for players. Introduced functional interfaces for cleaner and reusable code when processing armor stand counts and trial spawner timers.
2025-03-22 20:57:59 +01:00
auto 7c04361ea5 Add armor stand placement limits per chunk
Implemented functionality to restrict the number of armor stands a player can place in a chunk based on permissions. Added configuration support for customizable limits and proper error handling for edge cases. Registered the LimitArmorStands event listener in the plugin manager.
2025-03-22 20:33:31 +01:00
auto 10449f4a80 Refactor message-sending to use sendRichMessage API.
Replaced all instances of sendMiniMessage with sendRichMessage for improved readability and consistency across the codebase. This change ensures better handling of message formatting and placeholders.
2025-03-22 19:12:28 +01:00
40 changed files with 1588 additions and 236 deletions
Vendored
+7 -1
View File
@@ -3,7 +3,13 @@ pipeline {
stages { stages {
stage('Gradle') { stage('Gradle') {
steps { steps {
sh 'bash gradlew build' withCredentials([usernamePassword(credentialsId: 'alttd-snapshot-user', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh '''
set +x
chmod +x gradlew
./gradlew build -PalttdSnapshotUsername=$USERNAME -PalttdSnapshotPassword=$PASSWORD
'''
}
} }
} }
stage('Archive') { stage('Archive') {
+12 -10
View File
@@ -1,6 +1,7 @@
plugins { plugins {
id("java") id("java")
id("maven-publish") id("maven-publish")
id("com.github.ben-manes.versions") version "0.52.0"
} }
group = "com.alttd" group = "com.alttd"
@@ -9,12 +10,6 @@ description = "Altitude's Transfer Items plugin"
apply<JavaLibraryPlugin>() apply<JavaLibraryPlugin>()
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
tasks { tasks {
withType<JavaCompile> { withType<JavaCompile> {
options.encoding = Charsets.UTF_8.name() options.encoding = Charsets.UTF_8.name()
@@ -38,7 +33,14 @@ tasks {
} }
dependencies { dependencies {
compileOnly("com.alttd:Galaxy-API:1.21-R0.1-SNAPSHOT") { compileOnly("com.alttd.cosmos:cosmos-api:26.2.build.17-stable")
isChanging = true
} implementation("org.slf4j:slf4j-api:2.0.17")
}
compileOnly("org.projectlombok:lombok:1.18.38")
annotationProcessor("org.projectlombok:lombok:1.18.38")
implementation(platform("com.intellectualsites.bom:bom-newest:1.56"))
compileOnly("com.intellectualsites.plotsquared:plotsquared-core")
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false }
}
Binary file not shown.
+1 -2
View File
@@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+7 -12
View File
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,11 +80,14 @@ do
esac esac
done done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -140,16 +143,12 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -194,10 +193,6 @@ if "$cygwin" || "$msys" ; then
done done
fi 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; # Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in # shell script including quotes and variable substitutions, so put them in
Vendored
-1
View File
@@ -26,7 +26,6 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
+21 -3
View File
@@ -1,11 +1,29 @@
rootProject.name = "PlayerUtils" rootProject.name = "PlayerUtils"
val nexusUser = providers.gradleProperty("alttdSnapshotUsername").orNull ?: System.getenv("NEXUS_USERNAME")
val nexusPass = providers.gradleProperty("alttdSnapshotPassword").orNull ?: System.getenv("NEXUS_PASSWORD")
dependencyResolutionManagement { dependencyResolutionManagement {
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy maven {
maven("'https://jitpack.io'") // Vault url = uri("https://repo.alttd.com/repository/alttd-snapshot/")
credentials {
username = nexusUser
password = nexusPass
}
}
maven {
url = uri("https://repo.alttd.com/repository/alttd/")
credentials {
username = nexusUser
password = nexusPass
}
}
maven("https://jitpack.io")
// PlotSquared
maven("https://repo.papermc.io/repository/maven-public/")
} }
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
} }
@@ -14,4 +32,4 @@ pluginManagement {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
} }
} }
@@ -1,59 +1,77 @@
package com.alttd.playerutils; package com.alttd.playerutils;
import com.alttd.playerutils.commands.PlayerUtilsCommand; import com.alttd.playerutils.commands.PlayerUtilsCommand;
import com.alttd.playerutils.commands.playerutils_subcommands.GhastSpeed;
import com.alttd.playerutils.commands.playerutils_subcommands.RandomPlot;
import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock; import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock;
import com.alttd.playerutils.config.Config; import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.config.KeyStorage; import com.alttd.playerutils.config.KeyStorage;
import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.event_listeners.GoatHornEvent; import com.alttd.playerutils.event_listeners.*;
import com.alttd.playerutils.event_listeners.RotateBlockEvent; import lombok.extern.slf4j.Slf4j;
import com.alttd.playerutils.event_listeners.TeleportEvent;
import com.alttd.playerutils.event_listeners.XpBottleEvent;
import com.alttd.playerutils.util.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Slf4j
public final class PlayerUtils extends JavaPlugin { public final class PlayerUtils extends JavaPlugin {
private Logger logger;
private PlayerUtilsCommand playerUtilsCommand; private PlayerUtilsCommand playerUtilsCommand;
@Override @Override
public void onEnable() { public void onEnable() {
this.logger = new Logger(getLogger());
registerCommands();
registerEvents();
reloadConfigs(); reloadConfigs();
registerCommands();
registerRandomPlot();
registerEvents();
registerSchedulers(); registerSchedulers();
} }
private void registerRandomPlot() {
if (!getServer().getPluginManager().isPluginEnabled("PlotSquared")) {
log.warn("PlotSquared not found — random plot command will not be registered.");
return;
}
playerUtilsCommand.addSubCommand(new RandomPlot(this));
log.info("PlotSquared found - registered random plot command.");
}
@Override @Override
public void onDisable() { public void onDisable() {
KeyStorage.STORAGE.save(); KeyStorage.STORAGE.save();
} }
private void registerCommands() { private void registerCommands() {
playerUtilsCommand = new PlayerUtilsCommand(this, logger); playerUtilsCommand = new PlayerUtilsCommand(this);
} }
private void registerEvents() { private void registerEvents() {
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new XpBottleEvent(this, logger), this); pluginManager.registerEvents(new XpBottleEvent(this), this);
pluginManager.registerEvents(new TeleportEvent(), this); pluginManager.registerEvents(new TeleportEvent(), this);
pluginManager.registerEvents(new GoatHornEvent(logger), this); pluginManager.registerEvents(new GoatHornEvent(), this);
pluginManager.registerEvents(new LimitArmorStands(this), this);
pluginManager.registerEvents(new BlockBlockUseEvent(), this);
pluginManager.registerEvents(new PlayerJoin(this), this);
pluginManager.registerEvents(new BookWriteEvent(), this);
pluginManager.registerEvents(new BookByteLimitListener(), this);
pluginManager.registerEvents(new VillagerWorkstationEvent(this), this);
RotateBlockEvent rotateBlockEvent = new RotateBlockEvent(logger); RotateBlockEvent rotateBlockEvent = new RotateBlockEvent();
pluginManager.registerEvents(rotateBlockEvent, this); pluginManager.registerEvents(rotateBlockEvent, this);
playerUtilsCommand.addSubCommand(new RotateBlock(rotateBlockEvent)); playerUtilsCommand.addSubCommand(new RotateBlock(rotateBlockEvent));
GhastSpeedEvent ghastSpeedEvent = new GhastSpeedEvent();
pluginManager.registerEvents(ghastSpeedEvent, this);
playerUtilsCommand.addSubCommand(new GhastSpeed(ghastSpeedEvent));
} }
public void reloadConfigs() { public void reloadConfigs() {
Config.reload(logger); Config.reload();
Messages.reload(logger); Messages.reload();
KeyStorage.reload(logger); KeyStorage.reload();
} }
private void registerSchedulers() { private void registerSchedulers() {
@@ -3,26 +3,26 @@ package com.alttd.playerutils.commands;
import com.alttd.playerutils.PlayerUtils; import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.commands.playerutils_subcommands.*; import com.alttd.playerutils.commands.playerutils_subcommands.*;
import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.event_listeners.RotateBlockEvent; import lombok.Getter;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.command.*; import org.bukkit.command.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Getter
public class PlayerUtilsCommand implements CommandExecutor, TabExecutor { public class PlayerUtilsCommand implements CommandExecutor, TabExecutor {
private final List<SubCommand> subCommands; private final List<SubCommand> subCommands;
public PlayerUtilsCommand(PlayerUtils playerUtils, Logger logger) { public PlayerUtilsCommand(PlayerUtils playerUtils) {
PluginCommand command = playerUtils.getCommand("playerutils"); PluginCommand command = playerUtils.getCommand("playerutils");
if (command == null) { if (command == null) {
subCommands = null; subCommands = null;
logger.severe("Unable to find playerutils command."); log.error("Unable to find playerutils command.");
return; return;
} }
command.setExecutor(this); command.setExecutor(this);
@@ -30,21 +30,22 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor {
command.setAliases(List.of("pu")); command.setAliases(List.of("pu"));
subCommands = new ArrayList<>(List.of( subCommands = new ArrayList<>(List.of(
new Glow(logger), new Glow(),
new XPCheque(playerUtils), new XPCheque(playerUtils),
new XPCalc(), new XPCalc(),
new Reload(playerUtils), new Reload(playerUtils),
new Key(logger)) new Key(),
new ReCountArmorStands(playerUtils))
); );
} }
@Override @Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String cmd, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String cmd, @NotNull String[] args) {
if (args.length == 0) { if (args.length == 0) {
commandSender.sendMiniMessage(Messages.HELP.HELP_MESSAGE_WRAPPER.replaceAll("<commands>", subCommands.stream() commandSender.sendRichMessage(Messages.HELP.HELP_MESSAGE_WRAPPER.replaceAll("<commands>", subCommands.stream()
.filter(subCommand -> commandSender.hasPermission(subCommand.getPermission())) .filter(subCommand -> commandSender.hasPermission(subCommand.getPermission()))
.map(SubCommand::getHelpMessage) .map(SubCommand::getHelpMessage)
.collect(Collectors.joining("\n"))), null); .collect(Collectors.joining("\n"))));
return true; return true;
} }
@@ -53,13 +54,13 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor {
return false; return false;
if (!commandSender.hasPermission(subCommand.getPermission())) { if (!commandSender.hasPermission(subCommand.getPermission())) {
commandSender.sendMiniMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", subCommand.getPermission())); commandSender.sendRichMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", subCommand.getPermission()));
return true; return true;
} }
boolean executedCorrectly = subCommand.onCommand(commandSender, args); boolean executedCorrectly = subCommand.onCommand(commandSender, args);
if (!executedCorrectly) { if (!executedCorrectly) {
commandSender.sendMiniMessage(subCommand.getHelpMessage(), null); commandSender.sendRichMessage(subCommand.getHelpMessage());
} }
return true; return true;
} }
@@ -85,10 +86,6 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor {
return res; return res;
} }
public List<SubCommand> getSubCommands() {
return subCommands;
}
private SubCommand getSubCommand(String cmdName) { private SubCommand getSubCommand(String cmdName) {
return subCommands.stream() return subCommands.stream()
.filter(subCommand -> subCommand.getName().equals(cmdName)) .filter(subCommand -> subCommand.getName().equals(cmdName))
@@ -102,4 +99,4 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor {
} }
subCommands.add(subCommand); subCommands.add(subCommand);
} }
} }
@@ -0,0 +1,11 @@
package com.alttd.playerutils.commands.argument_parser;
import org.bukkit.command.CommandSender;
import java.util.Optional;
public interface ArgumentParser<T> {
Optional<T> parse(CommandSender commandSender, String argument);
}
@@ -0,0 +1,23 @@
package com.alttd.playerutils.commands.argument_parser;
import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.data_objects.GHAST_SPEED;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
public class GhastSpeedParser implements ArgumentParser<GHAST_SPEED> {
@Override
public Optional<GHAST_SPEED> parse(CommandSender commandSender, String speed) {
GHAST_SPEED ghastSpeed;
try {
ghastSpeed = GHAST_SPEED.valueOf(speed.toUpperCase());
} catch (IllegalArgumentException e) {
return Optional.empty();
}
return Optional.of(ghastSpeed);
}
}
@@ -0,0 +1,21 @@
package com.alttd.playerutils.commands.argument_parser;
import com.alttd.playerutils.config.Messages;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
public class OnlinePlayerParser implements ArgumentParser<Player> {
@Override
public Optional<Player> parse(CommandSender commandSender, String playerName) {
Player player = commandSender.getServer().getPlayer(playerName);
if (player == null || !player.isOnline()) {
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_NOT_FOUND, Placeholder.parsed("player", playerName));
return Optional.empty();
}
return Optional.of(player);
}
}
@@ -0,0 +1,90 @@
package com.alttd.playerutils.commands.playerutils_subcommands;
import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.commands.argument_parser.GhastSpeedParser;
import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.data_objects.GHAST_SPEED;
import com.alttd.playerutils.event_listeners.GhastSpeedEvent;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.HappyGhast;
import org.bukkit.entity.Player;
import java.util.*;
public class GhastSpeed extends SubCommand {
private final static GhastSpeedParser GHAST_SPEED_PARSER = new GhastSpeedParser();
private final static int GHAST_SPEED_ARG = 1;
private final GhastSpeedEvent ghastSpeedEvent;
public GhastSpeed(GhastSpeedEvent ghastSpeedEvent) {
this.ghastSpeedEvent = ghastSpeedEvent;
}
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (args.length != 2) {
return false;
}
if (!(commandSender instanceof Player player)) {
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true;
}
Optional<GHAST_SPEED> parsedGhastSpeed = GHAST_SPEED_PARSER.parse(commandSender, args[GHAST_SPEED_ARG]);
if (parsedGhastSpeed.isEmpty()) {
return false;
}
GHAST_SPEED ghastSpeed = parsedGhastSpeed.get();
if (!(player.getVehicle() instanceof HappyGhast happyGhast)) {
commandSender.sendRichMessage(Messages.GHAST_SPEED.NOT_RIDING_A_GHAST);
return true;
}
AttributeInstance attribute = happyGhast.getAttribute(Attribute.FLYING_SPEED);
if (attribute == null) {
commandSender.sendRichMessage(Messages.GHAST_SPEED.FAILED_TO_SET_SPEED);
return true;
}
if (!commandSender.hasPermission(getPermission() + "." + ghastSpeed.name().toLowerCase())) {
commandSender.sendRichMessage(Messages.GENERIC.NO_PERMISSION,
Placeholder.parsed("permission", getPermission() + "." +
ghastSpeed.name().toLowerCase()));
return true;
}
double newSpeed = GHAST_SPEED.getSpeed(ghastSpeed);
ghastSpeedEvent.setNewSpeed(player.getUniqueId(), ghastSpeed);
attribute.setBaseValue(newSpeed);
commandSender.sendRichMessage(Messages.GHAST_SPEED.NEW_SPEED_SET_TO,
Placeholder.parsed("speed", ghastSpeed.name().toLowerCase().replace("_", " ")));
return true;
}
@Override
public String getName() {
return "ghastspeed";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
if (args.length == 2) {
return Arrays.stream(GHAST_SPEED.values())
.map(GHAST_SPEED::name)
.filter(name -> commandSender.hasPermission(
getPermission() + "." + name.toLowerCase())).toList();
}
return List.of();
}
@Override
public String getHelpMessage() {
return Messages.HELP.GHAST_SPEED;
}
}
@@ -2,7 +2,7 @@ package com.alttd.playerutils.commands.playerutils_subcommands;
import com.alttd.playerutils.commands.SubCommand; import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
@@ -19,13 +19,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Glow extends SubCommand { @Slf4j public class Glow extends SubCommand {
private final Logger logger;
public Glow(Logger logger) {
this.logger = logger;
}
@Override @Override
public boolean onCommand(CommandSender commandSender, String[] args) { public boolean onCommand(CommandSender commandSender, String[] args) {
@@ -41,20 +35,20 @@ public class Glow extends SubCommand {
Player player = playerFromArg.get(); Player player = playerFromArg.get();
if (args[1].equalsIgnoreCase("off")) {
turnOffGlow(commandSender, player, otherPlayer);
return true;
}
Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard(); Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard();
board.getTeams().stream() board.getTeams().stream()
.filter(team -> team.getName().startsWith("Glow")) .filter(team -> team.getName().startsWith("Glow"))
.filter(team -> team.hasPlayer(player)) .filter(team -> team.hasPlayer(player))
.forEach(team -> team.removePlayer(player)); .forEach(team -> team.removePlayer(player));
if (args[1].equalsIgnoreCase("off")) {
turnOffGlow(commandSender, player, otherPlayer, board);
return true;
}
Optional<DyeColor> any = Arrays.stream(DyeColor.values()).filter(chatColor -> chatColor.name().equalsIgnoreCase(args[1])).findAny(); Optional<DyeColor> any = Arrays.stream(DyeColor.values()).filter(chatColor -> chatColor.name().equalsIgnoreCase(args[1])).findAny();
if (any.isEmpty()) { if (any.isEmpty()) {
commandSender.sendMiniMessage(getHelpMessage(), null); commandSender.sendRichMessage(getHelpMessage());
return true; return true;
} }
@@ -75,37 +69,43 @@ public class Glow extends SubCommand {
private Optional<Player> getTargetPlayer(CommandSender commandSender, String[] args) { private Optional<Player> getTargetPlayer(CommandSender commandSender, String[] args) {
if (args.length == 2) { if (args.length == 2) {
if (!(commandSender instanceof Player commandPlayer)) { if (!(commandSender instanceof Player commandPlayer)) {
commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_ONLY, null); commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return Optional.empty(); return Optional.empty();
} }
return Optional.of(commandPlayer); return Optional.of(commandPlayer);
} }
if (!commandSender.hasPermission(getPermission() + ".other")) { if (!commandSender.hasPermission(getPermission() + ".other")) {
commandSender.sendMiniMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", getPermission() + ".other")); commandSender.sendRichMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", getPermission() + ".other"));
return Optional.empty(); return Optional.empty();
} }
Optional<? extends Player> any = Bukkit.getOnlinePlayers().stream().filter(offlinePlayer -> offlinePlayer.getName().equalsIgnoreCase(args[2])).findAny(); Optional<? extends Player> any = Bukkit.getOnlinePlayers().stream().filter(offlinePlayer -> offlinePlayer.getName().equalsIgnoreCase(args[2])).findAny();
if (any.isPresent()) { if (any.isPresent()) {
return Optional.of(any.get()); return Optional.of(any.get());
} else { } else {
commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_NOT_FOUND, Placeholder.parsed("player", args[2])); commandSender.sendRichMessage(Messages.GENERIC.PLAYER_NOT_FOUND, Placeholder.parsed("player", args[2]));
return Optional.empty(); return Optional.empty();
} }
} }
private void turnOffGlow(CommandSender commandSender, Player player, boolean otherPlayer) { private void turnOffGlow(CommandSender commandSender, Player player, boolean otherPlayer, Scoreboard board) {
player.sendMiniMessage(Messages.GLOW.GLOW_OFF, null); player.sendRichMessage(Messages.GLOW.GLOW_OFF);
player.setGlowing(false); player.setGlowing(false);
board.getTeams().stream()
.filter(team -> team.getName().startsWith("Glow-"))
.filter(team -> team.hasPlayer(player))
.forEach(team -> team.removePlayer(player));
if (otherPlayer) { if (otherPlayer) {
commandSender.sendMiniMessage(Messages.GLOW.GLOW_OFF_FOR_PLAYER, Placeholder.component("player", player.name())); commandSender.sendRichMessage(Messages.GLOW.GLOW_OFF_FOR_PLAYER, Placeholder.component("player", player.name()));
} }
} }
private void turnOnGlow(CommandSender commandSender, Player player, Team team, DyeColor dyeColor, boolean otherPlayer) { private void turnOnGlow(CommandSender commandSender, Player player, Team team, DyeColor dyeColor, boolean otherPlayer) {
if (team.getScoreboard() == null) { if (team.getScoreboard() == null) {
commandSender.sendMiniMessage(Messages.GLOW.UNABLE_TO_GET_SCOREBOARD, null); commandSender.sendRichMessage(Messages.GLOW.UNABLE_TO_GET_SCOREBOARD);
logger.warning("Unable to get scoreboard for team"); log.warn("Unable to get scoreboard for team");
return; return;
} }
@@ -114,9 +114,9 @@ public class Glow extends SubCommand {
player.setScoreboard(team.getScoreboard()); player.setScoreboard(team.getScoreboard());
player.setGlowing(true); player.setGlowing(true);
player.sendMiniMessage(Messages.GLOW.GLOW_ON, Placeholder.parsed("color", dyeColor.name())); player.sendRichMessage(Messages.GLOW.GLOW_ON, Placeholder.parsed("color", dyeColor.name()));
if (otherPlayer) { if (otherPlayer) {
commandSender.sendMiniMessage(Messages.GLOW.GLOW_ON_FOR_PLAYER, TagResolver.resolver( commandSender.sendRichMessage(Messages.GLOW.GLOW_ON_FOR_PLAYER, TagResolver.resolver(
Placeholder.parsed("color", dyeColor.name()), Placeholder.parsed("color", dyeColor.name()),
Placeholder.component("player", player.name()) Placeholder.component("player", player.name())
)); ));
@@ -4,8 +4,8 @@ import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.config.Config; import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.config.KeyStorage; import com.alttd.playerutils.config.KeyStorage;
import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.util.Logger;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -15,18 +15,12 @@ import org.bukkit.entity.Player;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public class Key extends SubCommand { @Slf4j public class Key extends SubCommand {
private final Logger logger;
public Key(Logger logger) {
this.logger = logger;
}
@Override @Override
public boolean onCommand(CommandSender commandSender, String[] args) { public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_ONLY, null); commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true; return true;
} }
@@ -36,7 +30,7 @@ public class Key extends SubCommand {
String crate = args[1].toLowerCase(); String crate = args[1].toLowerCase();
if (!Config.KEY.CRATES.containsKey(crate)) { if (!Config.KEY.CRATES.containsKey(crate)) {
commandSender.sendMiniMessage(Messages.KEY.CRATE_NOT_EXIST, Placeholder.parsed("crate", crate)); commandSender.sendRichMessage(Messages.KEY.CRATE_NOT_EXIST, Placeholder.parsed("crate", crate));
return true; return true;
} }
@@ -45,21 +39,21 @@ public class Key extends SubCommand {
int totalKeys = Config.KEY.CRATES.getOrDefault(crate, 0); int totalKeys = Config.KEY.CRATES.getOrDefault(crate, 0);
int keys = crateMap.getOrDefault(uuid, 0); int keys = crateMap.getOrDefault(uuid, 0);
if (keys >= totalKeys) { if (keys >= totalKeys) {
commandSender.sendMiniMessage(Messages.KEY.RETRIEVED_ALL_KEYS, TagResolver.resolver( commandSender.sendRichMessage(Messages.KEY.RETRIEVED_ALL_KEYS, TagResolver.resolver(
Placeholder.parsed("crate", crate), Placeholder.parsed("crate", crate),
Placeholder.parsed("keys", String.valueOf(keys)))); Placeholder.parsed("keys", String.valueOf(keys))));
return true; return true;
} }
crateMap.addTo(uuid, 1); crateMap.addTo(uuid, 1);
logger.info(String.format("Gave %s one key for %s", player.getName(), crate)); log.info("Gave {} one key for {}", player.getName(), crate);
commandSender.getServer().dispatchCommand(Bukkit.getConsoleSender(), String.format("crate give v %s 1 %s", crate, player.getName())); commandSender.getServer().dispatchCommand(Bukkit.getConsoleSender(), String.format("crate give v %s 1 %s", crate, player.getName()));
if (keys + 1 == totalKeys) { if (keys + 1 == totalKeys) {
commandSender.sendMiniMessage(Messages.KEY.GAVE_FINAL_KEY, TagResolver.resolver( commandSender.sendRichMessage(Messages.KEY.GAVE_FINAL_KEY, TagResolver.resolver(
Placeholder.parsed("crate", crate), Placeholder.parsed("crate", crate),
Placeholder.parsed("keys", String.valueOf(keys + 1)))); Placeholder.parsed("keys", String.valueOf(keys + 1))));
} else { } else {
commandSender.sendMiniMessage(Messages.KEY.GAVE_KEY, TagResolver.resolver( commandSender.sendRichMessage(Messages.KEY.GAVE_KEY, TagResolver.resolver(
Placeholder.parsed("crate", crate), Placeholder.parsed("crate", crate),
Placeholder.parsed("keys", String.valueOf(keys + 1)), Placeholder.parsed("keys", String.valueOf(keys + 1)),
Placeholder.parsed("total_keys", String.valueOf(totalKeys)))); Placeholder.parsed("total_keys", String.valueOf(totalKeys))));
@@ -0,0 +1,181 @@
package com.alttd.playerutils.commands.playerutils_subcommands;
import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.config.Messages;
import com.plotsquared.bukkit.player.BukkitPlayer;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.events.TeleportCause;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.title.Title;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
@Slf4j
@RequiredArgsConstructor
public class RandomPlot extends SubCommand {
private static final String PERMISSION = "playerutils.randomplot";
private final PlayerUtils plugin;
private final MiniMessage miniMessage = MiniMessage.miniMessage();
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) {
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true;
}
String worldName = player.getWorld().getName();
if (!Config.RANDOM_PLOT.ALLOWED_WORLDS.contains(worldName)) {
player.sendRichMessage(Messages.RANDOM_PLOT.WORLD_NOT_ALLOWED);
return true;
}
if (!player.hasPermission(PERMISSION)) {
player.sendRichMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", PERMISSION));
return true;
}
if (!Bukkit.getPluginManager().isPluginEnabled("PlotSquared")) {
player.sendRichMessage(Messages.RANDOM_PLOT.PLOT_SQUARED_NOT_ENABLED);
return true;
}
List<Plot> plots = collectPlots(worldName);
if (plots.isEmpty()) {
player.sendRichMessage(Messages.RANDOM_PLOT.NO_PLOTS_FOUND);
return true;
}
Plot target = plots.get(ThreadLocalRandom.current().nextInt(plots.size()));
player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_START);
startCountdown(player, target);
return true;
}
// ── PlotSquared helpers ───────────────────────────────────────────────────
private List<Plot> collectPlots(String worldName) {
try {
return PlotSquared.get().getPlotAreaManager().getPlotAreasSet(worldName)
.stream()
.map(PlotArea::getPlots)
.flatMap(Collection::stream)
.toList();
} catch (Exception e) {
log.error("Failed to retrieve plots from PlotSquared for world '{}'", worldName, e);
}
return List.of();
}
private void teleportToPlot(Player player, Plot plot) {
try {
BukkitPlayer bPlayer = BukkitUtil.adapt(player);
plot.teleportPlayer(bPlayer, TeleportCause.PLUGIN, success -> {
if (Boolean.TRUE.equals(success)) {
player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_SUCCESS,
Placeholder.parsed("plot_id", plot.getId().toSeparatedString(";")),
Placeholder.parsed("plot_owner", getOwner(plot)));
}
});
} catch (Exception e) {
log.error("Failed to teleport {} to plot {}", player.getName(), plot.getId(), e);
}
}
private String getOwner(Plot plot) {
String ownerName;
UUID owner = plot.getOwner();
if (owner == null) {
ownerName = "Unknown";
} else {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner);
ownerName = offlinePlayer.getName();
if (ownerName == null) {
ownerName = "Unknown";
}
}
return ownerName;
}
// ── Countdown ─────────────────────────────────────────────────────────────
private void startCountdown(Player player, Plot plot) {
final Location origin = player.getLocation().clone();
final int[] secondsLeft = {Config.RANDOM_PLOT.COUNTDOWN_SECONDS};
new BukkitRunnable() {
@Override
public void run() {
if (!player.isOnline()) {
cancel();
return;
}
// Movement check — cancel if player moved more than 0.5 blocks on any axis
Location current = player.getLocation();
if (Math.abs(current.getX() - origin.getX()) > 0.5
|| Math.abs(current.getY() - origin.getY()) > 0.5
|| Math.abs(current.getZ() - origin.getZ()) > 0.5) {
cancel();
player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_CANCELLED);
return;
}
if (secondsLeft[0] <= 0) {
cancel();
teleportToPlot(player, plot);
return;
}
// Show countdown title for current second
String rawTitle = Config.RANDOM_PLOT.COUNTDOWN_TITLES
.getOrDefault(secondsLeft[0], String.valueOf(secondsLeft[0]));
Component title = miniMessage.deserialize(rawTitle);
Component subtitle = miniMessage.deserialize(Config.RANDOM_PLOT.COUNTDOWN_SUBTITLE);
player.showTitle(Title.title(
title,
subtitle,
Title.Times.times(Duration.ZERO, Duration.ofMillis(1200), Duration.ofMillis(200))
));
secondsLeft[0]--;
}
}.runTaskTimer(plugin, 0L, 20L);
}
@Override
public String getName() {
return "randomplot";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return List.of();
}
@Override
public String getHelpMessage() {
return Messages.HELP.RANDOM_PLOT;
}
}
@@ -0,0 +1,75 @@
package com.alttd.playerutils.commands.playerutils_subcommands;
import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.config.Messages;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Chunk;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@Slf4j
@RequiredArgsConstructor
public class ReCountArmorStands extends SubCommand {
private final PlayerUtils playerUtils;
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) {
return false;
}
Chunk chunk = player.getLocation().getChunk();
Optional<Integer> armorStandCount = recountArmorStands(chunk);
if (armorStandCount.isEmpty()) {
player.sendRichMessage(Messages.RECOUNT_ARMOR_STANDS.FAILED_COUNT);
return true;
}
player.sendRichMessage(Messages.RECOUNT_ARMOR_STANDS.SUCCESS, Placeholder.parsed("count", String.valueOf(armorStandCount.get())));
return true;
}
@Override
public String getName() {
return "recount_armor_stands";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return List.of();
}
@Override
public String getHelpMessage() {
return Messages.HELP.RECOUNT_ARMOR_STANDS;
}
private Optional<Integer> recountArmorStands(Chunk chunk) {
NamespacedKey namespacedKey = NamespacedKey.fromString("armor_stand_count", playerUtils);
if (namespacedKey == null) {
log.warn("Unable to retrieve name spaced key for armor stand count.");
return Optional.empty();
}
PersistentDataContainer persistentDataContainer = chunk.getPersistentDataContainer();
int armorStands = countArmorStands(chunk);
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, armorStands);
return Optional.of(armorStands);
}
private int countArmorStands(Chunk chunk) {
return (int) Arrays.stream(chunk.getEntities())
.filter(entity -> entity.getType().equals(EntityType.ARMOR_STAND))
.count();
}
}
@@ -18,7 +18,7 @@ public class Reload extends SubCommand {
@Override @Override
public boolean onCommand(CommandSender commandSender, String[] args) { public boolean onCommand(CommandSender commandSender, String[] args) {
playerUtils.reloadConfigs(); playerUtils.reloadConfigs();
commandSender.sendMiniMessage(Messages.RELOAD.RELOADED, null); commandSender.sendRichMessage(Messages.RELOAD.RELOADED);
return true; return true;
} }
@@ -19,15 +19,15 @@ public class RotateBlock extends SubCommand {
@Override @Override
public boolean onCommand(CommandSender commandSender, String[] args) { public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_ONLY, null); commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true; return true;
} }
boolean enabled = rotateBlockEvent.toggleRotate(player.getUniqueId()); boolean enabled = rotateBlockEvent.toggleRotate(player.getUniqueId());
if (enabled) if (enabled)
commandSender.sendMiniMessage(Messages.ROTATE_BLOCK.ENABLED, null); commandSender.sendRichMessage(Messages.ROTATE_BLOCK.ENABLED);
else else
commandSender.sendMiniMessage(Messages.ROTATE_BLOCK.DISABLED, null); commandSender.sendRichMessage(Messages.ROTATE_BLOCK.DISABLED);
return true; return true;
} }
@@ -34,7 +34,7 @@ public class XPCalc extends SubCommand {
startingLevel++; startingLevel++;
} }
commandSender.sendMiniMessage(Messages.XP_CALC.XP_NEEDED, Placeholder.parsed("xp_needed", String.valueOf(totalXpNeeded))); commandSender.sendRichMessage(Messages.XP_CALC.XP_NEEDED, Placeholder.parsed("xp_needed", String.valueOf(totalXpNeeded)));
return true; return true;
} }
@@ -31,7 +31,7 @@ public class XPCheque extends SubCommand {
@Override @Override
public boolean onCommand(CommandSender commandSender, String[] args) { public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_ONLY, null); commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true; return true;
} }
@@ -48,7 +48,7 @@ public class XPCheque extends SubCommand {
} }
if (amount > 64 || amount < 1) { if (amount > 64 || amount < 1) {
commandSender.sendMiniMessage(Messages.XP_CHEQUE.INVALID_AMOUNT, null); commandSender.sendRichMessage(Messages.XP_CHEQUE.INVALID_AMOUNT);
return true; return true;
} }
@@ -60,24 +60,24 @@ public class XPCheque extends SubCommand {
} }
if ((xpValue <= 0)) { if ((xpValue <= 0)) {
commandSender.sendMiniMessage(Messages.XP_CHEQUE.NEGATIVE, null); commandSender.sendRichMessage(Messages.XP_CHEQUE.NEGATIVE);
return true; return true;
} }
int totalExperience = player.getTotalExperience(); int totalExperience = player.calculateTotalExperiencePoints();
if (totalExperience < (xpValue * amount)) { if (totalExperience < (xpValue * amount)) {
commandSender.sendMiniMessage(Messages.XP_CHEQUE.NOT_ENOUGH_XP, Placeholder.parsed("xp", String.valueOf(totalExperience))); commandSender.sendRichMessage(Messages.XP_CHEQUE.NOT_ENOUGH_XP, Placeholder.parsed("xp", String.valueOf(totalExperience)));
return true; return true;
} }
ItemStack heldItem = player.getInventory().getItemInMainHand(); ItemStack heldItem = player.getInventory().getItemInMainHand();
if (!heldItem.getType().equals(Material.GLASS_BOTTLE)) { if (!heldItem.getType().equals(Material.GLASS_BOTTLE)) {
commandSender.sendMiniMessage(Messages.XP_CHEQUE.NOT_HOLDING_BOTTLE, null); commandSender.sendRichMessage(Messages.XP_CHEQUE.NOT_HOLDING_BOTTLE);
return true; return true;
} }
if (heldItem.getAmount() < amount) { if (heldItem.getAmount() < amount) {
commandSender.sendMiniMessage(Messages.XP_CHEQUE.NOT_ENOUGH_BOTTLE, Placeholder.parsed("amount", String.valueOf(amount))); commandSender.sendRichMessage(Messages.XP_CHEQUE.NOT_ENOUGH_BOTTLE, Placeholder.parsed("amount", String.valueOf(amount)));
return true; return true;
} }
@@ -122,7 +122,7 @@ public class XPCheque extends SubCommand {
NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils); NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils);
if (customXp == null) { if (customXp == null) {
player.sendMiniMessage(Messages.XP_CHEQUE.FAILED_STORAGE, null); player.sendRichMessage(Messages.XP_CHEQUE.FAILED_STORAGE);
return Optional.empty(); return Optional.empty();
} }
persistentDataContainer.set(customXp, PersistentDataType.INTEGER, xpValue); persistentDataContainer.set(customXp, PersistentDataType.INTEGER, xpValue);
@@ -139,7 +139,7 @@ public class XPCheque extends SubCommand {
} }
public void decreaseExperience(Player player, int xpToRemove) { public void decreaseExperience(Player player, int xpToRemove) {
int totalExp = player.getTotalExperience(); int totalExp = player.calculateTotalExperiencePoints();
int newTotalExp = Math.max(totalExp - xpToRemove, 0); int newTotalExp = Math.max(totalExp - xpToRemove, 0);
int level = 0; int level = 0;
@@ -153,7 +153,7 @@ public class XPCheque extends SubCommand {
float progress = (float) newTotalExp / getExpToNext(level); float progress = (float) newTotalExp / getExpToNext(level);
player.setTotalExperience(totalExp - xpToRemove); player.setExperienceLevelAndProgress(totalExp - xpToRemove);
player.setLevel(level); player.setLevel(level);
player.setExp(progress); player.setExp(progress);
} }
@@ -1,8 +1,8 @@
package com.alttd.playerutils.config; package com.alttd.playerutils.config;
import com.alttd.playerutils.PlayerUtils; import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.util.Logger;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
@@ -19,19 +19,16 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@SuppressWarnings({"unused", "SameParameterValue"}) @Slf4j @SuppressWarnings({"unused", "SameParameterValue"})
abstract class AbstractConfig { abstract class AbstractConfig {
File file; File file;
YamlConfiguration yaml; YamlConfiguration yaml;
private static Logger logger = null;
AbstractConfig(PlayerUtils playerUtils, String filename, Logger logger) { AbstractConfig(PlayerUtils playerUtils, String filename) {
AbstractConfig.logger = logger;
init(new File(playerUtils.getDataFolder(), filename), filename); init(new File(playerUtils.getDataFolder(), filename), filename);
} }
AbstractConfig(File file, String filename, Logger logger) { AbstractConfig(File file, String filename) {
AbstractConfig.logger = logger;
init(new File(file.getPath() + File.separator + filename), filename); init(new File(file.getPath() + File.separator + filename), filename);
} }
@@ -41,10 +38,9 @@ abstract class AbstractConfig {
try { try {
yaml.load(file); yaml.load(file);
} catch (IOException ignore) { } catch (IOException ignore) {
} catch (InvalidConfigurationException ex) { } catch (InvalidConfigurationException e) {
if (logger != null) log.error("Could not load {}, please correct your syntax errors", filename, e);
logger.severe(String.format("Could not load %s, please correct your syntax errors", filename)); throw new RuntimeException(e);
throw new RuntimeException(ex);
} }
yaml.options().copyDefaults(true); yaml.options().copyDefaults(true);
} }
@@ -59,10 +55,8 @@ abstract class AbstractConfig {
method.invoke(instance); method.invoke(instance);
} catch (InvocationTargetException ex) { } catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause()); throw new RuntimeException(ex.getCause());
} catch (Exception ex) { } catch (Exception e) {
if (logger != null) log.error("Error invoking {}.", method, e);
logger.severe("Error invoking %.", method.toString());
ex.printStackTrace();
} }
} }
} }
@@ -75,10 +69,8 @@ abstract class AbstractConfig {
private void save() { private void save() {
try { try {
yaml.save(file); yaml.save(file);
} catch (IOException ex) { } catch (IOException e) {
if (logger != null) log.error("Could not save {}.", file.toString(), e);
logger.severe("Could not save %.", file.toString());
ex.printStackTrace();
} }
} }
@@ -150,4 +142,4 @@ abstract class AbstractConfig {
ConfigurationSection getConfigurationSection(String path) { ConfigurationSection getConfigurationSection(String path) {
return yaml.getConfigurationSection(path); return yaml.getConfigurationSection(path);
} }
} }
@@ -1,6 +1,6 @@
package com.alttd.playerutils.config; package com.alttd.playerutils.config;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.io.File; import java.io.File;
@@ -8,24 +8,23 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public class Config extends AbstractConfig{ @Slf4j
public class Config extends AbstractConfig {
static Config config; static Config config;
private Logger logger;
Config(Logger logger) { Config() {
super( super(
new File(File.separator new File(File.separator
+ "mnt" + File.separator + "mnt" + File.separator
+ "configs" + File.separator + "configs" + File.separator
+ "PlayerUtils"), + "PlayerUtils"),
"config.yml", logger); "config.yml");
this.logger = logger;
} }
public static void reload(Logger logger) { public static void reload() {
logger.info("Reloading config"); log.info("Reloading config");
config = new Config(logger); config = new Config();
config.readConfig(Config.class, null); config.readConfig(Config.class, null);
} }
@@ -50,7 +49,7 @@ public class Config extends AbstractConfig{
CRATES.clear(); CRATES.clear();
ConfigurationSection configurationSection = config.getConfigurationSection(prefix.substring(0, prefix.length() - 1)); ConfigurationSection configurationSection = config.getConfigurationSection(prefix.substring(0, prefix.length() - 1));
if (configurationSection == null) { if (configurationSection == null) {
config.logger.warning("No keys configured, adding default"); log.warn("No keys configured, adding default");
config.set(prefix, "dailyvotecrate", 0); config.set(prefix, "dailyvotecrate", 0);
config.set(prefix, "weeklyvotecrate", 0); config.set(prefix, "weeklyvotecrate", 0);
config.set(prefix, "questcrate", 0); config.set(prefix, "questcrate", 0);
@@ -62,4 +61,94 @@ public class Config extends AbstractConfig{
} }
} }
} }
public static class ARMOR_STAND_LIMIT {
private static final String prefix = "armor_stand_limit.";
public static HashMap<String, Integer> LIMIT = new HashMap<>();
@SuppressWarnings("unused")
private static void load() {
LIMIT.clear();
ConfigurationSection configurationSection = config.getConfigurationSection(prefix.substring(0, prefix.length() - 1));
if (configurationSection == null) {
log.warn("No limits configured, adding default");
config.set(prefix, "default", 10);
}
Set<String> limits = configurationSection.getKeys(false);
for (String key : limits) {
LIMIT.put(key, config.getInt(prefix, key, 10));
}
}
}
public static class LOCATOR_BAR {
private static final String prefix = "locator_bar.";
public static double WAYPOINT_RECEIVE_RANGE = 200;
public static double WAYPOINT_TRANSMIT_RANGE = 200;
@SuppressWarnings("unused")
private static void load() {
WAYPOINT_RECEIVE_RANGE = config.getDouble(prefix, "waypoint_receive_range", WAYPOINT_RECEIVE_RANGE);
WAYPOINT_TRANSMIT_RANGE = config.getDouble(prefix, "waypoint_transmit_range", WAYPOINT_TRANSMIT_RANGE);
}
}
public static class VILLAGER_WORKSTATION {
private static final String prefix = "villager-workstation.";
public static int RANGE = 8;
public static int CHECK_INTERVAL_TICKS = 5;
public static int PARTICLE_RING_COUNT = 8;
@SuppressWarnings("unused")
private static void load() {
RANGE = config.getInt(prefix, "range", RANGE);
CHECK_INTERVAL_TICKS = config.getInt(prefix, "check-interval-ticks", CHECK_INTERVAL_TICKS);
PARTICLE_RING_COUNT = config.getInt(prefix, "particle-ring-count", PARTICLE_RING_COUNT);
}
}
public static class RANDOM_PLOT {
private static final String prefix = "random-plot.";
// Command
public static List<String> ALLOWED_WORLDS = List.of("plotworld");
// Permissions
public static int COUNTDOWN_SECONDS = 3;
public static HashMap<Integer, String> COUNTDOWN_TITLES = new HashMap<>();
public static String COUNTDOWN_SUBTITLE = "<dark_grey>Preparing teleportation</dark_grey>";
@SuppressWarnings("unused")
private static void load() {
ALLOWED_WORLDS = config.getStringList(prefix, "allowed-worlds", ALLOWED_WORLDS);
COUNTDOWN_SECONDS = config.getInt(prefix + "countdown.", "seconds", COUNTDOWN_SECONDS);
COUNTDOWN_SUBTITLE = config.getString(prefix + "countdown.", "subtitle", COUNTDOWN_SUBTITLE);
// Countdown titles — integer keys map to display strings
COUNTDOWN_TITLES.clear();
ConfigurationSection titlesSection =
config.getConfigurationSection("random-plot.countdown.titles");
if (titlesSection != null) {
for (String key : titlesSection.getKeys(false)) {
try {
COUNTDOWN_TITLES.put(Integer.parseInt(key),
titlesSection.getString(key, key));
} catch (NumberFormatException ignored) {
log.warn("Invalid countdown title key: {}", key);
}
}
} else {
config.yaml.addDefault("random-plot.countdown.titles.3", "<color:#08FBFF>Go!");
config.yaml.addDefault("random-plot.countdown.titles.2", "<color:#08FBFF>2");
config.yaml.addDefault("random-plot.countdown.titles.1", "<color:#08FBFF>1");
COUNTDOWN_TITLES.put(3, "<color:#08FBFF>2");
COUNTDOWN_TITLES.put(2, "<color:#08FBFF>1");
COUNTDOWN_TITLES.put(1, "<color:#08FBFF>Go!");
}
}
}
} }
@@ -1,7 +1,7 @@
package com.alttd.playerutils.config; package com.alttd.playerutils.config;
import com.alttd.playerutils.util.Logger;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.io.File; import java.io.File;
@@ -9,24 +9,23 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@Slf4j
public class KeyStorage extends AbstractConfig { public class KeyStorage extends AbstractConfig {
static KeyStorage config; static KeyStorage config;
private final Logger logger;
public KeyStorage(Logger logger) { public KeyStorage() {
super( super(
new File(File.separator new File(File.separator
+ "mnt" + File.separator + "mnt" + File.separator
+ "configs" + File.separator + "configs" + File.separator
+ "PlayerUtils"), + "PlayerUtils"),
"key_storage.yml", logger); "key_storage.yml");
this.logger = logger;
} }
public static void reload(Logger logger) { public static void reload() {
logger.info("Reloading key storage"); log.info("Reloading key storage");
config = new KeyStorage(logger); config = new KeyStorage();
config.readConfig(KeyStorage.class, null); config.readConfig(KeyStorage.class, null);
} }
@@ -43,13 +42,13 @@ public class KeyStorage extends AbstractConfig {
Object2IntOpenHashMap<UUID> count = new Object2IntOpenHashMap<>(); Object2IntOpenHashMap<UUID> count = new Object2IntOpenHashMap<>();
ConfigurationSection configurationSection = config.getConfigurationSection(prefix + crate); ConfigurationSection configurationSection = config.getConfigurationSection(prefix + crate);
if (configurationSection == null) { if (configurationSection == null) {
config.logger.info(String.format("No section yet for crate %s", crate)); log.info("No section yet for crate {}", crate);
KEYS.put(crate, count); KEYS.put(crate, count);
continue; continue;
} }
List<UUID> uuids = configurationSection.getKeys(false).stream().map(UUID::fromString).toList(); List<UUID> uuids = configurationSection.getKeys(false).stream().map(UUID::fromString).toList();
if (uuids.isEmpty()) { if (uuids.isEmpty()) {
config.logger.info(String.format("No keys yet for crate %s", crate)); log.info("No keys yet for crate {}", crate);
KEYS.put(crate, count); KEYS.put(crate, count);
continue; continue;
} }
@@ -62,7 +61,7 @@ public class KeyStorage extends AbstractConfig {
} }
public synchronized static void save() { public synchronized static void save() {
config.logger.info("Saving KeyStorage"); log.info("Saving KeyStorage");
KEYS.keySet() KEYS.keySet()
.forEach(crate -> KEYS.get(crate) .forEach(crate -> KEYS.get(crate)
.forEach((uuid, keys) -> config.set(prefix + crate + ".", uuid.toString(), keys))); .forEach((uuid, keys) -> config.set(prefix + crate + ".", uuid.toString(), keys)));
@@ -1,27 +1,22 @@
package com.alttd.playerutils.config; package com.alttd.playerutils.config;
import com.alttd.playerutils.util.Logger;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
public class Messages extends AbstractConfig { public class Messages extends AbstractConfig {
static Messages config; static Messages config;
private final Logger logger;
Messages(Logger logger) { Messages() {
super( super(
new File(File.separator new File(File.separator
+ "mnt" + File.separator + "mnt" + File.separator
+ "configs" + File.separator + "configs" + File.separator
+ "PlayerUtils"), + "PlayerUtils"),
"messages.yml", logger); "messages.yml");
this.logger = logger;
} }
public static void reload(Logger logger) { public static void reload() {
config = new Messages(logger); config = new Messages();
config.readConfig(Messages.class, null); config.readConfig(Messages.class, null);
} }
@@ -36,6 +31,9 @@ public class Messages extends AbstractConfig {
public static String RELOAD = "<green>Reload the configs for PlayerUtils: <gold>/pu reload</gold></green>"; public static String RELOAD = "<green>Reload the configs for PlayerUtils: <gold>/pu reload</gold></green>";
public static String ROTATE_BLOCK = "<green>Enable rotating blocks with a blaze rod: <gold>/pu rotateblock</gold></green>"; public static String ROTATE_BLOCK = "<green>Enable rotating blocks with a blaze rod: <gold>/pu rotateblock</gold></green>";
public static String KEY = "<green>Receive a key that you are owed: <gold>/pu key</gold></green>"; public static String KEY = "<green>Receive a key that you are owed: <gold>/pu key</gold></green>";
public static String GHAST_SPEED = "<green>Set the speed of a ghast: <gold>/pu ghastspeed <speed></gold></green>";
public static String RECOUNT_ARMOR_STANDS = "<green>Recount armor stands in current chunk: <gold>/pu recount</gold></green>";
public static String RANDOM_PLOT = "<green>Get a random plot: <gold>/pu randomplot</gold></green>";
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void load() { private static void load() {
@@ -46,6 +44,8 @@ public class Messages extends AbstractConfig {
XP_CALC = config.getString(prefix, "xp-calc", XP_CALC); XP_CALC = config.getString(prefix, "xp-calc", XP_CALC);
RELOAD = config.getString(prefix, "reload", RELOAD); RELOAD = config.getString(prefix, "reload", RELOAD);
ROTATE_BLOCK = config.getString(prefix, "rotate-block", ROTATE_BLOCK); ROTATE_BLOCK = config.getString(prefix, "rotate-block", ROTATE_BLOCK);
GHAST_SPEED = config.getString(prefix, "ghast-speed", GHAST_SPEED);
RECOUNT_ARMOR_STANDS = config.getString(prefix, "recount-armor-stands", RECOUNT_ARMOR_STANDS);
} }
} }
@@ -157,4 +157,78 @@ public class Messages extends AbstractConfig {
GAVE_FINAL_KEY = config.getString(prefix, "gave-final-key", GAVE_FINAL_KEY); GAVE_FINAL_KEY = config.getString(prefix, "gave-final-key", GAVE_FINAL_KEY);
} }
} }
public static class GHAST_SPEED {
private static final String prefix = "pu-command.ghast-speed.";
public static String NOT_RIDING_A_GHAST = "<red>You are not riding a ghast</red>";
public static String FAILED_TO_SET_SPEED = "<red>Failed to set ghast speed</red>";
public static String NEW_SPEED_SET_TO = "<green>New speed set to <speed></green>";
@SuppressWarnings("unused")
private static void load() {
NOT_RIDING_A_GHAST = config.getString(prefix, "not-riding-a-ghast", NOT_RIDING_A_GHAST);
FAILED_TO_SET_SPEED = config.getString(prefix, "failed-to-set-speed", FAILED_TO_SET_SPEED);
NEW_SPEED_SET_TO = config.getString(prefix, "new-speed-set-to", NEW_SPEED_SET_TO);
}
}
public static class BLOCK_BLOCK_USE {
private static final String prefix = "block-block-use.";
public static String BLOCKED = "<red>You are not powerful enough!</red>";
@SuppressWarnings("unused")
private static void load() {
BLOCKED = config.getString(prefix, "blocked", BLOCKED);
}
}
public static class RECOUNT_ARMOR_STANDS {
private static final String prefix = "recount-armor-stands.";
public static String FAILED_COUNT = "<red>Unable to recount armor stands.</red>";
public static String SUCCESS = "<green>Recounted <count> armor stands and the count to chunk data.</green>";
@SuppressWarnings("unused")
private static void load() {
FAILED_COUNT = config.getString(prefix, "failed-count", FAILED_COUNT);
SUCCESS = config.getString(prefix, "success", SUCCESS);
}
}
public static class VILLAGER_WORKSTATION {
private static final String prefix = "villager-workstation.";
public static String WORKSTATION = "<gold>Villager (<profession>): <yellow><workstation></yellow></gold>";
public static String NO_WORKSTATION = "<gold>Villager (<profession>): <gray>No workstation</gray></gold>";
@SuppressWarnings("unused")
private static void load() {
WORKSTATION = config.getString(prefix, "workstation", WORKSTATION);
NO_WORKSTATION = config.getString(prefix, "no-workstation", NO_WORKSTATION);
}
}
public static class RANDOM_PLOT {
private static final String prefix = "random-plot.";
public static String WORLD_NOT_ALLOWED = "<red>You must be in an allowed world to use this command.</red>";
public static String PLOT_SQUARED_NOT_ENABLED = "<red>PlotSquared is not available on this server.</red>";
public static String TELEPORT_START = "<green>Starting teleport countdown...</green>";
public static String TELEPORT_SUCCESS = "<gold>You have arrived at <plot_id> by <plot_owner> random plot!</gold>";
public static String TELEPORT_CANCELLED = "<red>Teleport cancelled! You moved.</red>";
public static String NO_PLOTS_FOUND = "<red>No plots found in this world.</red>";
@SuppressWarnings("unused")
private static void load() {
WORLD_NOT_ALLOWED = config.getString(prefix, "world-not-allowed", WORLD_NOT_ALLOWED);
PLOT_SQUARED_NOT_ENABLED = config.getString(prefix, "plotsquared-not-enabled", PLOT_SQUARED_NOT_ENABLED);
TELEPORT_START = config.getString(prefix, "teleport-start", TELEPORT_START);
TELEPORT_SUCCESS = config.getString(prefix, "teleport-success", TELEPORT_SUCCESS);
TELEPORT_CANCELLED = config.getString(prefix, "teleport-cancelled", TELEPORT_CANCELLED);
NO_PLOTS_FOUND = config.getString(prefix, "no-plots-found", NO_PLOTS_FOUND);
}
}
} }
@@ -0,0 +1,19 @@
package com.alttd.playerutils.data_objects;
public enum GHAST_SPEED {
SLOW,
NORMAL,
FAST,
VERY_FAST,
EXTREMELY_FAST;
public static double getSpeed(GHAST_SPEED ghastSpeed) {
return switch (ghastSpeed) {
case SLOW -> 0.025;
case NORMAL -> 0.05;
case FAST -> 0.075;
case VERY_FAST -> 0.1;
case EXTREMELY_FAST -> 0.125;
};
}
}
@@ -0,0 +1,65 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.config.Messages;
import com.destroystokyo.paper.MaterialTags;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
public class BlockBlockUseEvent implements Listener {
@EventHandler
public void onBlockPlace(BlockCanBuildEvent event) {
Player player = event.getPlayer();
if (player != null && player.hasPermission("playerutils.block-block-use.bypass")) {
return;
}
if (isNotBlocked(event.getMaterial())) {
return;
}
event.setBuildable(false);
if (player != null) {
player.sendRichMessage(Messages.BLOCK_BLOCK_USE.BLOCKED);
}
}
@EventHandler
public void onItemUse(PlayerInteractEvent event) {
Player player = event.getPlayer();
if (player.hasPermission("playerutils.block-block-use.bypass")) {
return;
}
if (!event.getAction().isRightClick()) {
return;
}
ItemStack item = event.getItem();
if (item == null || isNotBlocked(item.getType())) {
return;
}
player.sendRichMessage(Messages.BLOCK_BLOCK_USE.BLOCKED);
event.setCancelled(true);
}
@EventHandler
public void onDispenserUse(BlockDispenseEvent event) {
if (isNotBlocked(event.getItem().getType())) {
return;
}
event.setCancelled(true);
}
public boolean isNotBlocked(Material type) {
return !type.equals(Material.BEDROCK)
&& !type.equals(Material.SPAWNER)
&& !type.equals(Material.BARRIER)
&& !type.equals(Material.END_PORTAL_FRAME)
&& !MaterialTags.SPAWN_EGGS.isTagged(type);
}
}
@@ -0,0 +1,105 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.util.BookByteUtils;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Item;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.ItemStack;
@Slf4j
public class BookByteLimitListener implements Listener {
private boolean isOversizedBook(ItemStack stack) {
boolean isOversizedBook = BookByteUtils.shouldCountForBookByteLimit(stack)
&& BookByteUtils.computeBytes(stack) > BookByteUtils.getMAX_BOOK_BYTES();
if (isOversizedBook) {
log.warn("Player tried to drop an oversized book");
Component message = MiniMessage.miniMessage().deserialize(
"<red>Player tried to drop an oversized book</red>");
Bukkit.broadcast(message, "staffutils.patrol");
}
return isOversizedBook;
}
private boolean isOversizedBook(ItemStack stack, HumanEntity humanEntity) {
boolean isOversizedBook = BookByteUtils.shouldCountForBookByteLimit(stack)
&& BookByteUtils.computeBytes(stack) > BookByteUtils.getMaxBookBytes(humanEntity);
if (isOversizedBook) {
log.warn("{} [{}] tried to drop an oversized book", humanEntity.getName(), humanEntity.getUniqueId());
Component message = MiniMessage.miniMessage().deserialize(
"<red>Player <player> tried to drop an oversized book</red>",
Placeholder.unparsed("player", humanEntity.getName()));
Bukkit.broadcast(message, "staffutils.patrol");
}
return isOversizedBook;
}
@EventHandler
public void onItemSpawn(ItemSpawnEvent event) {
Item item = event.getEntity();
if (isOversizedBook(item.getItemStack())) {
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerDrop(PlayerDropItemEvent event) {
if (isOversizedBook(event.getItemDrop().getItemStack(), event.getPlayer())) {
event.setCancelled(true);
}
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
InventoryAction action = event.getAction();
switch (action) {
case PLACE_ALL, PLACE_ONE, PLACE_SOME, SWAP_WITH_CURSOR -> {
if (isOversizedBook(event.getCursor(), event.getWhoClicked())) {
event.setCancelled(true);
}
}
case MOVE_TO_OTHER_INVENTORY -> {
if (isOversizedBook(event.getCurrentItem(), event.getWhoClicked())) {
event.setCancelled(true);
}
}
case HOTBAR_SWAP -> {
ItemStack hotbarItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton());
if (isOversizedBook(hotbarItem, event.getWhoClicked())) {
event.setCancelled(true);
}
}
default -> {
}
}
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (isOversizedBook(event.getOldCursor(), event.getWhoClicked())) {
event.setCancelled(true);
}
}
@EventHandler
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
if (event.getSource().getHolder() instanceof HumanEntity humanEntity && isOversizedBook(event.getItem(), humanEntity)) {
event.setCancelled(true);
} else if (isOversizedBook(event.getItem())) {
event.setCancelled(true);
}
}
}
@@ -0,0 +1,43 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.util.BookByteUtils;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerEditBookEvent;
import org.bukkit.inventory.meta.BookMeta;
@Slf4j
public class BookWriteEvent implements Listener {
@EventHandler
public void onPlayerEditBook(PlayerEditBookEvent event) {
Player player = event.getPlayer();
BookMeta meta = event.getNewBookMeta();
int totalBytes = BookByteUtils.computeBytes(meta);
if (totalBytes > BookByteUtils.getMaxBookBytes(player)) {
log.warn("Player {} [{}] tried to write a book with {} bytes",
player.getName(), player.getUniqueId(), totalBytes);
event.setCancelled(true);
Component message = MiniMessage.miniMessage().deserialize(
"<red>Player <player> tried to write a book with <bytes> bytes</red>",
Placeholder.unparsed("player", player.getName()), Placeholder.parsed("bytes", String.valueOf(totalBytes)));
Bukkit.broadcast(message, "staffutils.patrol");
} else if (totalBytes > BookByteUtils.getBigBookBytes(player)) {
log.warn("Player {} [{}] wrote a large book with {} bytes",
player.getName(), player.getUniqueId(), totalBytes);
Component message = MiniMessage.miniMessage().deserialize(
"<red>Player <player> wrote a book with <bytes> bytes</red>",
Placeholder.unparsed("player", player.getName()), Placeholder.parsed("bytes", String.valueOf(totalBytes)));
Bukkit.broadcast(message, "staffutils.patrol");
}
}
}
@@ -0,0 +1,71 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.data_objects.GHAST_SPEED;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HappyGhast;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDismountEvent;
import org.bukkit.event.entity.EntityMountEvent;
import java.util.HashMap;
import java.util.UUID;
@Slf4j
public class GhastSpeedEvent implements Listener {
private final HashMap<UUID, GHAST_SPEED> lastSetSpeed = new HashMap<>();
public GhastSpeedEvent() {
}
@EventHandler
public void onEntityMount(EntityMountEvent event) {
if (!(event.getEntity() instanceof Player player)) {
return;
}
Entity mount = event.getMount();
if (!(mount instanceof HappyGhast happyGhast)) {
return;
}
GHAST_SPEED ghastSpeed = lastSetSpeed.get(player.getUniqueId());
if (ghastSpeed == null) {
return;
}
AttributeInstance attribute = happyGhast.getAttribute(Attribute.FLYING_SPEED);
if (attribute == null) {
return;
}
attribute.setBaseValue(GHAST_SPEED.getSpeed(ghastSpeed));
}
@EventHandler
public void onEntityDismount(EntityDismountEvent event) {
if (!(event.getDismounted() instanceof HappyGhast happyGhast)) {
return;
}
if (happyGhast.getPassengers().size() >= 2) {
return;
}
AttributeInstance attribute = happyGhast.getAttribute(Attribute.FLYING_SPEED);
if (attribute == null) {
return;
}
attribute.setBaseValue(GHAST_SPEED.getSpeed(GHAST_SPEED.NORMAL));
}
public void setNewSpeed(UUID uuid, GHAST_SPEED speed) {
lastSetSpeed.put(uuid, speed);
}
}
@@ -1,6 +1,6 @@
package com.alttd.playerutils.event_listeners; package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -11,13 +11,7 @@ import org.bukkit.inventory.ItemStack;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class GoatHornEvent implements Listener { @Slf4j public class GoatHornEvent implements Listener {
private final Logger logger;
public GoatHornEvent(Logger logger) {
this.logger = logger;
}
@EventHandler @EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
@@ -41,11 +35,10 @@ public class GoatHornEvent implements Listener {
if (player.getLocation().distance(spawn) > 250) { if (player.getLocation().distance(spawn) > 250) {
logger.info(String.format("Player %s with uuid %s used a goat horn", player.getName(), player.getUniqueId())); log.info("Player {} with uuid {} used a goat horn", player.getName(), player.getUniqueId());
return; return;
} }
log.info("Player {} with uuid {} used a goat horn in spawn", player.getName(), player.getUniqueId());
logger.info(String.format("Player %s with uuid %s used a goat horn in spawn", player.getName(), player.getUniqueId()));
player.setCooldown(Material.GOAT_HORN, (int) TimeUnit.MINUTES.toSeconds(5) * 20); player.setCooldown(Material.GOAT_HORN, (int) TimeUnit.MINUTES.toSeconds(5) * 20);
} }
@@ -0,0 +1,218 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.config.Config;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.title.Title;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.Dispenser;
import org.bukkit.block.TrialSpawner;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
@Slf4j public class LimitArmorStands implements Listener {
private final PlayerUtils playerUtils;
public LimitArmorStands(PlayerUtils playerUtils) {
this.playerUtils = playerUtils;
}
@FunctionalInterface
private interface ArmorStandCountConsumer {
void apply(int armorStandCount, @NotNull Player player, @NotNull NamespacedKey namespacedKey,
@NotNull PersistentDataContainer persistentDataContainer);
}
@FunctionalInterface
private interface TrialSpawnerTimerConsumer {
void apply(int ticks, @NotNull Player player);
}
@EventHandler
public void onPlayerInteractEvent(PlayerInteractEvent event) {
handleArmorStandPlacing(event, (armorStandCount, player, namespacedKey, persistentDataContainer) -> {
int max = 0;
for (String permission : Config.ARMOR_STAND_LIMIT.LIMIT.keySet()) {
if (player.hasPermission("pu.limit." + permission)) {
max = Math.max(max, Config.ARMOR_STAND_LIMIT.LIMIT.get(permission));
}
}
if (armorStandCount >= max) {
event.setCancelled(true);
player.sendRichMessage("<red>You can not place more than <max> armor stands in a chunk</red>",
Placeholder.parsed("max", String.valueOf(max)));
return;
}
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, ++armorStandCount);
});
if (event.getPlayer().hasPermission("pu.trial_spawner.cooldown")) {
handleRightClickTrialSpawner(event, (ticks, player) -> {
MiniMessage miniMessage = MiniMessage.miniMessage();
Title title = Title.title(Component.empty(),
miniMessage.deserialize("<green>Cooldown <red><seconds></red></green>",
Placeholder.parsed("seconds", String.valueOf(ticks / 20))));
player.showTitle(title);
});
}
}
@EventHandler
public void onPlayerDamageEntity(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Player player)) {
return;
}
if (!(event.getEntity() instanceof ArmorStand armorStand)) {
return;
}
boolean willBreak = armorStand.getHealth() - event.getFinalDamage() <= 0.0;
if (!willBreak) {
return;
}
Optional<ChunkContainerAndNamespacedKey> optionalResult = getGetChunkContainerAndNamespacedKey(armorStand.getLocation().getChunk());
if (optionalResult.isEmpty()) {
player.sendRichMessage("<red>Something went wrong while checking the armor stand count. " +
"You will not be able to place this until this is fixed. Please contact a staff member</red>");
event.setCancelled(true);
return;
}
ChunkContainerAndNamespacedKey result = optionalResult.get();
int newArmorStandCount = Math.max(0, result.armorStandCount() - 1);
result.persistentDataContainer().set(result.namespacedKey(), PersistentDataType.INTEGER, newArmorStandCount);
}
private record ChunkContainerAndNamespacedKey(NamespacedKey namespacedKey, PersistentDataContainer persistentDataContainer, int armorStandCount) {
}
@EventHandler
public void onBlockDispense(BlockDispenseEvent event) {
if (!event.getItem().getType().equals(Material.ARMOR_STAND)) {
return;
}
Block block = event.getBlock();
if (!(block.getState() instanceof Dispenser)) {
return;
}
if (!(block.getBlockData() instanceof Directional directional)) {
return;
}
Chunk targetChunk = block.getRelative(directional.getFacing()).getChunk();
Optional<ChunkContainerAndNamespacedKey> optional = getGetChunkContainerAndNamespacedKey(targetChunk);
if (optional.isEmpty()) {
event.setCancelled(true);
return;
}
ChunkContainerAndNamespacedKey result = optional.get();
Optional<Integer> optionalLimit = Config.ARMOR_STAND_LIMIT.LIMIT.values().stream().min(Integer::compareTo);
if (optionalLimit.isEmpty()) {
log.error("Unable to find a valid limit for armor stands");
event.setCancelled(true);
return;
}
if (result.armorStandCount() >= optionalLimit.get()) {
event.setCancelled(true);
return;
}
result.persistentDataContainer().set(result.namespacedKey(), PersistentDataType.INTEGER, result.armorStandCount() + 1);
}
private void handleArmorStandPlacing(PlayerInteractEvent event, ArmorStandCountConsumer consumer) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
if (!event.getMaterial().equals(Material.ARMOR_STAND)) {
return;
}
if (event.getClickedBlock() == null) {
return;
}
Player player = event.getPlayer();
Chunk chunk = event.getClickedBlock().getChunk();
Optional<ChunkContainerAndNamespacedKey> getChunkContainerAndNamespacedKey = getGetChunkContainerAndNamespacedKey(chunk);
if (getChunkContainerAndNamespacedKey.isEmpty()) {
player.sendRichMessage("<red>Something went wrong while checking the armor stand count. " +
"You will not be able to place this until this is fixed. Please contact a staff member</red>");
event.setCancelled(true);
return;
}
ChunkContainerAndNamespacedKey result = getChunkContainerAndNamespacedKey.get();
consumer.apply(result.armorStandCount(), event.getPlayer(), result.namespacedKey(), result.persistentDataContainer());
}
private Optional<LimitArmorStands.ChunkContainerAndNamespacedKey> getGetChunkContainerAndNamespacedKey(Chunk chunk) {
NamespacedKey namespacedKey = NamespacedKey.fromString("armor_stand_count", playerUtils);
if (namespacedKey == null) {
log.warn("Unable to retrieve name spaced key for armor stand count.");
return Optional.empty();
}
PersistentDataContainer persistentDataContainer = chunk.getPersistentDataContainer();
if (!persistentDataContainer.has(namespacedKey, PersistentDataType.INTEGER)) {
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, countArmorStands(chunk));
}
Integer armorStandCount = persistentDataContainer.get(namespacedKey, PersistentDataType.INTEGER);
if (armorStandCount == null) {
return Optional.empty();
}
return Optional.of(new ChunkContainerAndNamespacedKey(namespacedKey, persistentDataContainer, armorStandCount));
}
private void handleRightClickTrialSpawner(PlayerInteractEvent event, TrialSpawnerTimerConsumer consumer) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
if (!event.getMaterial().equals(Material.AIR)) {
return;
}
if (event.getClickedBlock() == null) {
return;
}
Block block = event.getClickedBlock();
if (!block.getType().equals(Material.TRIAL_SPAWNER)) {
return;
}
if (!(block instanceof TrialSpawner trialSpawner)) {
return;
}
if (!(trialSpawner.getBlockData() instanceof org.bukkit.block.data.type.TrialSpawner trialSpawnerData)) {
return;
}
Player player = event.getPlayer();
if (!trialSpawnerData.getTrialSpawnerState().equals(org.bukkit.block.data.type.TrialSpawner.State.INACTIVE)) {
consumer.apply(0, player);
return;
}
consumer.apply(trialSpawner.getCooldownLength(), player);
}
private int countArmorStands(Chunk chunk) {
return (int) Arrays.stream(chunk.getEntities())
.filter(entity -> entity.getType().equals(EntityType.ARMOR_STAND))
.count();
}
}
@@ -0,0 +1,66 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.config.Config;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.NamespacedKey;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
@Slf4j
public class PlayerJoin implements Listener {
private final NamespacedKey WAYPOINT_RECEIVE_KEY;
private final NamespacedKey WAYPOINT_TRANSMIT_KEY;
public PlayerJoin(JavaPlugin plugin) {
this.WAYPOINT_RECEIVE_KEY = new NamespacedKey(plugin, "waypoint_receive_modifier");
this.WAYPOINT_TRANSMIT_KEY = new NamespacedKey(plugin, "waypoint_transmit_modifier");
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
setModifiedAttribute(player, Attribute.WAYPOINT_RECEIVE_RANGE, Config.LOCATOR_BAR.WAYPOINT_RECEIVE_RANGE, WAYPOINT_RECEIVE_KEY);
setModifiedAttribute(player, Attribute.WAYPOINT_TRANSMIT_RANGE, Config.LOCATOR_BAR.WAYPOINT_TRANSMIT_RANGE, WAYPOINT_TRANSMIT_KEY);
}
private void setModifiedAttribute(Player player, Attribute attribute, double configValue, NamespacedKey key) {
AttributeInstance attributeInstance = player.getAttribute(attribute);
if (attributeInstance == null) {
log.error("Unable to retrieve attribute instance for player {}.", player.getName());
return;
}
for (AttributeModifier modifier : attributeInstance.getModifiers()) {
attributeInstance.removeModifier(modifier);
}
double attributeDelta = configValue - attributeInstance.getBaseValue();
if (attributeDelta != 0) {
AttributeModifier attributeModifier = new AttributeModifier(
key,
attributeDelta,
AttributeModifier.Operation.ADD_NUMBER
);
attributeInstance.addTransientModifier(attributeModifier);
}
double actualValue = attributeInstance.getValue();
if (Math.abs(actualValue - configValue) > 0.01) {
log.warn("Failed to set attribute {} to {} for {}, actual value is {}.",
attribute, configValue, player.getName(), actualValue);
} else {
log.info("Set attribute {} for {} to {}.", attribute, player.getName(), configValue);
}
}
}
@@ -1,6 +1,6 @@
package com.alttd.playerutils.event_listeners; package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import org.bukkit.Axis; import org.bukkit.Axis;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
@@ -21,17 +21,12 @@ import org.bukkit.inventory.ItemStack;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j
public class RotateBlockEvent implements Listener { public class RotateBlockEvent implements Listener {
private final HashSet<UUID> rotateEnabled = new HashSet<>(); private final HashSet<UUID> rotateEnabled = new HashSet<>();
private final Logger logger;
private static final List<BlockFace> VALID_FOUR_STATES = List.of(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST); private static final List<BlockFace> VALID_FOUR_STATES = List.of(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST);
public RotateBlockEvent(Logger logger) {
this.logger = logger;
}
public synchronized boolean toggleRotate(UUID uuid) { public synchronized boolean toggleRotate(UUID uuid) {
if (rotateEnabled.contains(uuid)) { if (rotateEnabled.contains(uuid)) {
rotateEnabled.remove(uuid); rotateEnabled.remove(uuid);
@@ -61,7 +56,8 @@ public class RotateBlockEvent implements Listener {
return; return;
Material type = block.getType(); Material type = block.getType();
logger.debug(String.format("Material %s with action %s", type, event.getAction().isLeftClick() ? "left click" : "right click")); log.debug("Material {} with action {}", type, event.getAction().isLeftClick() ? "left " +
"click" : "right click");
if (type.equals(Material.IRON_TRAPDOOR) && event.getAction().isLeftClick()) { if (type.equals(Material.IRON_TRAPDOOR) && event.getAction().isLeftClick()) {
event.setCancelled(true); event.setCancelled(true);
toggleTrapDoor(block, player); toggleTrapDoor(block, player);
@@ -0,0 +1,18 @@
package com.alttd.playerutils.event_listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityTameEvent;
import java.util.UUID;
public class VanillaPetTameEvent implements Listener {
@EventHandler
public void onEntityTame(EntityTameEvent event) {
UUID uniqueId = event.getOwner().getUniqueId();
}
}
@@ -0,0 +1,84 @@
package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.config.Messages;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.entity.memory.MemoryKey;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
@Slf4j
public class VillagerWorkstationEvent implements Listener {
public VillagerWorkstationEvent(JavaPlugin plugin) {
Bukkit.getScheduler().runTaskTimer(plugin, this::checkPlayersLookingAtVillagers,
0L, Config.VILLAGER_WORKSTATION.CHECK_INTERVAL_TICKS);
}
private void checkPlayersLookingAtVillagers() {
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.getInventory().getItemInMainHand().getType().equals(Material.AIR)) {
continue;
}
Entity target = player.getTargetEntity(Config.VILLAGER_WORKSTATION.RANGE);
if (!(target instanceof Villager villager)) {
continue;
}
showWorkstation(player, villager);
}
}
private void showWorkstation(Player player, Villager villager) {
String professionName = formatName(villager.getProfession().key().asMinimalString());
Location jobSite = villager.getMemory(MemoryKey.JOB_SITE);
if (jobSite == null) {
player.sendActionBar(MiniMessage.miniMessage().deserialize(
Messages.VILLAGER_WORKSTATION.NO_WORKSTATION
.replace("<profession>", professionName)));
return;
}
// Read the actual block type at the linked location so it reflects reality
String blockName = formatName(jobSite.getBlock().getType().name());
player.sendActionBar(MiniMessage.miniMessage().deserialize(
Messages.VILLAGER_WORKSTATION.WORKSTATION
.replace("<profession>", professionName)
.replace("<workstation>", blockName)));
spawnWorkstationParticles(player, jobSite.getBlock().getLocation());
}
private void spawnWorkstationParticles(Player player, Location blockLoc) {
World world = blockLoc.getWorld();
if (world == null) {
return;
}
double cx = blockLoc.getX() + 0.5;
double cy = blockLoc.getY() + 1.1;
double cz = blockLoc.getZ() + 0.5;
int count = Config.VILLAGER_WORKSTATION.PARTICLE_RING_COUNT;
for (int i = 0; i < count; i++) {
double angle = (2 * Math.PI * i) / count;
double x = cx + 0.5 * Math.cos(angle);
double z = cz + 0.5 * Math.sin(angle);
player.spawnParticle(Particle.HAPPY_VILLAGER, x, cy, z, 1, 0, 0, 0, 0);
}
}
private static String formatName(String enumName) {
String lower = enumName.replace("_", " ").toLowerCase();
return Character.toUpperCase(lower.charAt(0)) + lower.substring(1);
}
}
@@ -2,7 +2,7 @@ package com.alttd.playerutils.event_listeners;
import com.alttd.playerutils.PlayerUtils; import com.alttd.playerutils.PlayerUtils;
import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
@@ -25,15 +25,13 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class XpBottleEvent implements Listener { @Slf4j public class XpBottleEvent implements Listener {
private final PlayerUtils playerUtils; private final PlayerUtils playerUtils;
private final Logger logger;
private final MiniMessage miniMessage = MiniMessage.miniMessage(); private final MiniMessage miniMessage = MiniMessage.miniMessage();
public XpBottleEvent(PlayerUtils playerUtils, Logger logger) { public XpBottleEvent(PlayerUtils playerUtils) {
this.playerUtils = playerUtils; this.playerUtils = playerUtils;
this.logger = logger;
} }
@EventHandler @EventHandler
@@ -42,7 +40,7 @@ public class XpBottleEvent implements Listener {
PersistentDataContainer persistentDataContainer = item.getItemMeta().getPersistentDataContainer(); PersistentDataContainer persistentDataContainer = item.getItemMeta().getPersistentDataContainer();
NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils); NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils);
if (customXp == null) { if (customXp == null) {
logger.warning("Unable to retrieve name spaced key."); log.warn("Unable to retrieve name spaced key.");
return; return;
} }
Integer integer = persistentDataContainer.get(customXp, PersistentDataType.INTEGER); Integer integer = persistentDataContainer.get(customXp, PersistentDataType.INTEGER);
@@ -83,7 +81,7 @@ public class XpBottleEvent implements Listener {
} }
for (Map.Entry<CookingRecipe<?>, Integer> entry : recipesUsed.entrySet()) { for (Map.Entry<CookingRecipe<?>, Integer> entry : recipesUsed.entrySet()) {
exp += entry.getKey().getExperience() * entry.getValue(); exp += (int) (entry.getKey().getExperience() * entry.getValue());
} }
Optional<ItemStack> optionalItemStack = getExpBottleItem(player, exp); Optional<ItemStack> optionalItemStack = getExpBottleItem(player, exp);
@@ -103,7 +101,7 @@ public class XpBottleEvent implements Listener {
NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils); NamespacedKey customXp = NamespacedKey.fromString("custom_xp", playerUtils);
if (customXp == null) { if (customXp == null) {
player.sendMiniMessage(Messages.XP_CHEQUE.FAILED_STORAGE, null); player.sendRichMessage(Messages.XP_CHEQUE.FAILED_STORAGE);
return Optional.empty(); return Optional.empty();
} }
persistentDataContainer.set(customXp, PersistentDataType.INTEGER, xpValue); persistentDataContainer.set(customXp, PersistentDataType.INTEGER, xpValue);
@@ -0,0 +1,131 @@
package com.alttd.playerutils.util;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Material;
import org.bukkit.block.ShulkerBox;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.BlockStateMeta;
import java.nio.charset.StandardCharsets;
/**
* Utility to compute the UTF-8 byte size of a written book's contents.
*/
public final class BookByteUtils {
private BookByteUtils() {
}
// 65,000 bytes per book (below CoreProtect hard limit ~65,535)
@Getter
private static final int MAX_BOOK_BYTES = 30_000;
@Getter
private static final int BIG_BOOK_BYTES = 10_000;
public static int getBigBookBytes(HumanEntity humanEntity) {
return calcBookBytes(humanEntity, BIG_BOOK_BYTES);
}
public static int getMaxBookBytes(HumanEntity humanEntity) {
return calcBookBytes(humanEntity, MAX_BOOK_BYTES);
}
private static int calcBookBytes(HumanEntity humanEntity, int bookBytes) {
if (humanEntity.hasPermission("playerutils.bigbook.bypass")) {
return Integer.MAX_VALUE;
} else if (humanEntity.hasPermission("playerutils.bigbook.double")) {
return bookBytes * 2;
} else if (humanEntity.hasPermission("playerutils.bigbook.quadruple")) {
return bookBytes * 4;
} else {
return bookBytes;
}
}
public static boolean shouldCountForBookByteLimit(ItemStack stack) {
if (stack == null) {
return false;
}
Material type = stack.getType();
if (type == Material.WRITTEN_BOOK || type == Material.WRITABLE_BOOK) {
return true;
}
if (stack.getItemMeta() instanceof BookMeta) {
return true;
}
if (!(stack.getItemMeta() instanceof BlockStateMeta bsm) || !(bsm.getBlockState() instanceof ShulkerBox shulker)) {
return false;
}
for (ItemStack content : shulker.getInventory().getContents()) {
if (content == null) {
continue;
}
Material contentType = content.getType();
if (contentType == Material.WRITTEN_BOOK || contentType == Material.WRITABLE_BOOK) {
return true;
}
if (content.getItemMeta() instanceof BookMeta) {
return true;
}
}
return false;
}
/**
* Compute the number of bytes used by the provided BookMeta.
*/
public static int computeBytes(BookMeta meta) {
if (meta == null) {
return 0;
}
int totalBytes = 0;
String title = meta.getTitle();
if (title != null) {
totalBytes += title.getBytes(StandardCharsets.UTF_8).length;
}
for (Component page : meta.pages()) {
if (page == null) {
continue;
}
String pageString = MiniMessage.miniMessage().serialize(page);
if (pageString.isEmpty()) {
continue;
}
totalBytes += pageString.getBytes(StandardCharsets.UTF_8).length;
}
return totalBytes;
}
/**
* Compute the number of bytes used by a book item stack. If the item is not a written book, returns 0.
*/
public static int computeBytes(ItemStack stack) {
if (stack == null) {
return 0;
}
// Direct written book
if (stack.getItemMeta() instanceof BookMeta meta) {
int perBook = computeBytes(meta);
return perBook * Math.max(1, stack.getAmount());
}
// Shulker box: sum bytes of contained written books
if (stack.getItemMeta() instanceof BlockStateMeta bsm && bsm.getBlockState() instanceof ShulkerBox shulker) {
int total = 0;
for (ItemStack content : shulker.getInventory().getContents()) {
if (content == null) {
continue;
}
if (content.getItemMeta() instanceof BookMeta bookMeta) {
total += computeBytes(bookMeta) * Math.max(1, content.getAmount());
}
}
return total;
}
return 0;
}
}
@@ -1,42 +0,0 @@
package com.alttd.playerutils.util;
import com.alttd.playerutils.config.Config;
public class Logger {
private final java.util.logging.Logger logger;
static private final String RESET = "\u001B[0m";
static private final String GREEN = "\u001B[32m";
static private final String TEAL = "\u001B[36m";
public Logger(java.util.logging.Logger logger) {
this.logger = logger;
}
public void debug(String debug, String... variables) {
if (!Config.SETTINGS.DEBUG)
return;
logger.info(TEAL + replace(debug, variables) + RESET);
}
public void info(String info, String... variables) {
logger.info(GREEN + replace(info, variables) + RESET);
}
public void warning(String warning, String... variables) {
if (!Config.SETTINGS.WARNINGS)
return;
logger.warning(replace(warning, variables));
}
public void severe(String severe, String... variables) {
logger.severe(replace(severe, variables));
}
private String replace(String text, String... variables) {
for (String variable : variables) {
text = text.replaceFirst("%", variable);
}
return text;
}
}
+4
View File
@@ -2,6 +2,10 @@ name: PlayerUtils
version: '${version}' version: '${version}'
main: com.alttd.playerutils.PlayerUtils main: com.alttd.playerutils.PlayerUtils
api-version: '1.20' api-version: '1.20'
softdepend:
- PlotSquared
- LuckPerms
- Vault
commands: commands:
playerutils: playerutils:
description: Base command for player utils description: Base command for player utils