Kick/(un)freeze player when they lose/gain additional requirements

This commit is contained in:
Vankka 2024-06-29 15:09:34 +03:00
parent a81e8305a0
commit f285669990
No known key found for this signature in database
GPG Key ID: 62E48025ED4E7EBB
14 changed files with 333 additions and 344 deletions

View File

@ -25,7 +25,6 @@ package com.discordsrv.api.module.type;
import com.discordsrv.api.component.MinecraftComponent; import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.module.Module; import com.discordsrv.api.module.Module;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.api.punishment.Punishment; import com.discordsrv.api.punishment.Punishment;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -40,7 +39,6 @@ public interface PunishmentModule extends Module {
CompletableFuture<@Nullable Punishment> getBan(@NotNull UUID playerUUID); CompletableFuture<@Nullable Punishment> getBan(@NotNull UUID playerUUID);
CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher); CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher);
CompletableFuture<Void> removeBan(@NotNull UUID playerUUID); CompletableFuture<Void> removeBan(@NotNull UUID playerUUID);
CompletableFuture<Void> kickPlayer(@NotNull DiscordSRVPlayer player, @NotNull MinecraftComponent message);
} }
interface Mutes extends PunishmentModule { interface Mutes extends PunishmentModule {

View File

@ -66,7 +66,7 @@ public class PaperComponentHandle<T> {
unrelocated = handle.invoke(target); unrelocated = handle.invoke(target);
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
if (unrelocated != null) { if (unrelocated != null) {
return ComponentUtil.fromUnrelocated(unrelocated); return ComponentUtil.fromUnrelocated((com.discordsrv.unrelocate.net.kyori.adventure.text.Component) unrelocated);
} }
} }

View File

@ -18,16 +18,23 @@
package com.discordsrv.bukkit.player; package com.discordsrv.bukkit.player;
import com.discordsrv.common.component.util.ComponentUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Locale; import java.util.Locale;
@SuppressWarnings("JavaLangInvokeHandleSignature") // Unrelocate
public final class PaperPlayer { public final class PaperPlayer {
private PaperPlayer() {} private PaperPlayer() {}
private static final boolean localeMethodExists; private static final boolean LOCALE_METHOD_EXISTS;
private static final boolean getLocaleMethodExists; private static final boolean GETLOCALE_METHOD_EXISTS;
private static final MethodHandle KICK_COMPONENT_HANDLE;
static { static {
Class<?> playerClass = Player.class; Class<?> playerClass = Player.class;
@ -41,18 +48,40 @@ public final class PaperPlayer {
playerClass.getMethod("getLocale"); playerClass.getMethod("getLocale");
getLocale = true; getLocale = true;
} catch (ReflectiveOperationException ignored) {} } catch (ReflectiveOperationException ignored) {}
localeMethodExists = locale; LOCALE_METHOD_EXISTS = locale;
getLocaleMethodExists = getLocale; GETLOCALE_METHOD_EXISTS = getLocale;
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = null;
try {
handle = lookup.findVirtual(Player.class, "kick", MethodType.methodType(
void.class,
com.discordsrv.unrelocate.net.kyori.adventure.text.Component.class
));
} catch (ReflectiveOperationException ignored) {}
KICK_COMPONENT_HANDLE = handle;
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static Locale getLocale(Player player) { public static Locale getLocale(Player player) {
if (localeMethodExists) { if (LOCALE_METHOD_EXISTS) {
return player.locale(); return player.locale();
} else if (getLocaleMethodExists) { } else if (GETLOCALE_METHOD_EXISTS) {
return Locale.forLanguageTag(player.getLocale()); return Locale.forLanguageTag(player.getLocale());
} else { } else {
return null; return null;
} }
} }
public static boolean isKickAvailable() {
return KICK_COMPONENT_HANDLE != null;
}
public static void kick(Player player, Component reason) {
try {
KICK_COMPONENT_HANDLE.invokeExact(player, ComponentUtil.toUnrelocated(ComponentUtil.toAPI(reason)));
} catch (Throwable e) {
throw new RuntimeException("Failed to kick player", e);
}
}
} }

View File

@ -31,7 +31,6 @@ import com.discordsrv.bukkit.config.manager.BukkitMessagesConfigManager;
import com.discordsrv.bukkit.console.BukkitConsole; import com.discordsrv.bukkit.console.BukkitConsole;
import com.discordsrv.bukkit.listener.BukkitConnectionListener; import com.discordsrv.bukkit.listener.BukkitConnectionListener;
import com.discordsrv.bukkit.listener.BukkitDeathListener; import com.discordsrv.bukkit.listener.BukkitDeathListener;
import com.discordsrv.bukkit.listener.BukkitRequiredLinkingListener;
import com.discordsrv.bukkit.listener.BukkitStatusMessageListener; import com.discordsrv.bukkit.listener.BukkitStatusMessageListener;
import com.discordsrv.bukkit.listener.award.BukkitAwardForwarder; import com.discordsrv.bukkit.listener.award.BukkitAwardForwarder;
import com.discordsrv.bukkit.listener.chat.BukkitChatForwarder; import com.discordsrv.bukkit.listener.chat.BukkitChatForwarder;
@ -70,7 +69,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
private final BukkitPlayerProvider playerProvider; private final BukkitPlayerProvider playerProvider;
private final BukkitPluginManager pluginManager; private final BukkitPluginManager pluginManager;
private AbstractBukkitCommandHandler commandHandler; private AbstractBukkitCommandHandler commandHandler;
private final BukkitRequiredLinkingListener requiredLinkingListener;
private final BukkitGameCommandExecutionHelper autoCompleteHelper; private final BukkitGameCommandExecutionHelper autoCompleteHelper;
private final BukkitConnectionConfigManager connectionConfigManager; private final BukkitConnectionConfigManager connectionConfigManager;
@ -101,7 +99,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
load(); load();
this.requiredLinkingListener = new BukkitRequiredLinkingListener(this);
this.autoCompleteHelper = new BukkitGameCommandExecutionHelper(this); this.autoCompleteHelper = new BukkitGameCommandExecutionHelper(this);
} }
@ -238,7 +235,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
protected void disable() { protected void disable() {
super.disable(); super.disable();
requiredLinkingListener.disable();
audiences.close(); audiences.close();
} }

View File

@ -20,7 +20,6 @@ package com.discordsrv.bukkit.ban;
import com.discordsrv.api.component.MinecraftComponent; import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.module.type.PunishmentModule; import com.discordsrv.api.module.type.PunishmentModule;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.api.punishment.Punishment; import com.discordsrv.api.punishment.Punishment;
import com.discordsrv.bukkit.BukkitDiscordSRV; import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.common.bansync.BanSyncModule; import com.discordsrv.common.bansync.BanSyncModule;
@ -126,17 +125,4 @@ public class BukkitBanModule extends AbstractModule<BukkitDiscordSRV> implements
return null; return null;
}); });
} }
@Override
public CompletableFuture<Void> kickPlayer(@NotNull DiscordSRVPlayer srvPlayer, @NotNull MinecraftComponent message) {
Player player = discordSRV.server().getPlayer(srvPlayer.uniqueId());
if (player == null) {
return CompletableFuture.completedFuture(null);
}
return discordSRV.scheduler().executeOnMainThread(
player,
() -> player.kickPlayer(BukkitComponentSerializer.legacy().serialize(ComponentUtil.fromAPI(message)))
);
}
} }

View File

@ -1,306 +0,0 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2024 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.bukkit.listener;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.config.main.BukkitRequiredLinkingConfig;
import com.discordsrv.bukkit.requiredlinking.BukkitRequiredLinkingModule;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.linking.ServerRequiredLinkingConfig;
import com.discordsrv.common.linking.LinkStore;
import com.discordsrv.common.player.IPlayer;
import com.github.benmanes.caffeine.cache.Cache;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.*;
import org.bukkit.event.player.*;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class BukkitRequiredLinkingListener implements Listener {
private final BukkitDiscordSRV discordSRV;
private final Cache<UUID, Boolean> linkCheckRateLimit;
public BukkitRequiredLinkingListener(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.linkCheckRateLimit = discordSRV.caffeineBuilder()
.expireAfterWrite(LinkStore.LINKING_CODE_RATE_LIMIT)
.build();
register(PlayerLoginEvent.class, this::handle);
register(AsyncPlayerPreLoginEvent.class, this::handle);
discordSRV.server().getPluginManager().registerEvents(this, discordSRV.plugin());
}
@SuppressWarnings("unchecked")
private <T extends Event> void register(Class<T> eventType, BiConsumer<T, EventPriority> eventConsumer) {
for (EventPriority priority : EventPriority.values()) {
if (priority == EventPriority.MONITOR) {
continue;
}
discordSRV.server().getPluginManager().registerEvent(
eventType,
this,
priority,
(listener, event) -> eventConsumer.accept((T) event, priority),
discordSRV.plugin(),
true
);
}
}
public void disable() {
HandlerList.unregisterAll(this);
}
private BukkitRequiredLinkingModule getModule() {
int tries = 0;
BukkitRequiredLinkingModule module;
do {
module = discordSRV.getModule(BukkitRequiredLinkingModule.class);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
tries++;
} while (module == null || tries >= 50);
return module;
}
private CompletableFuture<Component> getBlockReason(UUID playerUUID, String playerName, boolean join) {
BukkitRequiredLinkingModule module = getModule();
if (module == null) {
Component message = ComponentUtil.fromAPI(
discordSRV.messagesConfig().minecraft.unableToLinkAtThisTime.textBuilder().build()
);
return CompletableFuture.completedFuture(message);
}
return module.getBlockReason(playerUUID, playerName, join);
}
//
// Kick
//
private void handle(AsyncPlayerPreLoginEvent event, EventPriority priority) {
handle(
"AsyncPlayerPreLoginEvent",
priority,
event.getUniqueId(),
event.getName(),
() -> event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED ? event.getLoginResult().name() : null,
text -> event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, text)
);
}
private void handle(PlayerLoginEvent event, EventPriority priority) {
Player player = event.getPlayer();
handle(
"PlayerLoginEvent",
priority,
player.getUniqueId(),
player.getName(),
() -> event.getResult() != PlayerLoginEvent.Result.ALLOWED ? event.getResult().name() : null,
text -> event.disallow(PlayerLoginEvent.Result.KICK_OTHER, text)
);
}
private void handle(
String eventType,
EventPriority priority,
UUID playerUUID,
String playerName,
Supplier<String> alreadyBlocked,
Consumer<String> disallow
) {
BukkitRequiredLinkingConfig config = discordSRV.config().requiredLinking;
if (!config.enabled || config.action != ServerRequiredLinkingConfig.Action.KICK
|| !eventType.equals(config.kick.event) || !priority.name().equals(config.kick.priority)) {
return;
}
String blockType = alreadyBlocked.get();
if (blockType != null) {
discordSRV.logger().debug(playerName + " is already blocked for " + eventType + "/" + priority + " (" + blockType + ")");
return;
}
Component kickReason = getBlockReason(playerUUID, playerName, true).join();
if (kickReason != null) {
disallow.accept(BukkitComponentSerializer.legacy().serialize(kickReason));
}
}
//
// Freeze
//
private final Map<UUID, Component> frozen = new ConcurrentHashMap<>();
private boolean isFrozen(Player player) {
return frozen.containsKey(player.getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
return;
}
if (discordSRV.isShutdown()) {
return;
} else if (!discordSRV.isReady()) {
try {
discordSRV.waitForStatus(DiscordSRV.Status.CONNECTED);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
BukkitRequiredLinkingConfig config = discordSRV.config().requiredLinking;
if (!config.enabled || config.action != ServerRequiredLinkingConfig.Action.FREEZE) {
return;
}
Component blockReason = getBlockReason(event.getUniqueId(), event.getName(), false).join();
if (blockReason != null) {
frozen.put(event.getUniqueId(), blockReason);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onLogin(PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
frozen.remove(event.getPlayer().getUniqueId());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
Component blockReason = frozen.get(uuid);
if (blockReason == null) {
return;
}
IPlayer player = discordSRV.playerProvider().player(uuid);
if (player == null) {
throw new IllegalStateException("Player not available: " + uuid);
}
player.sendMessage(blockReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Component freezeReason = frozen.get(event.getPlayer().getUniqueId());
if (freezeReason == null) {
return;
}
Location from = event.getFrom(), to = event.getTo();
if (from.getWorld().getName().equals(to.getWorld().getName())
&& from.getBlockX() == to.getBlockX()
&& from.getBlockZ() == to.getBlockZ()
&& from.getBlockY() >= to.getBlockY()) {
return;
}
event.setTo(
new Location(
from.getWorld(),
from.getBlockX() + 0.5,
from.getBlockY(),
from.getBlockZ() + 0.5,
from.getYaw(),
from.getPitch()
)
);
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
player.sendMessage(freezeReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerChat(AsyncPlayerChatEvent event) {
Component freezeReason = frozen.get(event.getPlayer().getUniqueId());
if (freezeReason == null) {
event.getRecipients().removeIf(this::isFrozen);
return;
}
event.setCancelled(true);
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
player.sendMessage(freezeReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
if (!isFrozen(event.getPlayer())) {
return;
}
event.setCancelled(true);
String message = event.getMessage();
if (message.startsWith("/")) message = message.substring(1);
if (message.equals("discord link") || message.equals("link")) {
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
if (linkCheckRateLimit.getIfPresent(player.uniqueId()) != null) {
player.sendMessage(discordSRV.messagesConfig(player).pleaseWaitBeforeRunningThatCommandAgain.asComponent());
return;
}
linkCheckRateLimit.put(player.uniqueId(), true);
player.sendMessage(Component.text("Checking..."));
UUID uuid = player.uniqueId();
getBlockReason(uuid, player.username(), false).whenComplete((reason, t) -> {
if (t != null) {
return;
}
if (reason == null) {
frozen.remove(uuid);
} else {
frozen.put(uuid, reason);
player.sendMessage(reason);
}
});
}
}
}

View File

@ -26,12 +26,14 @@ import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.player.IPlayer; import com.discordsrv.common.player.IPlayer;
import com.discordsrv.common.player.provider.model.SkinInfo; import com.discordsrv.common.player.provider.model.SkinInfo;
import net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.CompletableFuture;
public class BukkitPlayer extends BukkitCommandSender implements IPlayer { public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
@ -64,6 +66,17 @@ public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
return player.getName(); return player.getName();
} }
@Override
public CompletableFuture<Void> kick(Component component) {
return discordSRV.scheduler().executeOnMainThread(player, () -> {
if (PaperPlayer.isKickAvailable()) {
PaperPlayer.kick(player, component);
} else {
player.kickPlayer(BukkitComponentSerializer.legacy().serialize(component));
}
});
}
@Override @Override
public @Nullable SkinInfo skinInfo() { public @Nullable SkinInfo skinInfo() {
return SpigotPlayer.getSkinInfo(player); return SpigotPlayer.getSkinInfo(player);

View File

@ -20,14 +20,35 @@ package com.discordsrv.bukkit.requiredlinking;
import com.discordsrv.bukkit.BukkitDiscordSRV; import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.config.main.BukkitRequiredLinkingConfig; import com.discordsrv.bukkit.config.main.BukkitRequiredLinkingConfig;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.linking.ServerRequiredLinkingConfig;
import com.discordsrv.common.linking.LinkStore;
import com.discordsrv.common.linking.requirelinking.ServerRequireLinkingModule; import com.discordsrv.common.linking.requirelinking.ServerRequireLinkingModule;
import com.discordsrv.common.player.IPlayer; import com.discordsrv.common.player.IPlayer;
import org.bukkit.event.Listener; import com.github.benmanes.caffeine.cache.Cache;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.*;
import org.bukkit.event.player.*;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class BukkitRequiredLinkingModule extends ServerRequireLinkingModule<BukkitDiscordSRV> implements Listener { public class BukkitRequiredLinkingModule extends ServerRequireLinkingModule<BukkitDiscordSRV> implements Listener {
private final Cache<UUID, Boolean> linkCheckRateLimit;
public BukkitRequiredLinkingModule(BukkitDiscordSRV discordSRV) { public BukkitRequiredLinkingModule(BukkitDiscordSRV discordSRV) {
super(discordSRV); super(discordSRV);
this.linkCheckRateLimit = discordSRV.caffeineBuilder()
.expireAfterWrite(LinkStore.LINKING_CODE_RATE_LIMIT)
.build();
} }
@Override @Override
@ -35,12 +56,254 @@ public class BukkitRequiredLinkingModule extends ServerRequireLinkingModule<Bukk
return discordSRV.config().requiredLinking; return discordSRV.config().requiredLinking;
} }
@Override
public void enable() {
super.enable();
register(PlayerLoginEvent.class, this::handle);
register(AsyncPlayerPreLoginEvent.class, this::handle);
discordSRV.server().getPluginManager().registerEvents(this, discordSRV.plugin());
}
public void disable() {
HandlerList.unregisterAll(this);
}
@SuppressWarnings("unchecked")
private <T extends Event> void register(Class<T> eventType, BiConsumer<T, EventPriority> eventConsumer) {
for (EventPriority priority : EventPriority.values()) {
if (priority == EventPriority.MONITOR) {
continue;
}
discordSRV.server().getPluginManager().registerEvent(
eventType,
this,
priority,
(listener, event) -> eventConsumer.accept((T) event, priority),
discordSRV.plugin(),
true
);
}
}
@Override @Override
public void recheck(IPlayer player) { public void recheck(IPlayer player) {
getBlockReason(player.uniqueId(), player.username(), false).whenComplete((component, throwable) -> { getBlockReason(player.uniqueId(), player.username(), false).whenComplete((component, throwable) -> {
if (component != null) { if (component != null) {
// TODO: handle switch (action()) {
case KICK:
player.kick(component);
break;
case FREEZE:
freeze(player, component);
break;
}
} else if (action() == ServerRequiredLinkingConfig.Action.FREEZE) {
frozen.remove(player.uniqueId());
} }
}); });
} }
public ServerRequiredLinkingConfig.Action action() {
return config().action;
}
//
// Kick
//
private void handle(AsyncPlayerPreLoginEvent event, EventPriority priority) {
handle(
"AsyncPlayerPreLoginEvent",
priority,
event.getUniqueId(),
event.getName(),
() -> event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED ? event.getLoginResult().name() : null,
text -> event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, text)
);
}
private void handle(PlayerLoginEvent event, EventPriority priority) {
Player player = event.getPlayer();
handle(
"PlayerLoginEvent",
priority,
player.getUniqueId(),
player.getName(),
() -> event.getResult() != PlayerLoginEvent.Result.ALLOWED ? event.getResult().name() : null,
text -> event.disallow(PlayerLoginEvent.Result.KICK_OTHER, text)
);
}
private void handle(
String eventType,
EventPriority priority,
UUID playerUUID,
String playerName,
Supplier<String> alreadyBlocked,
Consumer<String> disallow
) {
BukkitRequiredLinkingConfig config = config();
if (!config.enabled || config.action != ServerRequiredLinkingConfig.Action.KICK
|| !eventType.equals(config.kick.event) || !priority.name().equals(config.kick.priority)) {
return;
}
String blockType = alreadyBlocked.get();
if (blockType != null) {
discordSRV.logger().debug(playerName + " is already blocked for " + eventType + "/" + priority + " (" + blockType + ")");
return;
}
Component kickReason = getBlockReason(playerUUID, playerName, true).join();
if (kickReason != null) {
disallow.accept(BukkitComponentSerializer.legacy().serialize(kickReason));
}
}
//
// Freeze
//
private final Map<UUID, Component> frozen = new ConcurrentHashMap<>();
private boolean isFrozen(Player player) {
return frozen.containsKey(player.getUniqueId());
}
private void freeze(IPlayer player, Component blockReason) {
frozen.put(player.uniqueId(), blockReason);
player.sendMessage(blockReason);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
return;
}
if (discordSRV.isShutdown()) {
return;
} else if (!discordSRV.isReady()) {
try {
discordSRV.waitForStatus(DiscordSRV.Status.CONNECTED);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
BukkitRequiredLinkingConfig config = config();
if (!config.enabled || config.action != ServerRequiredLinkingConfig.Action.FREEZE) {
return;
}
Component blockReason = getBlockReason(event.getUniqueId(), event.getName(), false).join();
if (blockReason != null) {
frozen.put(event.getUniqueId(), blockReason);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onLogin(PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
frozen.remove(event.getPlayer().getUniqueId());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
Component blockReason = frozen.get(uuid);
if (blockReason == null) {
return;
}
IPlayer player = discordSRV.playerProvider().player(uuid);
if (player == null) {
throw new IllegalStateException("Player not available: " + uuid);
}
player.sendMessage(blockReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Component freezeReason = frozen.get(event.getPlayer().getUniqueId());
if (freezeReason == null) {
return;
}
Location from = event.getFrom(), to = event.getTo();
if (from.getWorld().getName().equals(to.getWorld().getName())
&& from.getBlockX() == to.getBlockX()
&& from.getBlockZ() == to.getBlockZ()
&& from.getBlockY() >= to.getBlockY()) {
return;
}
event.setTo(
new Location(
from.getWorld(),
from.getBlockX() + 0.5,
from.getBlockY(),
from.getBlockZ() + 0.5,
from.getYaw(),
from.getPitch()
)
);
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
player.sendMessage(freezeReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerChat(AsyncPlayerChatEvent event) {
Component freezeReason = frozen.get(event.getPlayer().getUniqueId());
if (freezeReason == null) {
event.getRecipients().removeIf(this::isFrozen);
return;
}
event.setCancelled(true);
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
player.sendMessage(freezeReason);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
if (!isFrozen(event.getPlayer())) {
return;
}
event.setCancelled(true);
String message = event.getMessage();
if (message.startsWith("/")) message = message.substring(1);
if (message.equals("discord link") || message.equals("link")) {
IPlayer player = discordSRV.playerProvider().player(event.getPlayer());
if (linkCheckRateLimit.getIfPresent(player.uniqueId()) != null) {
player.sendMessage(discordSRV.messagesConfig(player).pleaseWaitBeforeRunningThatCommandAgain.asComponent());
return;
}
linkCheckRateLimit.put(player.uniqueId(), true);
player.sendMessage(Component.text("Checking..."));
UUID uuid = player.uniqueId();
getBlockReason(uuid, player.username(), false).whenComplete((reason, t) -> {
if (t != null) {
return;
}
if (reason == null) {
frozen.remove(uuid);
} else {
freeze(player, reason);
}
});
}
}
} }

View File

@ -309,7 +309,7 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
.applyPlaceholderService() .applyPlaceholderService()
.build(); .build();
return bans.kickPlayer(player, kickMessage); return player.kick(ComponentUtil.fromAPI(kickMessage));
}) })
.thenApply(v -> GenericSyncResults.ADD_GAME); .thenApply(v -> GenericSyncResults.ADD_GAME);
} else { } else {

View File

@ -90,7 +90,7 @@ public final class ComponentUtil {
} }
} }
public static MinecraftComponent fromUnrelocated(@NotNull Object unrelocatedAdventure) { public static MinecraftComponent fromUnrelocated(@NotNull com.discordsrv.unrelocate.net.kyori.adventure.text.Component unrelocatedAdventure) {
MinecraftComponentImpl component = MinecraftComponentImpl.empty(); MinecraftComponentImpl component = MinecraftComponentImpl.empty();
MinecraftComponent.Adapter<Object> adapter = component.unrelocatedAdapter(); MinecraftComponent.Adapter<Object> adapter = component.unrelocatedAdapter();
if (adapter == null) { if (adapter == null) {
@ -100,6 +100,14 @@ public final class ComponentUtil {
return component; return component;
} }
public static com.discordsrv.unrelocate.net.kyori.adventure.text.Component toUnrelocated(MinecraftComponent component) {
MinecraftComponent.Adapter<Object> adapter = component.unrelocatedAdapter();
if (adapter == null) {
throw new IllegalStateException("Could not get unrelocated adventure gson serializer");
}
return (com.discordsrv.unrelocate.net.kyori.adventure.text.Component) adapter.getComponent();
}
public static Component join(@NotNull Component delimiter, @NotNull Collection<? extends ComponentLike> components) { public static Component join(@NotNull Component delimiter, @NotNull Collection<? extends ComponentLike> components) {
return join(delimiter, components.toArray(new ComponentLike[0])); return join(delimiter, components.toArray(new ComponentLike[0]));
} }

View File

@ -154,9 +154,7 @@ public abstract class RequiredLinkingModule<T extends DiscordSRV> extends Abstra
public <RT> void stateChanged(Someone someone, RequirementType<RT> requirementType, RT value, boolean newState) { public <RT> void stateChanged(Someone someone, RequirementType<RT> requirementType, RT value, boolean newState) {
for (ParsedRequirements activeRequirement : getAllActiveRequirements()) { for (ParsedRequirements activeRequirement : getAllActiveRequirements()) {
for (Requirement<?> requirement : activeRequirement.usedRequirements()) { for (Requirement<?> requirement : activeRequirement.usedRequirements()) {
if (requirement.type() != requirementType if (requirement.type() != requirementType || !Objects.equals(requirement.value(), value)) {
|| !Objects.equals(requirement.value(), value)
|| newState == requirement.negated()) {
continue; continue;
} }
@ -258,7 +256,7 @@ public abstract class RequiredLinkingModule<T extends DiscordSRV> extends Abstra
} }
// None of the futures passed: additional requirements not met // None of the futures passed: additional requirements not met
return Component.text("You did not pass additionalRequirements"); return Component.text("You did not pass requirements");
}); });
}); });
} }

