1
mirror of https://github.com/Shiewk/SModeration.git synced 2026-04-29 06:34:17 +02:00

5 Commits

Author SHA1 Message Date
Shiewk 039b40bd53 (1.6.0) Switch to Paper plugin & support Folia 2025-07-09 12:59:43 +02:00
Shiewk a69c7cb426 Clear player title after chat input 2025-07-09 11:48:00 +02:00
Shiewk 8a147eb7cf Use AsyncPlayerPreLoginEvent instead of PlayerLoginEvent
because of its deprecation in Paper 1.21.6, this event disabled re-configuration APIs when a listener is registered for it; we don't want that to happen
2025-07-09 11:44:23 +02:00
Shiewk ecdd688a79 Fix SMod menu opening in newer versions 2025-07-09 11:08:29 +02:00
Shiewk 26166b5a73 Use API version 1.21.3, fix invsee 2025-07-09 10:28:54 +02:00
15 changed files with 190 additions and 35 deletions
+19 -6
View File
@@ -1,5 +1,6 @@
plugins { plugins {
id 'java' id 'java'
id("xyz.jpenilla.run-paper") version "2.3.1"
} }
group = 'de.shiewk' group = 'de.shiewk'
@@ -11,23 +12,35 @@ repositories {
name = "papermc-repo" name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/" url = "https://repo.papermc.io/repository/maven-public/"
} }
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
} }
processResources { processResources {
def props = [version: version] def props = [version: version]
inputs.properties props inputs.properties props
filteringCharset 'UTF-8' filteringCharset 'UTF-8'
filesMatching('plugin.yml') { filesMatching('paper-plugin.yml') {
expand props expand props
} }
} }
runPaper {
folia {
registerTask()
}
}
runServer {
minecraftVersion("1.21.7")
downloadPlugins {
// for testing from other client versions
modrinth("ViaVersion", "5.4.1")
modrinth("ViaBackwards", "5.4.1")
}
}
dependencies { dependencies {
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") //compileOnly "io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT"
compileOnly 'dev.folia:folia-api:1.21.4-R0.1-SNAPSHOT'
} }
jar { jar {
+1 -1
View File
@@ -1 +1 @@
pluginVersion = 1.5.0 pluginVersion = 1.6.0
@@ -2,9 +2,11 @@ package de.shiewk.smoderation.paper;
import de.shiewk.smoderation.paper.command.*; import de.shiewk.smoderation.paper.command.*;
import de.shiewk.smoderation.paper.config.SModerationConfig; import de.shiewk.smoderation.paper.config.SModerationConfig;
import de.shiewk.smoderation.paper.input.ChatInput;
import de.shiewk.smoderation.paper.input.ChatInputListener; import de.shiewk.smoderation.paper.input.ChatInputListener;
import de.shiewk.smoderation.paper.listener.*; import de.shiewk.smoderation.paper.listener.*;
import de.shiewk.smoderation.paper.storage.PunishmentContainer; import de.shiewk.smoderation.paper.storage.PunishmentContainer;
import de.shiewk.smoderation.paper.util.SchedulerUtil;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
@@ -72,7 +74,12 @@ public final class SModerationPaper extends JavaPlugin {
registerCommand(commands, new BanCommand()); registerCommand(commands, new BanCommand());
}); });
SchedulerUtil.scheduleGlobalRepeating(PLUGIN, CustomInventoryListener::onTick, 1, 1);
SchedulerUtil.scheduleGlobalRepeating(PLUGIN, ChatInput::tickAll, 1, 1);
container.load(SAVE_FILE); container.load(SAVE_FILE);
LOGGER.info("Folia: {}", SchedulerUtil.isFolia ? "yes" : "no");
} }
private void registerCommand(Commands commands, CommandProvider provider){ private void registerCommand(Commands commands, CommandProvider provider){
@@ -5,6 +5,7 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import de.shiewk.smoderation.paper.inventory.InvSeeEquipmentInventory; import de.shiewk.smoderation.paper.inventory.InvSeeEquipmentInventory;
import de.shiewk.smoderation.paper.inventory.InvSeeInventory;
import de.shiewk.smoderation.paper.util.CommandUtil; import de.shiewk.smoderation.paper.util.CommandUtil;
import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.argument.ArgumentTypes; import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
@@ -50,7 +51,7 @@ public final class InvseeCommand implements CommandProvider {
.append(target.teamDisplayName().colorIfAbsent(SECONDARY_COLOR)) .append(target.teamDisplayName().colorIfAbsent(SECONDARY_COLOR))
.append(text(".")) .append(text("."))
)); ));
sender.openInventory(target.getInventory()); new InvSeeInventory(sender, target).open();
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
@@ -30,7 +30,7 @@ public class ChatInput {
this.remainingTicks = remainingSeconds * 20 + 1; this.remainingTicks = remainingSeconds * 20 + 1;
} }
static void tickAll() { public static void tickAll() {
runningInputs.values().forEach(ChatInput::tick); runningInputs.values().forEach(ChatInput::tick);
} }
@@ -1,9 +1,7 @@
package de.shiewk.smoderation.paper.input; package de.shiewk.smoderation.paper.input;
import com.destroystokyo.paper.event.server.ServerTickStartEvent;
import io.papermc.paper.event.player.AsyncChatEvent; import io.papermc.paper.event.player.AsyncChatEvent;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
@@ -18,6 +16,7 @@ public class ChatInputListener implements Listener {
if (input != null){ if (input != null){
event.setCancelled(true); event.setCancelled(true);
input.getAction().accept(event.message()); input.getAction().accept(event.message());
event.getPlayer().clearTitle();
} }
} }
@@ -25,8 +24,4 @@ public class ChatInputListener implements Listener {
runningInputs.remove(event.getPlayer()); runningInputs.remove(event.getPlayer());
} }
@EventHandler(priority = EventPriority.MONITOR) public void onServerTickStart(ServerTickStartEvent event){
ChatInput.tickAll();
}
} }
@@ -1,6 +1,7 @@
package de.shiewk.smoderation.paper.inventory; package de.shiewk.smoderation.paper.inventory;
import de.shiewk.smoderation.paper.SModerationPaper; import de.shiewk.smoderation.paper.SModerationPaper;
import de.shiewk.smoderation.paper.util.SchedulerUtil;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
@@ -47,7 +48,7 @@ public class InvSeeEquipmentInventory implements AutoUpdatingCustomInventory {
if (viewer.hasPermission("smod.invsee.modify") && !subject.hasPermission("smod.invsee.preventmodify")){ if (viewer.hasPermission("smod.invsee.modify") && !subject.hasPermission("smod.invsee.preventmodify")){
event.setCancelled(false); event.setCancelled(false);
changing = true; changing = true;
Bukkit.getScheduler().scheduleSyncDelayedTask(SModerationPaper.PLUGIN, () -> { SchedulerUtil.scheduleGlobal(SModerationPaper.PLUGIN, () -> {
changing = false; changing = false;
final EntityEquipment equipment = subject.getEquipment(); final EntityEquipment equipment = subject.getEquipment();
equipment.setHelmet(inventory.getItem(0)); equipment.setHelmet(inventory.getItem(0));
@@ -0,0 +1,70 @@
package de.shiewk.smoderation.paper.inventory;
import de.shiewk.smoderation.paper.SModerationPaper;
import de.shiewk.smoderation.paper.util.SchedulerUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.text.Component.text;
public class InvSeeInventory implements AutoUpdatingCustomInventory {
private final HumanEntity viewer;
private final HumanEntity subject;
private final Inventory subjectInventory;
private final Inventory inventory;
private boolean changing = false;
public InvSeeInventory(HumanEntity viewer, HumanEntity subject) {
this.viewer = viewer;
this.subject = subject;
this.subjectInventory = subject.getInventory();
this.inventory = Bukkit.createInventory(this, 36, text("Player inventory"));
}
@Override
public void refresh() {
if (!changing){
loadContents();
}
}
private void loadContents() {
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, subjectInventory.getItem(i));
}
}
@Override
public void open() {
refresh();
viewer.openInventory(getInventory());
}
@Override
public void click(ItemStack stack, InventoryClickEvent event) {
if (viewer.hasPermission("smod.invsee.modify") && !subject.hasPermission("smod.invsee.preventmodify")){
event.setCancelled(false);
changing = true;
SchedulerUtil.scheduleGlobal(SModerationPaper.PLUGIN, () -> {
changing = false;
applyChanges();
});
}
}
private void applyChanges() {
for (int i = 0; i < inventory.getSize(); i++) {
subjectInventory.setItem(i, inventory.getItem(i));
}
}
@Override
public @NotNull Inventory getInventory() {
return inventory;
}
}
@@ -8,7 +8,9 @@ import de.shiewk.smoderation.paper.punishments.Punishment;
import de.shiewk.smoderation.paper.punishments.PunishmentType; import de.shiewk.smoderation.paper.punishments.PunishmentType;
import de.shiewk.smoderation.paper.storage.PunishmentContainer; import de.shiewk.smoderation.paper.storage.PunishmentContainer;
import de.shiewk.smoderation.paper.util.PlayerUtil; import de.shiewk.smoderation.paper.util.PlayerUtil;
import de.shiewk.smoderation.paper.util.SchedulerUtil;
import de.shiewk.smoderation.paper.util.TimeUtil; import de.shiewk.smoderation.paper.util.TimeUtil;
import io.papermc.paper.datacomponent.DataComponentTypes;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@@ -20,7 +22,6 @@ import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.inventory.meta.SkullMeta;
@@ -114,12 +115,12 @@ public class SModMenu extends PageableCustomInventory {
} }
public void promptSearchQuery(){ public void promptSearchQuery(){
Bukkit.getScheduler().scheduleSyncDelayedTask(PLUGIN, player::closeInventory); SchedulerUtil.scheduleForEntity(PLUGIN, player, player::closeInventory);
ChatInput.prompt(player, component -> { ChatInput.prompt(player, component -> {
if (component instanceof TextComponent text){ if (component instanceof TextComponent text){
this.searchQuery = text.content(); this.searchQuery = text.content();
// chat event is async // chat event is async
Bukkit.getScheduler().scheduleSyncDelayedTask(PLUGIN, this::open); SchedulerUtil.scheduleForEntity(PLUGIN, player, this::open);
} }
}, text("Enter your search query in chat").color(SECONDARY_COLOR), 30); }, text("Enter your search query in chat").color(SECONDARY_COLOR), 30);
} }
@@ -257,7 +258,12 @@ public class SModMenu extends PageableCustomInventory {
private ItemStack createSearchItem(){ private ItemStack createSearchItem(){
final ItemStack stack = new ItemStack(Material.FLOWER_BANNER_PATTERN); final ItemStack stack = new ItemStack(Material.FLOWER_BANNER_PATTERN);
stack.editMeta(meta -> { stack.editMeta(meta -> {
meta.addItemFlags(ItemFlag.HIDE_ITEM_SPECIFICS); try {
stack.setData(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP);
} catch (NoSuchFieldError e) {
// that component is no longer present under that name,
// we just create the stack without it instead of throwing
}
meta.displayName(applyFormatting(text("Search").color(PRIMARY_COLOR))); meta.displayName(applyFormatting(text("Search").color(PRIMARY_COLOR)));
final ArrayList<Component> lore = new ArrayList<>(List.of( final ArrayList<Component> lore = new ArrayList<>(List.of(
Component.empty(), Component.empty(),
@@ -1,6 +1,5 @@
package de.shiewk.smoderation.paper.listener; package de.shiewk.smoderation.paper.listener;
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
import de.shiewk.smoderation.paper.inventory.AutoUpdatingCustomInventory; import de.shiewk.smoderation.paper.inventory.AutoUpdatingCustomInventory;
import de.shiewk.smoderation.paper.inventory.CustomInventory; import de.shiewk.smoderation.paper.inventory.CustomInventory;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -26,8 +25,7 @@ public class CustomInventoryListener implements Listener {
} }
} }
@EventHandler(priority = EventPriority.LOWEST) public static void onTick(){
public void onServerTickEnd(ServerTickEndEvent event){
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (onlinePlayer.getOpenInventory().getTopInventory().getHolder() instanceof AutoUpdatingCustomInventory ci) { if (onlinePlayer.getOpenInventory().getTopInventory().getHolder() instanceof AutoUpdatingCustomInventory ci) {
ci.refresh(); ci.refresh();
@@ -11,7 +11,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldSaveEvent;
import static de.shiewk.smoderation.paper.SModerationPaper.CHAT_PREFIX; import static de.shiewk.smoderation.paper.SModerationPaper.CHAT_PREFIX;
@@ -19,13 +19,16 @@ import static de.shiewk.smoderation.paper.SModerationPaper.CHAT_PREFIX;
public class PunishmentListener implements Listener { public class PunishmentListener implements Listener {
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent event){ public void onPlayerLogin(AsyncPlayerPreLoginEvent event){
// Have to use AsyncPlayerPreLoginEvent since PlayerLoginEvent is deprecated in newer versions
// I would use the new PlayerConnectionValidateLoginEvent but there is literally no API available
// there to get player's UUIDs
Punishment punishment = SModerationPaper.container.find(p -> Punishment punishment = SModerationPaper.container.find(p ->
p.type == PunishmentType.BAN p.type == PunishmentType.BAN
&& p.to.equals(event.getPlayer().getUniqueId()) && p.to.equals(event.getUniqueId())
&& p.isActive()); && p.isActive());
if (punishment != null){ if (punishment != null){
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CHAT_PREFIX.append(punishment.playerMessage())); event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_BANNED, CHAT_PREFIX.append(punishment.playerMessage()));
} }
} }
@@ -63,7 +66,7 @@ public class PunishmentListener implements Listener {
@EventHandler @EventHandler
public void onWorldSave(WorldSaveEvent event){ public void onWorldSave(WorldSaveEvent event){
if (event.getWorld().equals(Bukkit.getServer().getWorlds().get(0))){ if (event.getWorld().equals(Bukkit.getServer().getWorlds().getFirst())){
SModerationPaper.container.save(SModerationPaper.SAVE_FILE); SModerationPaper.container.save(SModerationPaper.SAVE_FILE);
} }
} }
@@ -2,6 +2,7 @@ package de.shiewk.smoderation.paper.listener;
import de.shiewk.smoderation.paper.SModerationPaper; import de.shiewk.smoderation.paper.SModerationPaper;
import de.shiewk.smoderation.paper.command.VanishCommand; import de.shiewk.smoderation.paper.command.VanishCommand;
import de.shiewk.smoderation.paper.util.SchedulerUtil;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -52,7 +53,7 @@ public class VanishListener implements Listener {
if (message != null){ if (message != null){
broadcast(message.color(null)); broadcast(message.color(null));
} }
Bukkit.getScheduler().scheduleSyncDelayedTask(SModerationPaper.PLUGIN, () -> { SchedulerUtil.scheduleForEntity(SModerationPaper.PLUGIN, player, () -> {
player.sendMessage(SModerationPaper.CHAT_PREFIX player.sendMessage(SModerationPaper.CHAT_PREFIX
.decorate(TextDecoration.BOLD) .decorate(TextDecoration.BOLD)
.append( .append(
@@ -69,7 +70,7 @@ public class VanishListener implements Listener {
player.getPersistentDataContainer().remove(VanishCommand.KEY_VANISHED); player.getPersistentDataContainer().remove(VanishCommand.KEY_VANISHED);
} }
} }
Bukkit.getScheduler().scheduleSyncDelayedTask(SModerationPaper.PLUGIN, () -> { SchedulerUtil.scheduleForEntity(SModerationPaper.PLUGIN, player, () -> {
if (player.hasPermission("smod.vanish.see")){ if (player.hasPermission("smod.vanish.see")){
for (Player vanishedPlayer : VanishCommand.getVanishedPlayers()) { for (Player vanishedPlayer : VanishCommand.getVanishedPlayers()) {
// to show visible vanished players // to show visible vanished players
@@ -37,7 +37,7 @@ public class PunishmentContainer {
} }
public @Nullable Punishment find(Predicate<Punishment> predicate){ public @Nullable Punishment find(Predicate<Punishment> predicate){
for (Punishment punishment : new CopyOnWriteArrayList<>(punishments)) { for (Punishment punishment : punishments) {
if (predicate.test(punishment)){ if (predicate.test(punishment)){
return punishment; return punishment;
} }
@@ -47,7 +47,7 @@ public class PunishmentContainer {
public @NotNull List<Punishment> findAll(Predicate<Punishment> predicate){ public @NotNull List<Punishment> findAll(Predicate<Punishment> predicate){
List<Punishment> found = new ArrayList<>(); List<Punishment> found = new ArrayList<>();
for (Punishment punishment : new CopyOnWriteArrayList<>(punishments)) { for (Punishment punishment : punishments) {
if (predicate.test(punishment)){ if (predicate.test(punishment)){
found.add(punishment); found.add(punishment);
} }
@@ -74,7 +74,7 @@ public class PunishmentContainer {
return new ArrayList<>(punishments); return new ArrayList<>(punishments);
} }
public void load(File file){ public synchronized void load(File file){
final ComponentLogger logger = SModerationPaper.LOGGER; final ComponentLogger logger = SModerationPaper.LOGGER;
try { try {
logger.info("Loading from {}", file.getPath()); logger.info("Loading from {}", file.getPath());
@@ -99,7 +99,7 @@ public class PunishmentContainer {
} }
} }
public void save(File file) { public synchronized void save(File file) {
final ComponentLogger logger = SModerationPaper.LOGGER; final ComponentLogger logger = SModerationPaper.LOGGER;
try { try {
if (!file.isFile()){ if (!file.isFile()){
@@ -0,0 +1,58 @@
package de.shiewk.smoderation.paper.util;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
/**
* This class provides some convenience methods that make Folia support easier
*/
public final class SchedulerUtil {
private SchedulerUtil(){}
public static final boolean isFolia;
static {
boolean folia;
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
folia = true;
} catch (ClassNotFoundException e) {
folia = false;
}
isFolia = folia;
}
public static void scheduleForEntity(Plugin plugin, Entity entity, Runnable task, int delayTicks){
if (isFolia){
entity.getScheduler().execute(plugin, task, null, delayTicks);
} else {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, task, delayTicks);
}
}
public static void scheduleForEntity(Plugin plugin, Entity entity, Runnable task){
if (isFolia){
entity.getScheduler().run(plugin, t -> task.run(), null);
} else {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, task);
}
}
public static void scheduleGlobalRepeating(Plugin plugin, Runnable task, int delay, int interval){
if (isFolia){
Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, t -> task.run(), delay, interval);
} else {
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, interval);
}
}
public static void scheduleGlobal(Plugin plugin, Runnable task){
if (isFolia){
Bukkit.getGlobalRegionScheduler().run(plugin, t -> task.run());
} else {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, task);
}
}
}
@@ -1,11 +1,13 @@
name: SModeration name: SModeration
version: '${version}' version: '${version}'
main: de.shiewk.smoderation.paper.SModerationPaper main: de.shiewk.smoderation.paper.SModerationPaper
api-version: '1.21' website: https://github.com/Shiewk/SModeration
api-version: '1.21.3'
load: STARTUP load: STARTUP
authors: authors:
- Shiewk - Shiewk
description: "SModeration is an easy-to-use minecraft plugin for moderating your server." description: "SModeration is an easy-to-use minecraft plugin for moderating your server."
folia-supported: true
permissions: permissions:
smod.mute: smod.mute:
default: op default: op