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()
|
||||
mavenCentral()
|
||||
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.minebench.de/' }
|
||||
maven { url 'https://repo.alessiodp.com/releases/' }
|
||||
|
||||
@@ -13,6 +13,7 @@ dependencies {
|
||||
implementation 'de.tr7zw:item-nbt-api:2.12.3'
|
||||
|
||||
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 'commons-io:commons-io:2.16.0'
|
||||
compileOnly 'org.json:json:20240303'
|
||||
|
||||
@@ -24,41 +24,37 @@ import net.william278.husksync.HuskSync;
|
||||
import net.william278.husksync.data.BukkitData;
|
||||
import net.william278.husksync.user.BukkitUser;
|
||||
import net.william278.husksync.user.OnlineUser;
|
||||
import org.bukkit.Bukkit;
|
||||
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.PlayerDeathEvent;
|
||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||
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.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.server.MapInitializeEvent;
|
||||
import org.bukkit.event.world.WorldSaveEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
|
||||
BukkitDeathEventListener, Listener {
|
||||
protected final List<String> blacklistedCommands;
|
||||
|
||||
public BukkitEventListener(@NotNull BukkitHuskSync huskSync) {
|
||||
super(huskSync);
|
||||
this.blacklistedCommands = huskSync.getSettings().getSynchronization().getBlacklistedCommandsWhileLocked();
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, huskSync);
|
||||
protected final LockedHandler lockedHandler;
|
||||
|
||||
public BukkitEventListener(@NotNull BukkitHuskSync plugin) {
|
||||
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
|
||||
@@ -88,7 +84,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
||||
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
||||
|
||||
// If the player is locked or the plugin disabling, clear their drops
|
||||
if (cancelPlayerEvent(user.getUuid())) {
|
||||
if (lockedHandler.cancelPlayerEvent(user.getUuid())) {
|
||||
event.getDrops().clear();
|
||||
return;
|
||||
}
|
||||
@@ -125,91 +121,13 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
// We handle commands here to allow specific command handling on ProtocolLib servers
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPermissionCommand(@NotNull PlayerCommandPreprocessEvent event) {
|
||||
final String[] commandArgs = event.getMessage().substring(1).split(" ");
|
||||
final String commandLabel = commandArgs[0].toLowerCase(Locale.ENGLISH);
|
||||
|
||||
if (blacklistedCommands.contains("*") || blacklistedCommands.contains(commandLabel)) {
|
||||
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||
public void onCommandProcessed(@NotNull PlayerCommandPreprocessEvent event) {
|
||||
if (!lockedHandler.isCommandDisabled(event.getMessage().substring(1).split(" ")[0])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
||||
if (cancelPlayerEvent(uuid)) {
|
||||
if (lockedHandler.cancelPlayerEvent(event.getPlayer().getUniqueId())) {
|
||||
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}'
|
||||
website: 'https://william278.net'
|
||||
softdepend:
|
||||
- 'ProtocolLib'
|
||||
- 'MysqlPlayerDataBridge'
|
||||
- 'Plan'
|
||||
libraries:
|
||||
|
||||
@@ -57,7 +57,7 @@ public class Locales {
|
||||
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
|
||||
* @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"})
|
||||
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
|
||||
@Comment("Database settings")
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
||||
|
||||
@@ -104,15 +103,6 @@ public abstract class EventListener {
|
||||
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
|
||||
|
||||
@@ -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
|
||||
- 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 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
|
||||
- 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.
|
||||
- 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
|
||||
- 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 `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`.
|
||||
|
||||
<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`)
|
||||
- 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.
|
||||
<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`)
|
||||
- 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
|
||||
- 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)
|
||||
|
||||
### 5. Start every server 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:
|
||||
|
||||
@@ -44,7 +44,7 @@ public class PaperEventListener extends BukkitEventListener {
|
||||
public void handlePlayerDeath(@NotNull PlayerDeathEvent event) {
|
||||
// If the player is locked or the plugin disabling, clear their drops
|
||||
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
|
||||
if (cancelPlayerEvent(user.getUuid())) {
|
||||
if (lockedHandler.cancelPlayerEvent(user.getUuid())) {
|
||||
event.getDrops().clear();
|
||||
event.getItemsToKeep().clear();
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,10 @@ version: '${version}'
|
||||
api-version: '1.19'
|
||||
dependencies:
|
||||
server:
|
||||
ProtocolLib:
|
||||
required: false
|
||||
load: BEFORE
|
||||
join-classpath: true
|
||||
MysqlPlayerDataBridge:
|
||||
required: false
|
||||
load: BEFORE
|
||||
|
||||
@@ -20,7 +20,7 @@ class Parameters:
|
||||
backend_ports = [25567, 25568]
|
||||
backend_type = 'paper'
|
||||
backend_ram = 2048
|
||||
backend_plugins = ['../target/HuskSync-Paper-*.jar']
|
||||
backend_plugins = ['../target/HuskSync-Paper-*.jar', './ProtocolLib/ProtocolLib.jar']
|
||||
backend_plugin_folders = ['./HuskSync']
|
||||
operator_names = ['William278']
|
||||
operator_uuids = ['5dfb0558-e306-44f4-bb9a-f9218d4eb787']
|
||||
|
||||
Reference in New Issue
Block a user