mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-19 14:59:21 +00:00
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 adjustments
This commit is contained in:
@@ -70,6 +70,7 @@ allprojects {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
|
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
|
||||||
|
maven { url "https://repo.dmulloy2.net/repository/public/" }
|
||||||
maven { url 'https://repo.codemc.io/repository/maven-public/' }
|
maven { url 'https://repo.codemc.io/repository/maven-public/' }
|
||||||
maven { url 'https://repo.minebench.de/' }
|
maven { url 'https://repo.minebench.de/' }
|
||||||
maven { url 'https://repo.alessiodp.com/releases/' }
|
maven { url 'https://repo.alessiodp.com/releases/' }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ dependencies {
|
|||||||
implementation 'de.tr7zw:item-nbt-api:2.12.3'
|
implementation 'de.tr7zw:item-nbt-api:2.12.3'
|
||||||
|
|
||||||
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
|
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
|
||||||
|
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.32'
|
compileOnly 'org.projectlombok:lombok:1.18.32'
|
||||||
compileOnly 'commons-io:commons-io:2.16.0'
|
compileOnly 'commons-io:commons-io:2.16.0'
|
||||||
compileOnly 'org.json:json:20240303'
|
compileOnly 'org.json:json:20240303'
|
||||||
|
|||||||
@@ -24,41 +24,37 @@ import net.william278.husksync.HuskSync;
|
|||||||
import net.william278.husksync.data.BukkitData;
|
import net.william278.husksync.data.BukkitData;
|
||||||
import net.william278.husksync.user.BukkitUser;
|
import net.william278.husksync.user.BukkitUser;
|
||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Projectile;
|
|
||||||
import org.bukkit.event.Cancellable;
|
|
||||||
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.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.PlayerDeathEvent;
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
|
||||||
import org.bukkit.event.inventory.InventoryOpenEvent;
|
|
||||||
import org.bukkit.event.inventory.PrepareItemCraftEvent;
|
|
||||||
import org.bukkit.event.player.*;
|
|
||||||
import org.bukkit.event.server.MapInitializeEvent;
|
import org.bukkit.event.server.MapInitializeEvent;
|
||||||
import org.bukkit.event.world.WorldSaveEvent;
|
import org.bukkit.event.world.WorldSaveEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
|
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
|
||||||
BukkitDeathEventListener, Listener {
|
BukkitDeathEventListener, Listener {
|
||||||
protected final List<String> blacklistedCommands;
|
|
||||||
|
|
||||||
public BukkitEventListener(@NotNull BukkitHuskSync huskSync) {
|
protected final LockedHandler lockedHandler;
|
||||||
super(huskSync);
|
|
||||||
this.blacklistedCommands = huskSync.getSettings().getSynchronization().getBlacklistedCommandsWhileLocked();
|
public BukkitEventListener(@NotNull BukkitHuskSync plugin) {
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(this, huskSync);
|
super(plugin);
|
||||||
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
|
this.lockedHandler = createLockedHandler(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private LockedHandler createLockedHandler(@NotNull BukkitHuskSync plugin) {
|
||||||
|
if (getPlugin().isDependencyLoaded("ProtocolLib") && getPlugin().getSettings().isCancelPackets()) {
|
||||||
|
return new BukkitLockedPacketListener(plugin);
|
||||||
|
} else {
|
||||||
|
return new BukkitLockedEventListener(plugin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,7 +84,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
|||||||
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
||||||
|
|
||||||
// If the player is locked or the plugin disabling, clear their drops
|
// If the player is locked or the plugin disabling, clear their drops
|
||||||
if (cancelPlayerEvent(user.getUuid())) {
|
if (lockedHandler.cancelPlayerEvent(user.getUuid())) {
|
||||||
event.getDrops().clear();
|
event.getDrops().clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -125,91 +121,13 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We handle commands here to allow specific command handling on ProtocolLib servers
|
||||||
/*
|
|
||||||
* Events to cancel if the player has not been set yet
|
|
||||||
*/
|
|
||||||
|
|
||||||
@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 onCraftItem(@NotNull PrepareItemCraftEvent event) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
|
||||||
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
|
|
||||||
if (event.getEntity() instanceof Player player) {
|
|
||||||
cancelPlayerEvent(player.getUniqueId(), event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onPermissionCommand(@NotNull PlayerCommandPreprocessEvent event) {
|
public void onCommandProcessed(@NotNull PlayerCommandPreprocessEvent event) {
|
||||||
final String[] commandArgs = event.getMessage().substring(1).split(" ");
|
if (!lockedHandler.isCommandDisabled(event.getMessage().substring(1).split(" ")[0])) {
|
||||||
final String commandLabel = commandArgs[0].toLowerCase(Locale.ENGLISH);
|
return;
|
||||||
|
|
||||||
if (blacklistedCommands.contains("*") || blacklistedCommands.contains(commandLabel)) {
|
|
||||||
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
|
||||||
}
|
}
|
||||||
}
|
if (lockedHandler.cancelPlayerEvent(event.getPlayer().getUniqueId())) {
|
||||||
|
|
||||||
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
|
||||||
if (cancelPlayerEvent(uuid)) {
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ author: 'William278'
|
|||||||
description: '${description}'
|
description: '${description}'
|
||||||
website: 'https://william278.net'
|
website: 'https://william278.net'
|
||||||
softdepend:
|
softdepend:
|
||||||
|
- 'ProtocolLib'
|
||||||
- 'MysqlPlayerDataBridge'
|
- 'MysqlPlayerDataBridge'
|
||||||
- 'Plan'
|
- 'Plan'
|
||||||
libraries:
|
libraries:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class Locales {
|
|||||||
Map<String, String> locales = Maps.newTreeMap();
|
Map<String, String> locales = Maps.newTreeMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a raw, un-formatted locale loaded from the locales file
|
* Returns a raw, unformatted locale loaded from the locale file
|
||||||
*
|
*
|
||||||
* @param localeId String identifier of the locale, corresponding to a key in the file
|
* @param localeId String identifier of the locale, corresponding to a key in the file
|
||||||
* @return An {@link Optional} containing the locale corresponding to the id, if it exists
|
* @return An {@link Optional} containing the locale corresponding to the id, if it exists
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ public class Settings {
|
|||||||
@Comment({"Whether to enable the Player Analytics hook.", "Docs: https://william278.net/docs/husksync/plan-hook"})
|
@Comment({"Whether to enable the Player Analytics hook.", "Docs: https://william278.net/docs/husksync/plan-hook"})
|
||||||
private boolean enablePlanHook = true;
|
private boolean enablePlanHook = true;
|
||||||
|
|
||||||
|
@Comment("Whether to cancel game event packets directly when handling locked players if ProtocolLib is installed")
|
||||||
|
private boolean cancelPackets = true;
|
||||||
|
|
||||||
|
|
||||||
// Database settings
|
// Database settings
|
||||||
@Comment("Database settings")
|
@Comment("Database settings")
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
||||||
|
|
||||||
@@ -104,15 +103,6 @@ public abstract class EventListener {
|
|||||||
plugin.getDataSyncer().saveData(user, snapshot);
|
plugin.getDataSyncer().saveData(user, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether a player event should be canceled
|
|
||||||
*
|
|
||||||
* @param userUuid The UUID of the user to check
|
|
||||||
* @return Whether the event should be canceled
|
|
||||||
*/
|
|
||||||
protected final boolean cancelPlayerEvent(@NotNull UUID userUuid) {
|
|
||||||
return plugin.isDisabling() || plugin.isLocked(userUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the plugin disabling
|
* Handle the plugin disabling
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,23 +11,28 @@ This will walk you through installing HuskSync on your network of Spigot servers
|
|||||||
### 1. Install the jar
|
### 1. Install the jar
|
||||||
- Place the plugin jar file in the `/plugins/` directory of each Spigot server.
|
- Place the plugin jar file in the `/plugins/` directory of each Spigot server.
|
||||||
- You do not need to install HuskSync as a proxy plugin.
|
- You do not need to install HuskSync as a proxy plugin.
|
||||||
|
- You can additionally install [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) for better locked user handling, and [Plan](https://www.spigotmc.org/resources/plan-player-analytics.32536/) for analytics.
|
||||||
|
|
||||||
### 2. Restart servers
|
### 2. Restart servers
|
||||||
- Start, then stop every server to let HuskSync generate the [[config file]].
|
- Start, then stop every server to let HuskSync generate the [[config file]].
|
||||||
- HuskSync will throw an error in the console and disable itself as it is unable to connect to the database. You haven't set the credentials yet, so this is expected.
|
- HuskSync will throw an error in the console and disable itself as it is unable to connect to the database. You haven't set the credentials yet, so this is expected.
|
||||||
- Advanced users: If you'd prefer, you can just create one config.yml file and create symbolic links in each `/plugins/HuskSync/` folder to it to make updating it easier.
|
- Advanced users: If you'd prefer, you can create one config.yml file and create symbolic links in each `/plugins/HuskSync/` folder to it to make updating it easier.
|
||||||
|
|
||||||
### 3. Enter Mysql & Redis database credentials
|
### 3. Enter Mysql & Redis database credentials
|
||||||
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
||||||
- Under `credentials` in the `database` section, enter the credentials of your (MySQL/MariaDB/MongoDB/PostgreSQL) Database. You shouldn't touch the `connection_pool` properties.
|
- Under `credentials` in the `database` section, enter the credentials of your (MySQL/MariaDB/MongoDB/PostgreSQL) Database. You shouldn't touch the `connection_pool` properties.
|
||||||
- Under `credentials` in the `redis` section, enter the credentials of your Redis Database. If your Redis server doesn't have a password, leave the password blank as it is.
|
- Under `credentials` in the `redis` section, enter the credentials of your Redis Database. If your Redis server doesn't have a password, leave the password blank as it is.
|
||||||
- Unless you want to have multiple clusters of servers within your network, each with separate user data, you should not change the value of `cluster_id`.
|
- Unless you want to have multiple clusters of servers within your network, each with separate user data, you should not change the value of `cluster_id`.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>For MongoDB Users</b></summary>
|
<summary>Important — MongoDB Users</summary>
|
||||||
|
|
||||||
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
||||||
- Set `type` in the `database` section to `MONGO`
|
- Set `type` in the `database` section to `MONGO`
|
||||||
- Under `credentials` in the `database` section, enter the credentials of your MongoDB Database. You shouldn't touch the `connection_pool` properties.
|
- Under `credentials` in the `database` section, enter the credentials of your MongoDB Database. You shouldn't touch the `connection_pool` properties.
|
||||||
<details>
|
<details>
|
||||||
<summary><b>MongoDB Atlas</b></summary>
|
|
||||||
|
<summary>Additional configuration for MongoDB Atlas users</summary>
|
||||||
|
|
||||||
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
||||||
- Set `using_atlas` in the `mongo_settings` section to `true`.
|
- Set `using_atlas` in the `mongo_settings` section to `true`.
|
||||||
@@ -41,6 +46,7 @@ This will walk you through installing HuskSync on your network of Spigot servers
|
|||||||
### 4. Set server names in server.yml files
|
### 4. Set server names in server.yml files
|
||||||
- Navigate to the HuskSync server name file on each server (`~/plugins/HuskSync/server.yml`)
|
- Navigate to the HuskSync server name file on each server (`~/plugins/HuskSync/server.yml`)
|
||||||
- Set the `name:` of the server in this file to the ID of this server as defined in the config of your proxy (e.g., if this is the "hub" server you access with `/server hub`, put `'hub'` here)
|
- Set the `name:` of the server in this file to the ID of this server as defined in the config of your proxy (e.g., if this is the "hub" server you access with `/server hub`, put `'hub'` here)
|
||||||
|
|
||||||
### 5. Start every server again
|
### 5. Start every server again
|
||||||
- Provided your MySQL and Redis credentials were correct, synchronization should begin as soon as you start your servers again.
|
- Provided your MySQL and Redis credentials were correct, synchronization should begin as soon as you start your servers again.
|
||||||
- If you need to import data from HuskSync v1.x or MySQLPlayerDataBridge, please see the guides below:
|
- If you need to import data from HuskSync v1.x or MySQLPlayerDataBridge, please see the guides below:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class PaperEventListener extends BukkitEventListener {
|
|||||||
public void handlePlayerDeath(@NotNull PlayerDeathEvent event) {
|
public void handlePlayerDeath(@NotNull PlayerDeathEvent event) {
|
||||||
// If the player is locked or the plugin disabling, clear their drops
|
// If the player is locked or the plugin disabling, clear their drops
|
||||||
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
||||||
if (cancelPlayerEvent(user.getUuid())) {
|
if (lockedHandler.cancelPlayerEvent(user.getUuid())) {
|
||||||
event.getDrops().clear();
|
event.getDrops().clear();
|
||||||
event.getItemsToKeep().clear();
|
event.getItemsToKeep().clear();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ version: '${version}'
|
|||||||
api-version: '1.19'
|
api-version: '1.19'
|
||||||
dependencies:
|
dependencies:
|
||||||
server:
|
server:
|
||||||
|
ProtocolLib:
|
||||||
|
required: false
|
||||||
|
load: BEFORE
|
||||||
|
join-classpath: true
|
||||||
MysqlPlayerDataBridge:
|
MysqlPlayerDataBridge:
|
||||||
required: false
|
required: false
|
||||||
load: BEFORE
|
load: BEFORE
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Parameters:
|
|||||||
backend_ports = [25567, 25568]
|
backend_ports = [25567, 25568]
|
||||||
backend_type = 'paper'
|
backend_type = 'paper'
|
||||||
backend_ram = 2048
|
backend_ram = 2048
|
||||||
backend_plugins = ['../target/HuskSync-Paper-*.jar']
|
backend_plugins = ['../target/HuskSync-Paper-*.jar', './ProtocolLib/ProtocolLib.jar']
|
||||||
backend_plugin_folders = ['./HuskSync']
|
backend_plugin_folders = ['./HuskSync']
|
||||||
operator_names = ['William278']
|
operator_names = ['William278']
|
||||||
operator_uuids = ['5dfb0558-e306-44f4-bb9a-f9218d4eb787']
|
operator_uuids = ['5dfb0558-e306-44f4-bb9a-f9218d4eb787']
|
||||||
|
|||||||
Reference in New Issue
Block a user