forked from public-mirrors/HuskSync
feat: add ProtocolLib support for deeper-level packet cancellation (#274)
* feat: add support for ProtocolLib packet-level state cancelling * refactor: move commands to event listener, document ProtocolLib support * docs: make Setup less claustrophobic * fix: remove `@Getter` on `PlayerPacketAdapter` * build: add missing license headers * fix: inaccessible method on Paper * test: add ProtocolLib to network spin test * fix: whoops I targeted the wrong packets * fix: bad command disabled check logic * fix: final protocollib adjustmentsfeat/data-edit-commands
parent
4dfbc0e32b
commit
2f5ddf6164
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) William278 <will27528@gmail.com>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.william278.husksync.listener;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.william278.husksync.BukkitHuskSync;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Projectile;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||||
|
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||||
|
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
|
||||||
|
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class BukkitLockedEventListener implements LockedHandler, Listener {
|
||||||
|
|
||||||
|
protected final BukkitHuskSync plugin;
|
||||||
|
|
||||||
|
protected BukkitLockedEventListener(@NotNull BukkitHuskSync plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) {
|
||||||
|
final Projectile projectile = event.getEntity();
|
||||||
|
if (projectile.getShooter() instanceof Player player) {
|
||||||
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onDropItem(@NotNull PlayerDropItemEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPickupItem(@NotNull EntityPickupItemEvent event) {
|
||||||
|
if (event.getEntity() instanceof Player player) {
|
||||||
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerInteractEntity(@NotNull PlayerInteractEntityEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerInteractArmorStand(@NotNull PlayerArmorStandManipulateEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onBlockBreak(@NotNull BlockBreakEvent event) {
|
||||||
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onInventoryOpen(@NotNull InventoryOpenEvent event) {
|
||||||
|
if (event.getPlayer() instanceof Player player) {
|
||||||
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onInventoryClick(@NotNull InventoryClickEvent event) {
|
||||||
|
cancelPlayerEvent(event.getWhoClicked().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
|
||||||
|
if (event.getEntity() instanceof Player player) {
|
||||||
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
||||||
|
if (cancelPlayerEvent(uuid)) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) William278 <will27528@gmail.com>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.william278.husksync.listener;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import net.william278.husksync.BukkitHuskSync;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static com.comphenix.protocol.PacketType.Play.Client;
|
||||||
|
|
||||||
|
public class BukkitLockedPacketListener extends BukkitLockedEventListener implements LockedHandler {
|
||||||
|
|
||||||
|
protected BukkitLockedPacketListener(@NotNull BukkitHuskSync plugin) {
|
||||||
|
super(plugin);
|
||||||
|
ProtocolLibrary.getProtocolManager().addPacketListener(new PlayerPacketAdapter(this));
|
||||||
|
plugin.log(Level.INFO, "Using ProtocolLib to cancel packets for locked players");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PlayerPacketAdapter extends PacketAdapter {
|
||||||
|
|
||||||
|
// Packets we want the player to still be able to send/receiver to/from the server
|
||||||
|
private static final Set<PacketType> ALLOWED_PACKETS = Set.of(
|
||||||
|
Client.KEEP_ALIVE, Client.PONG, // Connection packets
|
||||||
|
Client.CHAT_COMMAND, Client.CHAT, Client.CHAT_SESSION_UPDATE, // Chat / command packets
|
||||||
|
Client.POSITION, Client.POSITION_LOOK, Client.LOOK
|
||||||
|
);
|
||||||
|
|
||||||
|
private final BukkitLockedPacketListener listener;
|
||||||
|
|
||||||
|
public PlayerPacketAdapter(@NotNull BukkitLockedPacketListener listener) {
|
||||||
|
super(listener.getPlugin(), ListenerPriority.HIGHEST, getPacketsToListenFor());
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(@NotNull PacketEvent event) {
|
||||||
|
if (listener.cancelPlayerEvent(event.getPlayer().getUniqueId()) && !event.isReadOnly()) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
if (listener.cancelPlayerEvent(event.getPlayer().getUniqueId()) && !event.isReadOnly()) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the set of ALL Server packets, excluding the set of allowed packets
|
||||||
|
@NotNull
|
||||||
|
private static Set<PacketType> getPacketsToListenFor() {
|
||||||
|
return Sets.difference(Client.getInstance().values(), ALLOWED_PACKETS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) William278 <will27528@gmail.com>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.william278.husksync.listener;
|
||||||
|
|
||||||
|
import net.william278.husksync.HuskSync;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for doing stuff with locked users or when the plugin is disabled
|
||||||
|
*/
|
||||||
|
public interface LockedHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get if a command should be disabled while the user is locked
|
||||||
|
*/
|
||||||
|
default boolean isCommandDisabled(@NotNull String label) {
|
||||||
|
final List<String> blocked = getPlugin().getSettings().getSynchronization().getBlacklistedCommandsWhileLocked();
|
||||||
|
return blocked.contains("*") || blocked.contains(label.toLowerCase(Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether a player event should be canceled
|
||||||
|
*
|
||||||
|
* @param userUuid The UUID of the user to check
|
||||||
|
* @return Whether the event should be canceled
|
||||||
|
*/
|
||||||
|
default boolean cancelPlayerEvent(@NotNull UUID userUuid) {
|
||||||
|
return getPlugin().isDisabling() || getPlugin().isLocked(userUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@ApiStatus.Internal
|
||||||
|
HuskSync getPlugin();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue