mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-12-22 08:27:37 +01:00
Implement block and entity picking
This commit is contained in:
parent
0c4dbbe0bb
commit
3cd01978df
@ -37,6 +37,13 @@ public class BlockPosition {
|
||||
return new BlockPosition(x + face.modX(), y + face.modY(), z + face.modZ());
|
||||
}
|
||||
|
||||
public double distanceFromCenterSquared(final double x, final double y, final double z) {
|
||||
final double dx = this.x + 0.5 - x;
|
||||
final double dy = this.y + 0.5 - y;
|
||||
final double dz = this.z + 0.5 - z;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
public int x() {
|
||||
return x;
|
||||
}
|
||||
|
@ -26,24 +26,25 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.bukkit.listeners.UpdateListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.multiversion.PlayerSneakListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_14_4to1_15.EntityToggleGlideListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_19_3to1_19_4.ArmorToggleListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_18_2to1_19.BlockBreakListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_19_3to1_19_4.ArmorToggleListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.LegacyChangeItemListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PaperPlayerChangeItemListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.ArmorListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.BlockListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.DeathListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.HandItemCache;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.PaperPatch;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PaperPlayerChangeItemListener;
|
||||
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PlayerChangeItemListener;
|
||||
import com.viaversion.viaversion.bukkit.providers.BukkitAckSequenceProvider;
|
||||
import com.viaversion.viaversion.bukkit.providers.BukkitBlockConnectionProvider;
|
||||
import com.viaversion.viaversion.bukkit.providers.BukkitInventoryQuickMoveProvider;
|
||||
import com.viaversion.viaversion.bukkit.providers.BukkitPickItemProvider;
|
||||
import com.viaversion.viaversion.bukkit.providers.BukkitViaMovementTransmitter;
|
||||
import com.viaversion.viaversion.protocols.v1_11_1to1_12.provider.InventoryQuickMoveProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.providers.BlockConnectionProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.provider.AckSequenceProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.provider.HandItemProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.provider.MovementTransmitterProvider;
|
||||
import java.util.HashSet;
|
||||
@ -188,6 +189,11 @@ public class BukkitViaLoader implements ViaPlatformLoader {
|
||||
new LegacyChangeItemListener(plugin).register();
|
||||
}
|
||||
}
|
||||
if (serverProtocolVersion.olderThan(ProtocolVersion.v1_21_4)) {
|
||||
if (PaperViaInjector.hasMethod(Material.class, "isItem")) {
|
||||
Via.getManager().getProviders().use(PickItemProvider.class, new BukkitPickItemProvider(plugin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasGetHandMethod() {
|
||||
|
@ -65,12 +65,7 @@ public final class PaperViaInjector {
|
||||
}
|
||||
|
||||
private static boolean hasServerProtocolMethod() {
|
||||
try {
|
||||
Class.forName("org.bukkit.UnsafeValues").getDeclaredMethod("getProtocolVersion");
|
||||
return true;
|
||||
} catch (final ClassNotFoundException | NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
return hasMethod("org.bukkit.UnsafeValues", "getProtocolVersion");
|
||||
}
|
||||
|
||||
private static boolean hasPaperInjectionMethod() {
|
||||
@ -78,12 +73,7 @@ public final class PaperViaInjector {
|
||||
}
|
||||
|
||||
private static boolean hasIsStoppingMethod() {
|
||||
try {
|
||||
Bukkit.class.getDeclaredMethod("isStopping");
|
||||
return true;
|
||||
} catch (final NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
return hasMethod(Bukkit.class, "isStopping");
|
||||
}
|
||||
|
||||
private static boolean hasPacketLimiter() {
|
||||
@ -98,4 +88,22 @@ public final class PaperViaInjector {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasMethod(final String className, final String method) {
|
||||
try {
|
||||
Class.forName(className).getDeclaredMethod(method);
|
||||
return true;
|
||||
} catch (final ClassNotFoundException | NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasMethod(final Class<?> clazz, final String method) {
|
||||
try {
|
||||
clazz.getDeclaredMethod(method);
|
||||
return true;
|
||||
} catch (final NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2024 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.bukkit.providers;
|
||||
|
||||
import com.viaversion.viaversion.ViaVersionPlugin;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemFactory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class BukkitPickItemProvider extends PickItemProvider {
|
||||
private static final boolean HAS_PLACEMENT_MATERIAL_METHOD = PaperViaInjector.hasMethod("org.bukkit.block.BlockData", "getPlacementMaterial");
|
||||
private static final boolean HAS_SPAWN_EGG_METHOD = PaperViaInjector.hasMethod(ItemFactory.class, "getSpawnEgg");
|
||||
private static final double BLOCK_RANGE = 4.5 + 1;
|
||||
private static final double BLOCK_RANGE_SQUARED = BLOCK_RANGE * BLOCK_RANGE;
|
||||
private static final double ENTITY_RANGE = 3 + 3;
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public BukkitPickItemProvider(final ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickItemFromBlock(final UserConnection connection, final BlockPosition blockPosition, final boolean includeData) {
|
||||
final UUID uuid = connection.getProtocolInfo().getUuid();
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
final Player player = plugin.getServer().getPlayer(uuid);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Location playerLocation = player.getLocation();
|
||||
if (blockPosition.distanceFromCenterSquared(playerLocation.getX(), playerLocation.getY(), playerLocation.getZ()) > BLOCK_RANGE_SQUARED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Block block = player.getWorld().getBlockAt(blockPosition.x(), blockPosition.y(), blockPosition.z());
|
||||
if (block.getType() == Material.AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemStack item = blockToItem(block, includeData && player.getGameMode() == GameMode.CREATIVE);
|
||||
if (item != null) {
|
||||
pickItem(player, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private @Nullable ItemStack blockToItem(final Block block, final boolean includeData) {
|
||||
if (HAS_PLACEMENT_MATERIAL_METHOD) {
|
||||
final ItemStack item = new ItemStack(block.getBlockData().getPlacementMaterial(), 1);
|
||||
if (includeData && item.getItemMeta() instanceof final BlockStateMeta blockStateMeta) {
|
||||
blockStateMeta.setBlockState(block.getState());
|
||||
}
|
||||
return item;
|
||||
} else if (block.getType().isItem()) {
|
||||
return new ItemStack(block.getType(), 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickItemFromEntity(final UserConnection connection, final int entityId, final boolean includeData) {
|
||||
if (!HAS_SPAWN_EGG_METHOD) {
|
||||
return;
|
||||
}
|
||||
|
||||
final UUID uuid = connection.getProtocolInfo().getUuid();
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
final Player player = plugin.getServer().getPlayer(uuid);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Entity entity = player.getWorld().getNearbyEntities(player.getLocation(), ENTITY_RANGE, ENTITY_RANGE, ENTITY_RANGE).stream()
|
||||
.filter(e -> e.getEntityId() == entityId)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Material spawnEggType = Bukkit.getItemFactory().getSpawnEgg(entity.getType());
|
||||
if (spawnEggType != null) {
|
||||
pickItem(player, new ItemStack(spawnEggType, 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void pickItem(final Player player, final ItemStack item) {
|
||||
// Find matching item
|
||||
final PlayerInventory inventory = player.getInventory();
|
||||
final ItemStack[] contents = inventory.getStorageContents();
|
||||
int sourceSlot = -1;
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
final ItemStack content = contents[i];
|
||||
if (content == null || !content.isSimilar(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sourceSlot = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sourceSlot != -1) {
|
||||
moveToHotbar(inventory, sourceSlot, contents);
|
||||
} else if (player.getGameMode() == GameMode.CREATIVE) {
|
||||
spawnItem(item, inventory, contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnItem(final ItemStack item, final PlayerInventory inventory, final ItemStack[] contents) {
|
||||
final int targetSlot = findEmptyHotbarSlot(inventory, inventory.getHeldItemSlot());
|
||||
inventory.setHeldItemSlot(targetSlot);
|
||||
final ItemStack heldItem = inventory.getItem(targetSlot);
|
||||
int emptySlot = targetSlot;
|
||||
if (heldItem != null && heldItem.getType() != Material.AIR) {
|
||||
// Swap to the first free slot in the inventory, else add it to the current hotbar slot
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
if (contents[i] == null || contents[i].getType() == Material.AIR) {
|
||||
emptySlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setItem(emptySlot, heldItem);
|
||||
inventory.setItemInMainHand(item);
|
||||
}
|
||||
|
||||
private void moveToHotbar(final PlayerInventory inventory, final int sourceSlot, final ItemStack[] contents) {
|
||||
if (sourceSlot <= 9) {
|
||||
inventory.setHeldItemSlot(sourceSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
final int heldSlot = inventory.getHeldItemSlot();
|
||||
final int targetSlot = findEmptyHotbarSlot(inventory, heldSlot);
|
||||
inventory.setHeldItemSlot(targetSlot);
|
||||
final ItemStack heldItem = inventory.getItem(targetSlot);
|
||||
inventory.setItemInMainHand(contents[sourceSlot]);
|
||||
inventory.setItem(sourceSlot, heldItem);
|
||||
}
|
||||
|
||||
private int findEmptyHotbarSlot(final PlayerInventory inventory, final int heldSlot) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
final ItemStack item = inventory.getItem(i);
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return heldSlot;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.minecraft.Particle;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
|
||||
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
||||
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
|
||||
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
|
||||
@ -35,6 +36,7 @@ import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundCon
|
||||
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.BlockItemPacketRewriter1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.ComponentRewriter1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.EntityPacketRewriter1_21_4;
|
||||
@ -190,6 +192,11 @@ public final class Protocol1_21_2To1_21_4 extends AbstractProtocol<ClientboundPa
|
||||
super.onMappingDataLoaded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(final ViaProviders providers) {
|
||||
providers.register(PickItemProvider.class, new PickItemProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final UserConnection connection) {
|
||||
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_4.PLAYER));
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2024 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.platform.providers.Provider;
|
||||
|
||||
public class PickItemProvider implements Provider {
|
||||
|
||||
public void pickItemFromBlock(final UserConnection connection, final BlockPosition blockPosition, final boolean includeData) {
|
||||
}
|
||||
|
||||
public void pickItemFromEntity(final UserConnection connection, final int entityId, final boolean includeData) {
|
||||
}
|
||||
}
|
@ -19,7 +19,9 @@ package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
@ -31,6 +33,7 @@ import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
|
||||
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
|
||||
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
@ -61,8 +64,18 @@ public final class BlockItemPacketRewriter1_21_4 extends StructuredItemRewriter<
|
||||
wrapper.write(Types.VAR_INT, (int) slot);
|
||||
});
|
||||
|
||||
protocol.cancelServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_BLOCK);
|
||||
protocol.cancelServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_ENTITY);
|
||||
protocol.registerServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_BLOCK, null, wrapper -> {
|
||||
final BlockPosition blockPosition = wrapper.read(Types.BLOCK_POSITION1_14);
|
||||
final boolean includeData = wrapper.read(Types.BOOLEAN);
|
||||
Via.getManager().getProviders().get(PickItemProvider.class).pickItemFromBlock(wrapper.user(), blockPosition, includeData);
|
||||
wrapper.cancel();
|
||||
});
|
||||
protocol.registerServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_ENTITY, null, wrapper -> {
|
||||
final int entityId = wrapper.read(Types.VAR_INT);
|
||||
final boolean includeData = wrapper.read(Types.BOOLEAN);
|
||||
Via.getManager().getProviders().get(PickItemProvider.class).pickItemFromEntity(wrapper.user(), entityId, includeData);
|
||||
wrapper.cancel();
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
|
||||
registerSetPlayerInventory(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY);
|
||||
|
Loading…
Reference in New Issue
Block a user