204 lines
9.1 KiB
Java
204 lines
9.1 KiB
Java
/*
|
|
* 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.api.Via;
|
|
import com.viaversion.viaversion.api.connection.ProtocolInfo;
|
|
import com.viaversion.viaversion.api.connection.UserConnection;
|
|
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
|
import com.viaversion.viaversion.bukkit.tasks.protocol1_12to1_11_1.BukkitInventoryUpdateTask;
|
|
import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
|
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
|
|
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.storage.ItemTransaction;
|
|
import com.viaversion.viaversion.util.ReflectionUtil;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.logging.Level;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.inventory.InventoryType;
|
|
import org.bukkit.inventory.Inventory;
|
|
import org.bukkit.inventory.InventoryView;
|
|
import org.bukkit.inventory.ItemStack;
|
|
|
|
public class BukkitInventoryQuickMoveProvider extends InventoryQuickMoveProvider {
|
|
|
|
private final Map<UUID, BukkitInventoryUpdateTask> updateTasks = new ConcurrentHashMap<>();
|
|
private final boolean supported;
|
|
// packet class
|
|
private Class<?> windowClickPacketClass;
|
|
private Object clickTypeEnum;
|
|
// Use for nms
|
|
private Method nmsItemMethod;
|
|
private Method craftPlayerHandle;
|
|
private Field connection;
|
|
private Method packetMethod;
|
|
|
|
public BukkitInventoryQuickMoveProvider() {
|
|
this.supported = isSupported();
|
|
setupReflection();
|
|
}
|
|
|
|
@Override
|
|
public boolean registerQuickMoveAction(short windowId, short slotId, short actionId, UserConnection userConnection) {
|
|
if (!supported) {
|
|
return false;
|
|
}
|
|
if (slotId < 0) { // clicked out of inv slot
|
|
return false;
|
|
}
|
|
if (windowId == 0) {
|
|
// windowId is always 0 for player inventory.
|
|
// This has almost definitely something to do with the offhand slot.
|
|
if (slotId >= 36 && slotId <= 45) {
|
|
int protocolId = Via.getAPI().getServerVersion().lowestSupportedVersion();
|
|
// this seems to be working just fine.
|
|
if (protocolId == ProtocolVersion.v1_8.getVersion()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
ProtocolInfo info = userConnection.getProtocolInfo();
|
|
UUID uuid = info.getUuid();
|
|
BukkitInventoryUpdateTask updateTask = updateTasks.get(uuid);
|
|
final boolean registered = updateTask != null;
|
|
if (!registered) {
|
|
updateTask = new BukkitInventoryUpdateTask(this, uuid);
|
|
updateTasks.put(uuid, updateTask);
|
|
}
|
|
// http://wiki.vg/index.php?title=Protocol&oldid=13223#Click_Window
|
|
updateTask.addItem(windowId, slotId, actionId);
|
|
if (!registered && Via.getPlatform().isPluginEnabled()) {
|
|
Via.getPlatform().runSync(updateTask);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public Object buildWindowClickPacket(Player p, ItemTransaction storage) {
|
|
if (!supported) {
|
|
return null;
|
|
}
|
|
InventoryView inv = p.getOpenInventory();
|
|
short slotId = storage.getSlotId();
|
|
Inventory tinv = inv.getTopInventory();
|
|
InventoryType tinvtype = tinv == null ? null : tinv.getType(); // can this even be null?
|
|
if (tinvtype != null) {
|
|
int protocolId = Via.getAPI().getServerVersion().lowestSupportedVersion();
|
|
if (protocolId == ProtocolVersion.v1_8.getVersion()) {
|
|
if (tinvtype == InventoryType.BREWING) {
|
|
// 1.9 added the blaze powder slot to brewing stand fix for 1.8 servers
|
|
if (slotId >= 5 && slotId <= 40) {
|
|
slotId -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ItemStack itemstack = null;
|
|
// must be after top inventory slot check
|
|
if (slotId <= inv.countSlots()) {
|
|
itemstack = inv.getItem(slotId);
|
|
} else {
|
|
// if not true we got too many slots (version inventory slot changes)?
|
|
String cause = "Too many inventory slots: slotId: " + slotId + " invSlotCount: " + inv.countSlots()
|
|
+ " invType: " + inv.getType() + " topInvType: " + tinvtype;
|
|
Via.getPlatform().getLogger().severe("Failed to get an item to create a window click packet. Please report this issue to the ViaVersion Github: " + cause);
|
|
}
|
|
Object packet = null;
|
|
try {
|
|
packet = windowClickPacketClass.getDeclaredConstructor().newInstance();
|
|
Object nmsItem = itemstack == null ? null : nmsItemMethod.invoke(null, itemstack);
|
|
ReflectionUtil.set(packet, "a", (int) storage.getWindowId());
|
|
ReflectionUtil.set(packet, "slot", (int) slotId);
|
|
ReflectionUtil.set(packet, "button", 0); // shift + left mouse click
|
|
ReflectionUtil.set(packet, "d", storage.getActionId());
|
|
ReflectionUtil.set(packet, "item", nmsItem);
|
|
int protocolId = Via.getAPI().getServerVersion().lowestSupportedVersion();
|
|
if (protocolId == ProtocolVersion.v1_8.getVersion()) {
|
|
ReflectionUtil.set(packet, "shift", 1);
|
|
} else if (protocolId >= ProtocolVersion.v1_9.getVersion()) { // 1.9+
|
|
ReflectionUtil.set(packet, "shift", clickTypeEnum);
|
|
}
|
|
} catch (Exception e) {
|
|
Via.getPlatform().getLogger().log(Level.SEVERE, "Failed to create a window click packet. Please report this issue to the ViaVersion Github: " + e.getMessage(), e);
|
|
}
|
|
return packet;
|
|
}
|
|
|
|
public boolean sendPacketToServer(Player p, Object packet) {
|
|
if (packet == null) {
|
|
// let the other packets pass through
|
|
return true;
|
|
}
|
|
try {
|
|
Object entityPlayer = craftPlayerHandle.invoke(p);
|
|
Object playerConnection = connection.get(entityPlayer);
|
|
// send
|
|
packetMethod.invoke(playerConnection, packet);
|
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
Via.getPlatform().getLogger().log(Level.SEVERE, "Failed to send packet to server", e);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void onTaskExecuted(UUID uuid) {
|
|
updateTasks.remove(uuid);
|
|
}
|
|
|
|
private void setupReflection() {
|
|
if (!supported) {
|
|
return;
|
|
}
|
|
try {
|
|
this.windowClickPacketClass = NMSUtil.nms("PacketPlayInWindowClick");
|
|
int protocolId = Via.getAPI().getServerVersion().lowestSupportedVersion();
|
|
if (protocolId >= ProtocolVersion.v1_9.getVersion()) {
|
|
Class<?> eclassz = NMSUtil.nms("InventoryClickType");
|
|
Object[] constants = eclassz.getEnumConstants();
|
|
this.clickTypeEnum = constants[1]; // QUICK_MOVE
|
|
}
|
|
Class<?> craftItemStack = NMSUtil.obc("inventory.CraftItemStack");
|
|
this.nmsItemMethod = craftItemStack.getDeclaredMethod("asNMSCopy", ItemStack.class);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Couldn't find required inventory classes", e);
|
|
}
|
|
try {
|
|
this.craftPlayerHandle = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
|
|
} catch (NoSuchMethodException | ClassNotFoundException e) {
|
|
throw new RuntimeException("Couldn't find CraftPlayer", e);
|
|
}
|
|
try {
|
|
this.connection = NMSUtil.nms("EntityPlayer").getDeclaredField("playerConnection");
|
|
} catch (NoSuchFieldException | ClassNotFoundException e) {
|
|
throw new RuntimeException("Couldn't find Player Connection", e);
|
|
}
|
|
try {
|
|
this.packetMethod = NMSUtil.nms("PlayerConnection").getDeclaredMethod("a", windowClickPacketClass);
|
|
} catch (NoSuchMethodException | ClassNotFoundException e) {
|
|
throw new RuntimeException("Couldn't find CraftPlayer", e);
|
|
}
|
|
}
|
|
|
|
private boolean isSupported() {
|
|
int protocolId = Via.getAPI().getServerVersion().lowestSupportedVersion();
|
|
return protocolId >= ProtocolVersion.v1_8.getVersion() && protocolId <= ProtocolVersion.v1_11_1.getVersion(); // 1.8-1.11.2, not needed with 1.12
|
|
}
|
|
} |