Author SHA1 Message Date
stijn 6c66ac7cc8 Refactor April Fools' prank logic: replace single explosion sound with randomized sounds, add logging, and adjust sound radius. 2026-03-31 23:25:35 +02:00
stijn 592f58a89b Add April Fools' prank functionality with scheduled explosions and test command 2026-03-31 22:51:37 +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
32 changed files with 1109 additions and 408 deletions
+7 -1
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"
@@ -38,7 +39,12 @@ tasks {
} }
dependencies { dependencies {
compileOnly("com.alttd:Galaxy-API:1.21-R0.1-SNAPSHOT") { compileOnly("com.alttd.cosmos:cosmos-api:1.21.10-R0.1-SNAPSHOT") {
isChanging = true isChanging = true
} }
implementation("org.slf4j:slf4j-api:2.0.17")
compileOnly("org.projectlombok:lombok:1.18.38")
annotationProcessor("org.projectlombok:lombok:1.18.38")
} }
+12 -2
View File
@@ -1,11 +1,21 @@
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("https://repo.destro.xyz/snapshots")
maven("https://jitpack.io")
} }
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
} }
@@ -1,26 +1,30 @@
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.AprilFools;
import com.alttd.playerutils.commands.playerutils_subcommands.GhastSpeed;
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.*; import com.alttd.playerutils.event_listeners.*;
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;
import com.alttd.playerutils.util.AprilFoolsPrank;
public final class PlayerUtils extends JavaPlugin { public final class PlayerUtils extends JavaPlugin {
private Logger logger;
private PlayerUtilsCommand playerUtilsCommand; private PlayerUtilsCommand playerUtilsCommand;
private AprilFoolsPrank aprilFoolsPrank;
@Override @Override
public void onEnable() { public void onEnable() {
this.logger = new Logger(getLogger()); // initialize prank utility
aprilFoolsPrank = new AprilFoolsPrank(this);
registerCommands(); registerCommands();
registerEvents(); registerEvents();
reloadConfigs(); reloadConfigs();
@@ -33,30 +37,43 @@ public final class PlayerUtils extends JavaPlugin {
} }
private void registerCommands() { private void registerCommands() {
playerUtilsCommand = new PlayerUtilsCommand(this, logger); playerUtilsCommand = new PlayerUtilsCommand(this);
// add april fools test command
playerUtilsCommand.addSubCommand(new AprilFools(aprilFoolsPrank));
} }
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, logger), this); pluginManager.registerEvents(new LimitArmorStands(this), this);
pluginManager.registerEvents(new AprilFoolsEvent(), this); pluginManager.registerEvents(new BlockBlockUseEvent(), this);
pluginManager.registerEvents(new PlayerJoin(this), this);
pluginManager.registerEvents(new BookWriteEvent(), this);
pluginManager.registerEvents(new BookByteLimitListener(), 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() {
// periodic key storage save (async)
Bukkit.getScheduler().runTaskTimerAsynchronously(this, KeyStorage.STORAGE::save, Bukkit.getScheduler().runTaskTimerAsynchronously(this, KeyStorage.STORAGE::save,
TimeUnit.MINUTES.toSeconds(5) * 20, TimeUnit.MINUTES.toSeconds(5) * 20); TimeUnit.MINUTES.toSeconds(5) * 20, TimeUnit.MINUTES.toSeconds(5) * 20);
// April 1st prank scheduler
aprilFoolsPrank.schedule();
} }
} }
@@ -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,11 +30,12 @@ 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))
); );
} }
@@ -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))
@@ -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,48 @@
package com.alttd.playerutils.commands.playerutils_subcommands;
import com.alttd.playerutils.commands.SubCommand;
import com.alttd.playerutils.config.Messages;
import com.alttd.playerutils.util.AprilFoolsPrank;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
public class AprilFools extends SubCommand {
private final AprilFoolsPrank prank;
public AprilFools(AprilFoolsPrank prank) {
this.prank = prank;
}
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (!(commandSender instanceof Player player)) {
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
return true;
}
boolean ok = prank.playSoundAroundPlayer(player);
if (ok) {
commandSender.sendRichMessage("<green>April Fools test triggered. Listen closely...");
} else {
commandSender.sendRichMessage("<red>Failed to trigger. You must be in the overworld named 'world'.");
}
return true;
}
@Override
public String getName() {
return "aprilfools";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return List.of();
}
@Override
public String getHelpMessage() {
return "<gray>/playerutils aprilfools</gray> <dark_gray>-</dark_gray> <gray>Play a fake explosion near you (testing).";
}
}
@@ -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) {
@@ -106,7 +100,7 @@ public class Glow extends SubCommand {
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.sendRichMessage(Messages.GLOW.UNABLE_TO_GET_SCOREBOARD); 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;
} }
@@ -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,13 +15,7 @@ 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) {
@@ -52,7 +46,7 @@ public class Key extends SubCommand {
} }
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.sendRichMessage(Messages.KEY.GAVE_FINAL_KEY, TagResolver.resolver( commandSender.sendRichMessage(Messages.KEY.GAVE_FINAL_KEY, TagResolver.resolver(
@@ -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();
}
}
@@ -64,7 +64,7 @@ public class XPCheque extends SubCommand {
return true; return true;
} }
int totalExperience = player.getTotalExperience(); int totalExperience = player.calculateTotalExperiencePoints();
if (totalExperience < (xpValue * amount)) { if (totalExperience < (xpValue * amount)) {
commandSender.sendRichMessage(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;
@@ -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();
} }
} }
@@ -1,31 +1,28 @@
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;
import java.util.HashMap; import java.util.HashMap;
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 +47,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);
@@ -73,7 +70,7 @@ public class Config extends AbstractConfig{
LIMIT.clear(); LIMIT.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 limits configured, adding default"); log.warn("No limits configured, adding default");
config.set(prefix, "default", 10); config.set(prefix, "default", 10);
} }
Set<String> limits = configurationSection.getKeys(false); Set<String> limits = configurationSection.getKeys(false);
@@ -82,4 +79,17 @@ public class Config extends AbstractConfig{
} }
} }
} }
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);
}
}
} }
@@ -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,6 +1,5 @@
package com.alttd.playerutils.config; package com.alttd.playerutils.config;
import com.alttd.playerutils.util.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
@@ -8,20 +7,18 @@ 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 +33,8 @@ 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>";
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void load() { private static void load() {
@@ -46,6 +45,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 +158,43 @@ 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);
}
}
} }
@@ -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;
};
}
}
@@ -1,226 +0,0 @@
package com.alttd.playerutils.event_listeners;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityBreedEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerBucketEntityEvent;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class AprilFoolsEvent implements Listener {
MiniMessage miniMessage = MiniMessage.miniMessage();
List<Component> happyMessages = List.of(
miniMessage.deserialize("<green>You have a sudden urge of calmness and peace</green>"),
miniMessage.deserialize("<green>Mother nature is pleased and graces us with her beauty!</green>"),
miniMessage.deserialize("<green>The gentle breeze feels like a soothing hug from nature.</green>")
);
List<Component> angryMessages = List.of(
miniMessage.deserialize("<red>Something seems off...</red>"),
miniMessage.deserialize("<red>Mother nature is angry and furious with you!</red>"),
miniMessage.deserialize("<red>The wind picks up, carrying the anger of the earth.</red>")
);
private final List<PotionEffectType> badEffects = List.of(PotionEffectType.BLINDNESS, PotionEffectType.NAUSEA,
PotionEffectType.LEVITATION, PotionEffectType.DARKNESS, PotionEffectType.MINING_FATIGUE, PotionEffectType.SLOWNESS);
private final List<PotionEffectType> goodEffects = List.of(PotionEffectType.ABSORPTION, PotionEffectType.GLOWING,
PotionEffectType.HEALTH_BOOST, PotionEffectType.HERO_OF_THE_VILLAGE, PotionEffectType.LUCK, PotionEffectType.REGENERATION,
PotionEffectType.REGENERATION, PotionEffectType.RESISTANCE, PotionEffectType.SATURATION, PotionEffectType.STRENGTH);
private final List<Material> badBlocks = List.of(Material.SHORT_GRASS, Material.TALL_GRASS, Material.SEAGRASS,
Material.TALL_SEAGRASS, Material.LILY_PAD, Material.KELP, Material.KELP_PLANT, Material.BEEHIVE,
Material.BAMBOO, Material.MOSS_BLOCK, Material.CACTUS, Material.SMALL_DRIPLEAF, Material.BIG_DRIPLEAF);
private final List<Material> goodBlocks = List.of(Material.CACTUS, Material.AZALEA, Material.FLOWERING_AZALEA);
private final HashMap<UUID, Integer> goodActions = new HashMap<>();
private final HashMap<UUID, Integer> badActions = new HashMap<>();
private boolean isFlower(Material material) {
return Tag.FLOWERS.isTagged(material);
}
private boolean isLog(Material material) {
return Tag.LOGS.isTagged(material);
}
private boolean isLeaves(Material material) {
return Tag.LEAVES.isTagged(material);
}
private boolean isBadBlock(Material material) {
return badBlocks.contains(material) || isFlower(material) || isLog(material) || isLeaves(material);
}
private boolean isGoodBlock(@NotNull Material material) {
return goodBlocks.contains(material) || isFlower(material);
}
private boolean isNotAprilFools() {
LocalDate currentDate = LocalDate.now(ZoneOffset.UTC);
return currentDate.getMonthValue() != 4 || currentDate.getDayOfMonth() != 1;
}
/**
* Determines whether the provided count exceeds a randomly generated limit.
* The limit is generated as a random integer between 100 and 300, inclusive.
*
* @param count the value to be compared against the generated random limit
* @return true if the count is greater than the random limit; false otherwise
*/
private boolean underLimit(int count) {
int randomValue = 100 + (int) (Math.random() * 201);
return count <= randomValue;
}
private Component getRandomMessage(@NotNull List<Component> messages) {
return messages.get((int) (Math.random() * messages.size()));
}
private PotionEffectType getRandomEffect(@NotNull List<PotionEffectType> potionEffectTypes) {
return potionEffectTypes.get((int) (Math.random() * potionEffectTypes.size()));
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
if (isNotAprilFools()) {
return;
}
if (!isGoodBlock(event.getBlock().getType())) {
return;
}
runGoodAction(event.getPlayer());
}
@EventHandler
public void onGrassBlockBoneMealed(PlayerInteractEvent event) {
if (isNotAprilFools()) {
return;
}
if (!event.getAction().isRightClick()) {
return;
}
ItemStack itemInHand = event.getPlayer().getInventory().getItemInMainHand();
if (itemInHand.getType() != Material.BONE_MEAL) {
return;
}
Block clickedBlock = event.getClickedBlock();
if (clickedBlock == null || clickedBlock.getType() != Material.GRASS_BLOCK) {
return;
}
if (event.getHand() != EquipmentSlot.HAND) {
return;
}
runGoodAction(event.getPlayer());
}
@EventHandler
public void onAnimalBred(EntityBreedEvent event) {
if (isNotAprilFools()) {
return;
}
if (!(event.getBreeder() instanceof Player player)) {
return;
}
runGoodAction(player);
}
private void runGoodAction(@NotNull Player player) {
goodActions.merge(player.getUniqueId(), 1, Integer::sum);
if (underLimit(goodActions.get(player.getUniqueId()))) {
return;
}
player.sendMessage(getRandomMessage(happyMessages));
player.addPotionEffect(getRandomEffect(goodEffects).createEffect(60 * 20, 1));
goodActions.put(player.getUniqueId(), 0);
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
if (isNotAprilFools()) {
return;
}
if (!isBadBlock(event.getBlock().getType())) {
return;
}
Player player = event.getPlayer();
runBadAction(player);
}
@EventHandler
public void onAnimalKilled(EntityDeathEvent event) {
if (isNotAprilFools()) {
return;
}
LivingEntity entity = event.getEntity();
if (entity.getKiller() == null) {
return;
}
if (entity instanceof Monster) {
return;
}
Player player = entity.getKiller();
runBadAction(player);
}
@EventHandler
public void onFishCollected(PlayerBucketEntityEvent event) {
if (isNotAprilFools()) {
return;
}
Player player = event.getPlayer();
runBadAction(player);
}
@EventHandler
public void onPlayerFish(PlayerFishEvent event) {
if (isNotAprilFools()) {
return;
}
Player player = event.getPlayer();
runBadAction(player);
}
private void runBadAction(@NotNull Player player) {
badActions.merge(player.getUniqueId(), 1, Integer::sum);
if (underLimit(badActions.get(player.getUniqueId()))) {
return;
}
player.sendMessage(getRandomMessage(angryMessages));
player.addPotionEffect(getRandomEffect(badEffects).createEffect(10 * 20, 1));
badActions.put(player.getUniqueId(), 0);
}
}
@@ -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,103 @@
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.MAX_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.MAX_BOOK_BYTES;
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 (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.MAX_BOOK_BYTES) {
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.BIG_BOOK_BYTES) {
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);
} }
@@ -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.Config; import com.alttd.playerutils.config.Config;
import com.alttd.playerutils.util.Logger; import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
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;
@@ -11,27 +11,31 @@ import org.bukkit.Chunk;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.Dispenser;
import org.bukkit.block.TrialSpawner; 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.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; 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.event.player.PlayerInteractEvent;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional;
public class LimitArmorStands implements Listener { @Slf4j public class LimitArmorStands implements Listener {
private final PlayerUtils playerUtils; private final PlayerUtils playerUtils;
private final Logger logger;
public LimitArmorStands(PlayerUtils playerUtils, Logger logger) { public LimitArmorStands(PlayerUtils playerUtils) {
this.playerUtils = playerUtils; this.playerUtils = playerUtils;
this.logger = logger;
} }
@FunctionalInterface @FunctionalInterface
@@ -74,6 +78,67 @@ public class LimitArmorStands implements Listener {
} }
} }
@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) { private void handleArmorStandPlacing(PlayerInteractEvent event, ArmorStandCountConsumer consumer) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return; return;
@@ -84,29 +149,34 @@ public class LimitArmorStands implements Listener {
if (event.getClickedBlock() == null) { if (event.getClickedBlock() == null) {
return; return;
} }
Player player = event.getPlayer();; Player player = event.getPlayer();
Chunk chunk = event.getClickedBlock().getChunk(); Chunk chunk = event.getClickedBlock().getChunk();
NamespacedKey namespacedKey = NamespacedKey.fromString("armor_stand_count", playerUtils); Optional<ChunkContainerAndNamespacedKey> getChunkContainerAndNamespacedKey = getGetChunkContainerAndNamespacedKey(chunk);
if (namespacedKey == null) { if (getChunkContainerAndNamespacedKey.isEmpty()) {
event.setCancelled(true);
logger.warning("Unable to retrieve name spaced key for armor stand count.");
player.sendRichMessage("<red>Something went wrong while checking the armor stand count. " + 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>"); "You will not be able to place this until this is fixed. Please contact a staff member</red>");
event.setCancelled(true);
return; 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(); PersistentDataContainer persistentDataContainer = chunk.getPersistentDataContainer();
if (!persistentDataContainer.has(namespacedKey, PersistentDataType.INTEGER)) { if (!persistentDataContainer.has(namespacedKey, PersistentDataType.INTEGER)) {
persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, countArmorStands(chunk)); persistentDataContainer.set(namespacedKey, PersistentDataType.INTEGER, countArmorStands(chunk));
} }
Integer armorStandCount = persistentDataContainer.get(namespacedKey, PersistentDataType.INTEGER); Integer armorStandCount = persistentDataContainer.get(namespacedKey, PersistentDataType.INTEGER);
if (armorStandCount == null) { if (armorStandCount == null) {
event.setCancelled(true); return Optional.empty();
logger.warning("Unable to retrieve armor stand count.");
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>");
return;
} }
consumer.apply(armorStandCount, event.getPlayer(), namespacedKey, persistentDataContainer); return Optional.of(new ChunkContainerAndNamespacedKey(namespacedKey, persistentDataContainer, armorStandCount));
} }
private void handleRightClickTrialSpawner(PlayerInteractEvent event, TrialSpawnerTimerConsumer consumer) { private void handleRightClickTrialSpawner(PlayerInteractEvent event, TrialSpawnerTimerConsumer consumer) {
@@ -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();
}
}
@@ -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);
@@ -0,0 +1,94 @@
package com.alttd.playerutils.util;
import com.alttd.playerutils.PlayerUtils;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* Encapsulates the April Fools' prank logic. - schedule(): registers the timed prank that only runs on April 1st and
* only during overworld night. - playExplosionAround(Player): immediately plays the explosion sound around the target
* for testing (no date/time checks), but still requires the target to be in the overworld named "world" to match
* intended environment.
*/
@Slf4j
public class AprilFoolsPrank {
private final PlayerUtils plugin;
public AprilFoolsPrank(PlayerUtils plugin) {
this.plugin = plugin;
}
/**
* Register the timed prank task. Safe to call on the main thread during plugin enable.
*/
public void schedule() {
// April 1st prank: during overworld night, every 2 minutes pick one player and play an explosion somewhere in a 30-block radius
Bukkit.getScheduler().runTaskTimer(plugin, () -> {
LocalDate now = LocalDate.now(ZoneId.systemDefault());
if (now.getMonth() != Month.APRIL || now.getDayOfMonth() != 1) {
return; // only active on April 1st
}
World world = Bukkit.getWorld("world");
if (world == null) {
return; // overworld not present
}
List<Player> players = world.getPlayers();
if (players.isEmpty()) {
return;
}
Player target = players.get(ThreadLocalRandom.current().nextInt(players.size()));
playOnce(world, target);
}, 20L, 20L * 60L * 2L); // start after 1s, repeat every 2 minutes
}
/**
* Trigger the prank once around the given player for testing. Returns true if executed. This method ignores the
* date and time checks so it can be tested easily, but it still requires the player to be in the overworld named
* "world".
*/
public boolean playSoundAroundPlayer(Player target) {
if (target == null) {
return false;
}
World world = target.getWorld();
if (!"world".equalsIgnoreCase(world.getName())) {
return false; // only intended for overworld
}
playOnce(world, target);
return true;
}
public List<Sound> getSounds() {
return List.of(Sound.ENTITY_GENERIC_EXPLODE, Sound.BLOCK_BELL_USE, Sound.ENTITY_SPIDER_AMBIENT,
Sound.ENTITY_TNT_PRIMED, Sound.ENTITY_CAT_BEG_FOR_FOOD, Sound.ENTITY_SHULKER_AMBIENT,
Sound.BLOCK_WOODEN_DOOR_CLOSE);
}
private void playOnce(World world, Player target) {
Location base = target.getLocation();
double radius = 25.0;
double r = ThreadLocalRandom.current().nextDouble(radius);
double theta = ThreadLocalRandom.current().nextDouble(Math.PI * 2);
double dx = r * Math.cos(theta);
double dz = r * Math.sin(theta);
Location soundLoc = new Location(world, base.getX() + dx, base.getY(), base.getZ() + dz);
List<Sound> sounds = getSounds();
Sound chosen = sounds.get(ThreadLocalRandom.current().nextInt(sounds.size()));
log.info("Playing sound {} at {} (for {})", chosen, soundLoc, target.getName());
world.playSound(soundLoc, chosen, 1f, 1.0f);
}
}
@@ -0,0 +1,106 @@
package com.alttd.playerutils.util;
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.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)
public static final int MAX_BOOK_BYTES = 30_000;
public static final int BIG_BOOK_BYTES = 10_000;
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;
}
}