From 1acbd8957d3b9708b34400cec6780b6c57e6aba7 Mon Sep 17 00:00:00 2001 From: 2008Choco Date: Sun, 11 Feb 2024 18:33:28 -0500 Subject: [PATCH] Separate the serverbound listener from VeinMinerPlayer --- .../choco/veinminer/ActivationStrategy.java | 2 + .../wtf/choco/veinminer/VeinMinerPlugin.java | 2 +- .../veinminer/command/CommandVeinMiner.java | 2 +- .../veinminer/data/PersistentDataStorage.java | 2 +- .../data/PersistentDataStorageJSON.java | 2 +- .../data/PersistentDataStorageNoOp.java | 2 +- .../data/PersistentDataStorageSQL.java | 2 +- .../PlaceholderExpansionVeinMiner.java | 2 +- .../listener/BreakBlockListener.java | 2 +- .../listener/PlayerDataListener.java | 2 +- .../VeinMinerBukkitChannelRegistrar.java | 4 +- .../player/PlayerNetworkListener.java | 246 ++++++++++++++++++ .../{ => player}/VeinMinerPlayer.java | 209 ++------------- .../VeinMinerPlayerManager.java | 3 +- 14 files changed, 287 insertions(+), 195 deletions(-) create mode 100644 veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/PlayerNetworkListener.java rename veinminer-bukkit/src/main/java/wtf/choco/veinminer/{ => player}/VeinMinerPlayer.java (62%) rename veinminer-bukkit/src/main/java/wtf/choco/veinminer/{manager => player}/VeinMinerPlayerManager.java (97%) diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/ActivationStrategy.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/ActivationStrategy.java index 6713104d..7b77097a 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/ActivationStrategy.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/ActivationStrategy.java @@ -6,6 +6,8 @@ import org.jetbrains.annotations.NotNull; +import wtf.choco.veinminer.player.VeinMinerPlayer; + /** * Represents a different method of activating vein miner. */ diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlugin.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlugin.java index a220afdb..871359b8 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlugin.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlugin.java @@ -57,7 +57,6 @@ import wtf.choco.veinminer.listener.McMMOIntegrationListener; import wtf.choco.veinminer.listener.PlayerDataListener; import wtf.choco.veinminer.manager.VeinMinerManager; -import wtf.choco.veinminer.manager.VeinMinerPlayerManager; import wtf.choco.veinminer.metrics.AntiCheat; import wtf.choco.veinminer.metrics.StatTracker; import wtf.choco.veinminer.network.VeinMinerBukkitChannelRegistrar; @@ -65,6 +64,7 @@ import wtf.choco.veinminer.pattern.VeinMiningPatternDefault; import wtf.choco.veinminer.pattern.VeinMiningPatternStaircase; import wtf.choco.veinminer.pattern.VeinMiningPatternStaircase.Direction; +import wtf.choco.veinminer.player.VeinMinerPlayerManager; import wtf.choco.veinminer.pattern.VeinMiningPatternTunnel; import wtf.choco.veinminer.tool.ToolCategoryRegistry; import wtf.choco.veinminer.update.SpigotMCUpdateChecker; diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/command/CommandVeinMiner.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/command/CommandVeinMiner.java index 05fc684e..d0f25c02 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/command/CommandVeinMiner.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/command/CommandVeinMiner.java @@ -28,12 +28,12 @@ import wtf.choco.network.data.NamespacedKey; import wtf.choco.veinminer.ActivationStrategy; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.api.event.player.PlayerVeinMiningPatternChangeEvent; import wtf.choco.veinminer.data.LegacyImportTask; import wtf.choco.veinminer.data.PersistentDataStorageSQL; import wtf.choco.veinminer.pattern.VeinMiningPattern; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.tool.VeinMinerToolCategory; import wtf.choco.veinminer.update.UpdateResult; import wtf.choco.veinminer.util.VMConstants; diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorage.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorage.java index 9cb31a61..51a74639 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorage.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorage.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; -import wtf.choco.veinminer.VeinMinerPlayer; +import wtf.choco.veinminer.player.VeinMinerPlayer; /** * A means of storing persistent {@link VeinMinerPlayer} data. diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageJSON.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageJSON.java index 5ae62d33..339f5100 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageJSON.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageJSON.java @@ -20,8 +20,8 @@ import org.jetbrains.annotations.NotNull; import wtf.choco.veinminer.ActivationStrategy; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.tool.VeinMinerToolCategory; /** diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageNoOp.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageNoOp.java index 51c71e25..b7eca208 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageNoOp.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageNoOp.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; -import wtf.choco.veinminer.VeinMinerPlayer; +import wtf.choco.veinminer.player.VeinMinerPlayer; /** * An implementation of {@link PersistentDataStorage} that performs no save or load diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageSQL.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageSQL.java index 5d7c0a4f..0cd64aee 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageSQL.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/data/PersistentDataStorageSQL.java @@ -17,9 +17,9 @@ import org.jetbrains.annotations.NotNull; import wtf.choco.veinminer.ActivationStrategy; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.pattern.VeinMiningPattern; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.tool.VeinMinerToolCategory; /** diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/integration/PlaceholderExpansionVeinMiner.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/integration/PlaceholderExpansionVeinMiner.java index 2a6f3715..d4dc7ff7 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/integration/PlaceholderExpansionVeinMiner.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/integration/PlaceholderExpansionVeinMiner.java @@ -8,8 +8,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.tool.VeinMinerToolCategory; /** diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/BreakBlockListener.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/BreakBlockListener.java index 8d36f301..9ee07f49 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/BreakBlockListener.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/BreakBlockListener.java @@ -22,7 +22,6 @@ import org.bukkit.util.RayTraceResult; import org.jetbrains.annotations.NotNull; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.anticheat.AntiCheatHook; import wtf.choco.veinminer.api.event.player.PlayerVeinMineEvent; @@ -33,6 +32,7 @@ import wtf.choco.veinminer.manager.VeinMinerManager; import wtf.choco.veinminer.metrics.StatTracker; import wtf.choco.veinminer.pattern.VeinMiningPattern; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.tool.VeinMinerToolCategory; import wtf.choco.veinminer.tool.VeinMinerToolCategoryHand; import wtf.choco.veinminer.util.VMConstants; diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/PlayerDataListener.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/PlayerDataListener.java index 94ce3a0a..7bb3655d 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/PlayerDataListener.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/listener/PlayerDataListener.java @@ -9,9 +9,9 @@ import org.bukkit.metadata.LazyMetadataValue.CacheStrategy; import org.jetbrains.annotations.NotNull; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSetPattern; +import wtf.choco.veinminer.player.VeinMinerPlayer; import wtf.choco.veinminer.util.VMConstants; public final class PlayerDataListener implements Listener { diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/network/VeinMinerBukkitChannelRegistrar.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/network/VeinMinerBukkitChannelRegistrar.java index 7be32084..891462b2 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/network/VeinMinerBukkitChannelRegistrar.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/network/VeinMinerBukkitChannelRegistrar.java @@ -10,6 +10,7 @@ import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.network.protocol.VeinMinerClientboundMessageListener; import wtf.choco.veinminer.network.protocol.VeinMinerServerboundMessageListener; +import wtf.choco.veinminer.player.VeinMinerPlayer; /** * A {@link ChannelRegistrar} implementation for VeinMiner on Bukkit servers. @@ -27,7 +28,8 @@ public VeinMinerBukkitChannelRegistrar(@NotNull VeinMinerPlugin plugin) { @Override protected VeinMinerServerboundMessageListener onSuccessfulMessage(@NotNull Player player, @NotNull String channel, @NotNull Message message) { - return plugin.getPlayerManager().get(player); + VeinMinerPlayer veinMinerPlayer = plugin.getPlayerManager().get(player); + return (veinMinerPlayer != null) ? veinMinerPlayer.getServerboundMessageListener() : null; } } diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/PlayerNetworkListener.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/PlayerNetworkListener.java new file mode 100644 index 00000000..e2b73781 --- /dev/null +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/PlayerNetworkListener.java @@ -0,0 +1,246 @@ +package wtf.choco.veinminer.player; + +import com.google.common.base.Preconditions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.RayTraceResult; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +import wtf.choco.network.data.NamespacedKey; +import wtf.choco.veinminer.ActivationStrategy; +import wtf.choco.veinminer.VeinMiner; +import wtf.choco.veinminer.VeinMinerPlugin; +import wtf.choco.veinminer.api.event.player.PlayerVeinMiningPatternChangeEvent; +import wtf.choco.veinminer.block.BlockList; +import wtf.choco.veinminer.block.VeinMinerBlock; +import wtf.choco.veinminer.manager.VeinMinerManager; +import wtf.choco.veinminer.network.protocol.VeinMinerServerboundMessageListener; +import wtf.choco.veinminer.network.protocol.clientbound.ClientboundHandshakeResponse; +import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSetConfig; +import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSyncRegisteredPatterns; +import wtf.choco.veinminer.network.protocol.clientbound.ClientboundVeinMineResults; +import wtf.choco.veinminer.network.protocol.serverbound.ServerboundHandshake; +import wtf.choco.veinminer.network.protocol.serverbound.ServerboundRequestVeinMine; +import wtf.choco.veinminer.network.protocol.serverbound.ServerboundSelectPattern; +import wtf.choco.veinminer.network.protocol.serverbound.ServerboundToggleVeinMiner; +import wtf.choco.veinminer.pattern.PatternRegistry; +import wtf.choco.veinminer.pattern.VeinMiningPattern; +import wtf.choco.veinminer.tool.VeinMinerToolCategory; +import wtf.choco.veinminer.util.BlockPosition; +import wtf.choco.veinminer.util.VMEventFactory; + +/** + * A {@link VeinMinerServerboundMessageListener} for a {@link VeinMinerPlayer}. + *

+ * NOTE: Not part of the public API. This class is intended for internal use only. + * Most methods in this interface can be found mirrored in {@link VeinMinerPlayer} instead. Anything + * not in VeinMinerPlayer is not meant to be called. + */ +@Internal +public final class PlayerNetworkListener implements VeinMinerServerboundMessageListener { + + private boolean clientReady = false; + private boolean usingClientMod = false; + private boolean clientKeyPressed = false; + private Queue onClientReadyTasks = new ConcurrentLinkedQueue<>(); + + private final VeinMinerPlayer player; + + PlayerNetworkListener(@NotNull VeinMinerPlayer player) { + Preconditions.checkArgument(player != null, "player must not be null"); + this.player = player; + } + + /** + * Check whether or not the client is ready to receive messages. + *

+ * This method will only be true if {@link #isUsingClientMod()} is {@code true}, the client has + * successfully shaken hands with the server, is capable of being sent a client message, and + * has been synchronized with the server as per the protocol specification. + * + * @return true if the client is ready, false otherwise + */ + public boolean isClientReady() { + return clientReady; + } + + /** + * Check whether or not this player is using the client mod. + * + * @return true if using client mod, false otherwise + */ + public boolean isUsingClientMod() { + return usingClientMod; + } + + /** + * Check whether or not vein miner is active as a result of this user's client mod. This method will + * always return {@code false} if the player {@link #isUsingClientMod() is not using the client mod}. + * + * @return true if active, false otherwise + */ + public boolean isClientKeyPressed() { + return clientKeyPressed; + } + + /** + * Add a {@link Runnable} task to execute when this client is ready. + * + * @param task the task + */ + public void addOnClientReadyTask(@NotNull Runnable task) { + Preconditions.checkArgument(task != null, "task must not be null"); + this.onClientReadyTasks.add(task); + } + + @Override + public void handleHandshake(@NotNull ServerboundHandshake message) { + int serverProtocolVersion = VeinMiner.PROTOCOL.getVersion(); + if (serverProtocolVersion != message.getProtocolVersion()) { + this.player.getPlayer().kickPlayer("Your client-side version of VeinMiner (for Bukkit) is " + (serverProtocolVersion > message.getProtocolVersion() ? "out of date. Please update." : "too new. Please downgrade.")); + return; + } + + this.usingClientMod = true; + this.player.setActivationStrategy(ActivationStrategy.CLIENT); + this.player.setDirty(false); // We can force dirty = false. Data hasn't loaded yet, but we still want to set the strategy to client automatically + + /* + * Let the client know whether or not the client is even allowed. + * We send this one tick later so we know that the player's connection has been initialized + */ + VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + this.player.sendMessage(new ClientboundHandshakeResponse()); + + // Synchronize all registered patterns to the client + PatternRegistry patternRegistry = plugin.getPatternRegistry(); + + List patternKeys = new ArrayList<>(); + patternRegistry.getPatterns().forEach(pattern -> patternKeys.add(pattern.getKey())); + VeinMiningPattern defaultPattern = plugin.getConfiguration().getDefaultVeinMiningPattern(); + + // Move the default pattern to the start if it wasn't already there + if (patternKeys.size() > 1 && patternKeys.remove(defaultPattern.getKey())) { + patternKeys.add(0, defaultPattern.getKey()); + } + + // Don't send any patterns to which the player does not have access + patternKeys.removeIf(patternKey -> { + VeinMiningPattern pattern = patternRegistry.get(patternKey); + if (pattern == null) { + return true; + } + + String permission = pattern.getPermission(); + return permission != null && !player.getPlayer().hasPermission(permission); + }); + + this.player.sendMessage(new ClientboundSyncRegisteredPatterns(patternKeys)); + this.player.sendMessage(new ClientboundSetConfig(player.getClientConfig())); + + // The client is ready, accept post-client init tasks now + this.clientReady = true; + + Runnable runnable; + while ((runnable = onClientReadyTasks.poll()) != null) { + runnable.run(); + } + }, 1); + } + + @Override + public void handleToggleVeinMiner(@NotNull ServerboundToggleVeinMiner message) { + if (!player.getClientConfig().isAllowActivationKeybind()) { + return; + } + + if (!VMEventFactory.callPlayerClientActivateVeinMinerEvent(player.getPlayer(), message.isActivated())) { + return; + } + + this.clientKeyPressed = message.isActivated(); + } + + @Override + public void handleRequestVeinMine(@NotNull ServerboundRequestVeinMine message) { + ItemStack itemStack = player.getPlayer().getInventory().getItemInMainHand(); + VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); + VeinMinerToolCategory category = plugin.getToolCategoryRegistry().get(itemStack); + + if (category == null) { + this.player.sendMessage(new ClientboundVeinMineResults()); + return; + } + + RayTraceResult rayTraceResult = player.getPlayer().rayTraceBlocks(6); + if (rayTraceResult == null) { + this.player.sendMessage(new ClientboundVeinMineResults()); + return; + } + + Block targetBlock = rayTraceResult.getHitBlock(); + BlockFace targetBlockFace = rayTraceResult.getHitBlockFace(); + + if (targetBlock == null || targetBlockFace == null) { + this.player.sendMessage(new ClientboundVeinMineResults()); + return; + } + + // Validate the client's target block against the server's client block. It should be within 2 blocks of the client's target + BlockPosition clientTargetBlock = message.getPosition(); + if (clientTargetBlock.distanceSquared(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()) >= 4) { // TODO: Reach attribute + this.player.sendMessage(new ClientboundVeinMineResults()); + return; + } + + targetBlock = targetBlock.getWorld().getBlockAt(clientTargetBlock.x(), clientTargetBlock.y(), clientTargetBlock.z()); + BlockData targetBlockData = targetBlock.getBlockData(); + + VeinMinerManager veinMinerManager = plugin.getVeinMinerManager(); + VeinMinerBlock vmBlock = veinMinerManager.getVeinMinerBlock(targetBlockData, category); + + if (vmBlock == null) { + this.player.sendMessage(new ClientboundVeinMineResults()); + return; + } + + BlockList aliasBlockList = veinMinerManager.getAlias(vmBlock); + List blocks = player.getVeinMiningPattern().allocateBlocks(targetBlock, targetBlockFace, vmBlock, category.getConfiguration(), aliasBlockList); + + this.player.sendMessage(new ClientboundVeinMineResults(blocks.parallelStream().map(block -> new BlockPosition(block.getX(), block.getY(), block.getZ())).toList())); + } + + @Override + public void handleSelectPattern(@NotNull ServerboundSelectPattern message) { + if (!player.getClientConfig().isAllowPatternSwitchingKeybind()) { + return; + } + + VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); + VeinMiningPattern pattern = plugin.getPatternRegistry().getOrDefault(message.getPatternKey(), plugin.getConfiguration().getDefaultVeinMiningPattern()); + String patternPermission = pattern.getPermission(); + + if (patternPermission != null && !player.getPlayer().hasPermission(patternPermission)) { + return; + } + + PlayerVeinMiningPatternChangeEvent event = VMEventFactory.callPlayerVeinMiningPatternChangeEvent(player.getPlayer(), player.getVeinMiningPattern(), pattern, PlayerVeinMiningPatternChangeEvent.Cause.CLIENT); + if (event.isCancelled()) { + return; + } + + this.player.setVeinMiningPattern(event.getNewPattern()); + } + +} diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlayer.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayer.java similarity index 62% rename from veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlayer.java rename to veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayer.java index ec618a93..2fa60f0c 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/VeinMinerPlayer.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayer.java @@ -1,23 +1,13 @@ -package wtf.choco.veinminer; +package wtf.choco.veinminer.player; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Objects; -import java.util.Queue; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; -import org.bukkit.Bukkit; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.RayTraceResult; import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; @@ -25,34 +15,21 @@ import wtf.choco.network.Message; import wtf.choco.network.data.NamespacedKey; import wtf.choco.network.receiver.MessageReceiver; -import wtf.choco.veinminer.api.event.player.PlayerVeinMiningPatternChangeEvent; -import wtf.choco.veinminer.block.BlockList; -import wtf.choco.veinminer.block.VeinMinerBlock; +import wtf.choco.veinminer.ActivationStrategy; +import wtf.choco.veinminer.VeinMiner; +import wtf.choco.veinminer.VeinMinerPlugin; import wtf.choco.veinminer.config.ClientConfig; -import wtf.choco.veinminer.manager.VeinMinerManager; -import wtf.choco.veinminer.manager.VeinMinerPlayerManager; import wtf.choco.veinminer.network.protocol.VeinMinerClientboundMessageListener; import wtf.choco.veinminer.network.protocol.VeinMinerServerboundMessageListener; -import wtf.choco.veinminer.network.protocol.clientbound.ClientboundHandshakeResponse; import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSetConfig; import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSetPattern; -import wtf.choco.veinminer.network.protocol.clientbound.ClientboundSyncRegisteredPatterns; -import wtf.choco.veinminer.network.protocol.clientbound.ClientboundVeinMineResults; -import wtf.choco.veinminer.network.protocol.serverbound.ServerboundHandshake; -import wtf.choco.veinminer.network.protocol.serverbound.ServerboundRequestVeinMine; -import wtf.choco.veinminer.network.protocol.serverbound.ServerboundSelectPattern; -import wtf.choco.veinminer.network.protocol.serverbound.ServerboundToggleVeinMiner; -import wtf.choco.veinminer.pattern.PatternRegistry; import wtf.choco.veinminer.pattern.VeinMiningPattern; import wtf.choco.veinminer.tool.VeinMinerToolCategory; -import wtf.choco.veinminer.util.BlockPosition; -import wtf.choco.veinminer.util.VMEventFactory; /** - * A {@link Player} wrapper holding all VeinMiner-related data for an online player, as well as a - * network handler for vein miner protocol messages for that player. + * A {@link Player} wrapper holding all VeinMiner-related data for an online player. */ -public final class VeinMinerPlayer implements MessageReceiver, VeinMinerServerboundMessageListener { +public final class VeinMinerPlayer implements MessageReceiver { private ActivationStrategy activationStrategy; private final Set disabledCategories = new HashSet<>(); @@ -60,17 +37,12 @@ public final class VeinMinerPlayer implements MessageReceiver, VeinMinerServerbo private boolean dirty = false; - private boolean clientReady = false; - private Queue onClientReady = new ConcurrentLinkedQueue<>(); - - private boolean usingClientMod = false; - private boolean clientKeyPressed = false; - private boolean veinMining = false; private ClientConfig clientConfig; private final Player player; + private final PlayerNetworkListener networkListener; /** * Construct a new {@link VeinMinerPlayer}. @@ -88,6 +60,7 @@ public final class VeinMinerPlayer implements MessageReceiver, VeinMinerServerbo public VeinMinerPlayer(@NotNull Player player, @NotNull ClientConfig clientConfig) { this.player = player; this.clientConfig = clientConfig; + this.networkListener = new PlayerNetworkListener(this); } /** @@ -113,6 +86,20 @@ public UUID getPlayerUUID() { return player.getUniqueId(); } + /** + * Get the {@link VeinMinerServerboundMessageListener} for this player. + *

+ * NOTE: Not part of the public API. This method is intended for internal use only. + * Callers should NEVER invoke any methods in the returned object. + * + * @return the serverbound message listener + */ + @Internal + @NotNull + public VeinMinerServerboundMessageListener getServerboundMessageListener() { + return networkListener; + } + /** * Set whether or not the given {@link VeinMinerToolCategory} is enabled for this player. * @@ -285,7 +272,7 @@ public VeinMiningPattern getVeinMiningPattern() { */ public boolean executeWhenClientIsReady(@NotNull Runnable runnable) { if (!isClientReady()) { - this.onClientReady.add(runnable); + this.networkListener.addOnClientReadyTask(runnable); return true; } @@ -322,7 +309,7 @@ public boolean executeWhenClientIsReady(@NotNull Consumer consu * @see #executeWhenClientIsReady(Consumer) */ public boolean isClientReady() { - return clientReady; + return networkListener.isClientReady(); } /** @@ -331,7 +318,7 @@ public boolean isClientReady() { * @return true if using client mod, false otherwise */ public boolean isUsingClientMod() { - return usingClientMod; + return networkListener.isUsingClientMod(); } /** @@ -341,7 +328,7 @@ public boolean isUsingClientMod() { * @return true if active, false otherwise */ public boolean isClientKeyPressed() { - return clientKeyPressed; + return networkListener.isClientKeyPressed(); } /** @@ -448,148 +435,4 @@ public void sendMessage(@NotNull Message me VeinMiner.PROTOCOL.sendMessageToClient(this, message); } - @Internal - @Override - public void handleHandshake(@NotNull ServerboundHandshake message) { - int serverProtocolVersion = VeinMiner.PROTOCOL.getVersion(); - if (serverProtocolVersion != message.getProtocolVersion()) { - this.player.kickPlayer("Your client-side version of VeinMiner (for Bukkit) is " + (serverProtocolVersion > message.getProtocolVersion() ? "out of date. Please update." : "too new. Please downgrade.")); - return; - } - - this.usingClientMod = true; - this.setActivationStrategy(ActivationStrategy.CLIENT); - this.dirty = false; // We can force dirty = false. Data hasn't loaded yet, but we still want to set the strategy to client automatically - - /* - * Let the client know whether or not the client is even allowed. - * We send this one tick later so we know that the player's connection has been initialized - */ - VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); - Bukkit.getScheduler().runTaskLater(plugin, () -> { - this.sendMessage(new ClientboundHandshakeResponse()); - - // Synchronize all registered patterns to the client - PatternRegistry patternRegistry = plugin.getPatternRegistry(); - - List patternKeys = new ArrayList<>(); - patternRegistry.getPatterns().forEach(pattern -> patternKeys.add(pattern.getKey())); - VeinMiningPattern defaultPattern = plugin.getConfiguration().getDefaultVeinMiningPattern(); - - // Move the default pattern to the start if it wasn't already there - if (patternKeys.size() > 1 && patternKeys.remove(defaultPattern.getKey())) { - patternKeys.add(0, defaultPattern.getKey()); - } - - // Don't send any patterns to which the player does not have access - patternKeys.removeIf(patternKey -> { - VeinMiningPattern pattern = patternRegistry.get(patternKey); - if (pattern == null) { - return true; - } - - String permission = pattern.getPermission(); - return permission != null && !player.hasPermission(permission); - }); - - this.sendMessage(new ClientboundSyncRegisteredPatterns(patternKeys)); - this.sendMessage(new ClientboundSetConfig(clientConfig)); - - // The client is ready, accept post-client init tasks now - this.clientReady = true; - - Runnable runnable; - while ((runnable = onClientReady.poll()) != null) { - runnable.run(); - } - }, 1); - } - - @Internal - @Override - public void handleToggleVeinMiner(@NotNull ServerboundToggleVeinMiner message) { - if (!clientConfig.isAllowActivationKeybind()) { - return; - } - - if (!VMEventFactory.callPlayerClientActivateVeinMinerEvent(player, message.isActivated())) { - return; - } - - this.clientKeyPressed = message.isActivated(); - } - - @Internal - @Override - public void handleRequestVeinMine(@NotNull ServerboundRequestVeinMine message) { - ItemStack itemStack = player.getInventory().getItemInMainHand(); - VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); - VeinMinerToolCategory category = plugin.getToolCategoryRegistry().get(itemStack); - - if (category == null) { - this.sendMessage(new ClientboundVeinMineResults()); - return; - } - - RayTraceResult rayTraceResult = player.rayTraceBlocks(6); - if (rayTraceResult == null) { - this.sendMessage(new ClientboundVeinMineResults()); - return; - } - - Block targetBlock = rayTraceResult.getHitBlock(); - BlockFace targetBlockFace = rayTraceResult.getHitBlockFace(); - - if (targetBlock == null || targetBlockFace == null) { - this.sendMessage(new ClientboundVeinMineResults()); - return; - } - - // Validate the client's target block against the server's client block. It should be within 2 blocks of the client's target - BlockPosition clientTargetBlock = message.getPosition(); - if (clientTargetBlock.distanceSquared(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()) >= 4) { // TODO: Reach attribute - this.sendMessage(new ClientboundVeinMineResults()); - return; - } - - targetBlock = targetBlock.getWorld().getBlockAt(clientTargetBlock.x(), clientTargetBlock.y(), clientTargetBlock.z()); - BlockData targetBlockData = targetBlock.getBlockData(); - - VeinMinerManager veinMinerManager = plugin.getVeinMinerManager(); - VeinMinerBlock vmBlock = veinMinerManager.getVeinMinerBlock(targetBlockData, category); - - if (vmBlock == null) { - this.sendMessage(new ClientboundVeinMineResults()); - return; - } - - BlockList aliasBlockList = veinMinerManager.getAlias(vmBlock); - List blocks = getVeinMiningPattern().allocateBlocks(targetBlock, targetBlockFace, vmBlock, category.getConfiguration(), aliasBlockList); - - this.sendMessage(new ClientboundVeinMineResults(blocks.parallelStream().map(block -> new BlockPosition(block.getX(), block.getY(), block.getZ())).toList())); - } - - @Internal - @Override - public void handleSelectPattern(@NotNull ServerboundSelectPattern message) { - if (!clientConfig.isAllowPatternSwitchingKeybind()) { - return; - } - - VeinMinerPlugin plugin = VeinMinerPlugin.getInstance(); - VeinMiningPattern pattern = plugin.getPatternRegistry().getOrDefault(message.getPatternKey(), plugin.getConfiguration().getDefaultVeinMiningPattern()); - String patternPermission = pattern.getPermission(); - - if (patternPermission != null && !player.hasPermission(patternPermission)) { - return; - } - - PlayerVeinMiningPatternChangeEvent event = VMEventFactory.callPlayerVeinMiningPatternChangeEvent(player, getVeinMiningPattern(), pattern, PlayerVeinMiningPatternChangeEvent.Cause.CLIENT); - if (event.isCancelled()) { - return; - } - - this.setVeinMiningPattern(event.getNewPattern()); - } - } diff --git a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/manager/VeinMinerPlayerManager.java b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayerManager.java similarity index 97% rename from veinminer-bukkit/src/main/java/wtf/choco/veinminer/manager/VeinMinerPlayerManager.java rename to veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayerManager.java index 2ca2745e..fd068ca2 100644 --- a/veinminer-bukkit/src/main/java/wtf/choco/veinminer/manager/VeinMinerPlayerManager.java +++ b/veinminer-bukkit/src/main/java/wtf/choco/veinminer/player/VeinMinerPlayerManager.java @@ -1,4 +1,4 @@ -package wtf.choco.veinminer.manager; +package wtf.choco.veinminer.player; import java.util.Collection; import java.util.Collections; @@ -13,7 +13,6 @@ import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; -import wtf.choco.veinminer.VeinMinerPlayer; import wtf.choco.veinminer.config.ClientConfig; /**