View File

@ -52,7 +52,7 @@ public class DiscordRoleRequirementType extends LongRequirementType {
} }
@Subscribe @Subscribe
public void onDiscordMemberRoleAdd(AbstractDiscordMemberRoleChangeEvent<?> event) { public void onDiscordMemberRoleChange(AbstractDiscordMemberRoleChangeEvent<?> event) {
boolean add = event instanceof DiscordMemberRoleAddEvent; boolean add = event instanceof DiscordMemberRoleAddEvent;
Someone someone = Someone.of(event.getMember().getUser().getId()); Someone someone = Someone.of(event.getMember().getUser().getId());

View File

@ -34,6 +34,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@PlaceholderPrefix("player_") @PlaceholderPrefix("player_")
public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSender { public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSender {
@ -66,6 +67,8 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
return identity().uuid(); return identity().uuid();
} }
CompletableFuture<Void> kick(Component component);
@NotNull @NotNull
@Placeholder("display_name") @Placeholder("display_name")
Component displayName(); Component displayName();

View File

@ -174,8 +174,9 @@ public abstract class SQLStorage implements Storage {
} }
// Get the uuid for the code // Get the uuid for the code
try (Statement statement = connection.createStatement()) { try (PreparedStatement statement = connection.prepareStatement("select PLAYERUUID from " + tablePrefix() + LINKING_CODES_TABLE_NAME + " where CODE = ? LIMIT 1;")) {
try (ResultSet resultSet = statement.executeQuery("select PLAYERUUID from " + tablePrefix() + LINKING_CODES_TABLE_NAME + " LIMIT 1;")) { statement.setString(1, code);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) { if (resultSet.next()) {
return UUID.fromString(resultSet.getString("PLAYERUUID")); return UUID.fromString(resultSet.getString("PLAYERUUID"));
} }