Skip to content

Commit

Permalink
Separate the serverbound listener from VeinMinerPlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
2008Choco committed Feb 11, 2024
1 parent 90bbfe5 commit 1acbd89
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import org.jetbrains.annotations.NotNull;

import wtf.choco.veinminer.player.VeinMinerPlayer;

/**
* Represents a different method of activating vein miner.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@
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;
import wtf.choco.veinminer.pattern.PatternRegistry;
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -27,7 +28,8 @@ public VeinMinerBukkitChannelRegistrar(@NotNull VeinMinerPlugin plugin) {

@Override
protected VeinMinerServerboundMessageListener onSuccessfulMessage(@NotNull Player player, @NotNull String channel, @NotNull Message<VeinMinerServerboundMessageListener> message) {
return plugin.getPlayerManager().get(player);
VeinMinerPlayer veinMinerPlayer = plugin.getPlayerManager().get(player);
return (veinMinerPlayer != null) ? veinMinerPlayer.getServerboundMessageListener() : null;
}

}
Original file line number Diff line number Diff line change
@@ -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}.
* <p>
* <strong>NOTE:</strong> 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<Runnable> 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.
* <p>
* 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<NamespacedKey> 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<Block> 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());
}

}
Loading

0 comments on commit 1acbd89

Please sign in to comment.