From 2f5ddf61648a99b979eda1bc6752d9211b0bb75e Mon Sep 17 00:00:00 2001 From: William Date: Wed, 10 Apr 2024 17:09:53 +0100 Subject: [PATCH] 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 --- build.gradle | 1 + bukkit/build.gradle | 1 + .../listener/BukkitEventListener.java | 126 +++--------------- .../listener/BukkitLockedEventListener.java | 125 +++++++++++++++++ .../listener/BukkitLockedPacketListener.java | 82 ++++++++++++ bukkit/src/main/resources/plugin.yml | 1 + .../william278/husksync/config/Locales.java | 2 +- .../william278/husksync/config/Settings.java | 3 + .../husksync/listener/EventListener.java | 10 -- .../husksync/listener/LockedHandler.java | 57 ++++++++ docs/Setup.md | 12 +- .../husksync/listener/PaperEventListener.java | 2 +- paper/src/main/resources/paper-plugin.yml | 4 + test/spin_network.py | 2 +- 14 files changed, 308 insertions(+), 120 deletions(-) create mode 100644 bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java create mode 100644 bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedPacketListener.java create mode 100644 common/src/main/java/net/william278/husksync/listener/LockedHandler.java diff --git a/build.gradle b/build.gradle index 78448826..fe8ae307 100644 --- a/build.gradle +++ b/build.gradle @@ -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/' } diff --git a/bukkit/build.gradle b/bukkit/build.gradle index c7e42273..80730f8f 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -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' diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java index 4766f577..21c282bf 100644 --- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java @@ -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 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); } } diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java new file mode 100644 index 00000000..948175df --- /dev/null +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedEventListener.java @@ -0,0 +1,125 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * 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); + } + } + +} diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedPacketListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedPacketListener.java new file mode 100644 index 00000000..26ba0317 --- /dev/null +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitLockedPacketListener.java @@ -0,0 +1,82 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * 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 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 getPacketsToListenFor() { + return Sets.difference(Client.getInstance().values(), ALLOWED_PACKETS); + } + + } + +} diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 7a0de42d..3c119330 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -6,6 +6,7 @@ author: 'William278' description: '${description}' website: 'https://william278.net' softdepend: + - 'ProtocolLib' - 'MysqlPlayerDataBridge' - 'Plan' libraries: diff --git a/common/src/main/java/net/william278/husksync/config/Locales.java b/common/src/main/java/net/william278/husksync/config/Locales.java index f5fc94aa..9babe821 100644 --- a/common/src/main/java/net/william278/husksync/config/Locales.java +++ b/common/src/main/java/net/william278/husksync/config/Locales.java @@ -57,7 +57,7 @@ public class Locales { Map 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 diff --git a/common/src/main/java/net/william278/husksync/config/Settings.java b/common/src/main/java/net/william278/husksync/config/Settings.java index 50f549d8..b252f329 100644 --- a/common/src/main/java/net/william278/husksync/config/Settings.java +++ b/common/src/main/java/net/william278/husksync/config/Settings.java @@ -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") diff --git a/common/src/main/java/net/william278/husksync/listener/EventListener.java b/common/src/main/java/net/william278/husksync/listener/EventListener.java index 5103f238..af352227 100644 --- a/common/src/main/java/net/william278/husksync/listener/EventListener.java +++ b/common/src/main/java/net/william278/husksync/listener/EventListener.java @@ -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 diff --git a/common/src/main/java/net/william278/husksync/listener/LockedHandler.java b/common/src/main/java/net/william278/husksync/listener/LockedHandler.java new file mode 100644 index 00000000..088be49a --- /dev/null +++ b/common/src/main/java/net/william278/husksync/listener/LockedHandler.java @@ -0,0 +1,57 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * 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 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(); + +} diff --git a/docs/Setup.md b/docs/Setup.md index 4983b789..319f5321 100644 --- a/docs/Setup.md +++ b/docs/Setup.md @@ -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`. +
-For MongoDB Users +Important — MongoDB Users - 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.
-MongoDB Atlas + +Additional configuration for MongoDB Atlas users - 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: diff --git a/paper/src/main/java/net/william278/husksync/listener/PaperEventListener.java b/paper/src/main/java/net/william278/husksync/listener/PaperEventListener.java index 6b469d37..d4fc8acf 100644 --- a/paper/src/main/java/net/william278/husksync/listener/PaperEventListener.java +++ b/paper/src/main/java/net/william278/husksync/listener/PaperEventListener.java @@ -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; diff --git a/paper/src/main/resources/paper-plugin.yml b/paper/src/main/resources/paper-plugin.yml index 50383884..37226d66 100644 --- a/paper/src/main/resources/paper-plugin.yml +++ b/paper/src/main/resources/paper-plugin.yml @@ -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 diff --git a/test/spin_network.py b/test/spin_network.py index ea252049..4ffffa93 100644 --- a/test/spin_network.py +++ b/test/spin_network.py @@ -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']