2021-03-22 23:06:40 +01:00
/ *
* This file is part of ViaVersion - https : //github.com/ViaVersion/ViaVersion
2024-01-01 12:39:45 +01:00
* Copyright ( C ) 2016 - 2024 ViaVersion and contributors
2021-03-22 23:06:40 +01:00
*
* 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/>.
* /
2021-04-26 20:52:34 +02:00
package com.viaversion.viaversion.bukkit.providers ;
2017-09-24 20:35:38 +02:00
2021-04-26 20:52:34 +02:00
import com.viaversion.viaversion.api.Via ;
2021-04-27 13:41:39 +02:00
import com.viaversion.viaversion.api.connection.ProtocolInfo ;
2021-04-26 21:35:29 +02:00
import com.viaversion.viaversion.api.connection.UserConnection ;
2021-04-26 21:16:10 +02:00
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion ;
2021-04-26 20:52:34 +02:00
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 ;
2018-10-27 13:25:42 +02:00
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 ;
2023-02-12 11:44:25 +01:00
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 ;
2018-10-27 13:25:42 +02:00
2017-10-08 16:33:43 +02:00
public class BukkitInventoryQuickMoveProvider extends InventoryQuickMoveProvider {
2017-09-24 20:35:38 +02:00
2019-08-29 22:24:01 +02:00
private final Map < UUID , BukkitInventoryUpdateTask > updateTasks = new ConcurrentHashMap < > ( ) ;
2019-08-23 22:13:37 +02:00
private final boolean supported ;
2017-09-25 17:04:39 +02:00
// packet class
2017-10-08 16:33:43 +02:00
private Class < ? > windowClickPacketClass ;
2017-09-25 20:49:43 +02:00
private Object clickTypeEnum ;
2017-09-25 17:04:39 +02:00
// Use for nms
private Method nmsItemMethod ;
2017-10-08 16:33:43 +02:00
private Method craftPlayerHandle ;
2017-09-25 17:04:39 +02:00
private Field connection ;
private Method packetMethod ;
2017-10-08 16:33:43 +02:00
public BukkitInventoryQuickMoveProvider ( ) {
2017-09-25 17:04:39 +02:00
this . supported = isSupported ( ) ;
setupReflection ( ) ;
}
2017-09-25 15:13:56 +02:00
2017-09-24 20:35:38 +02:00
@Override
2018-01-22 20:32:22 +01:00
public boolean registerQuickMoveAction ( short windowId , short slotId , short actionId , UserConnection userConnection ) {
2017-09-25 17:04:39 +02:00
if ( ! supported ) {
return false ;
}
2017-11-28 16:17:02 +01:00
if ( slotId < 0 ) { // clicked out of inv slot
return false ;
}
2018-01-22 20:32:22 +01:00
if ( windowId = = 0 ) {
2020-12-08 12:42:13 +01:00
// windowId is always 0 for player inventory.
// This has almost definitely something to do with the offhand slot.
2021-04-03 09:09:00 +02:00
if ( slotId > = 36 & & slotId < = 45 ) {
2021-03-26 12:51:38 +01:00
int protocolId = Via . getAPI ( ) . getServerVersion ( ) . lowestSupportedVersion ( ) ;
2018-01-22 20:32:22 +01:00
// this seems to be working just fine.
2020-10-16 18:21:45 +02:00
if ( protocolId = = ProtocolVersion . v1_8 . getVersion ( ) ) {
2018-01-22 20:32:22 +01:00
return false ;
}
}
}
2020-06-07 12:19:36 +02:00
ProtocolInfo info = userConnection . getProtocolInfo ( ) ;
2017-09-25 17:04:39 +02:00
UUID uuid = info . getUuid ( ) ;
2019-08-29 22:24:01 +02:00
BukkitInventoryUpdateTask updateTask = updateTasks . get ( uuid ) ;
2017-10-08 16:33:43 +02:00
final boolean registered = updateTask ! = null ;
2017-09-25 17:04:39 +02:00
if ( ! registered ) {
2017-10-08 16:33:43 +02:00
updateTask = new BukkitInventoryUpdateTask ( this , uuid ) ;
2019-08-29 22:24:01 +02:00
updateTasks . put ( uuid , updateTask ) ;
2017-09-25 17:04:39 +02:00
}
2017-09-25 15:13:56 +02:00
// http://wiki.vg/index.php?title=Protocol&oldid=13223#Click_Window
2017-10-08 16:33:43 +02:00
updateTask . addItem ( windowId , slotId , actionId ) ;
2020-12-08 12:42:13 +01:00
if ( ! registered & & Via . getPlatform ( ) . isPluginEnabled ( ) ) {
2021-02-09 11:35:13 +01:00
Via . getPlatform ( ) . runSync ( updateTask ) ;
2017-09-25 17:04:39 +02:00
}
return true ;
}
2017-10-08 16:33:43 +02:00
public Object buildWindowClickPacket ( Player p , ItemTransaction storage ) {
2017-09-25 17:04:39 +02:00
if ( ! supported ) {
return null ;
}
InventoryView inv = p . getOpenInventory ( ) ;
short slotId = storage . getSlotId ( ) ;
2018-01-22 20:32:22 +01:00
Inventory tinv = inv . getTopInventory ( ) ;
InventoryType tinvtype = tinv = = null ? null : tinv . getType ( ) ; // can this even be null?
if ( tinvtype ! = null ) {
2021-03-26 12:51:38 +01:00
int protocolId = Via . getAPI ( ) . getServerVersion ( ) . lowestSupportedVersion ( ) ;
2020-10-16 18:21:45 +02:00
if ( protocolId = = ProtocolVersion . v1_8 . getVersion ( ) ) {
2018-01-22 20:32:22 +01:00
if ( tinvtype = = InventoryType . BREWING ) {
// 1.9 added the blaze powder slot to brewing stand fix for 1.8 servers
if ( slotId > = 5 & & slotId < = 40 ) {
2019-08-23 22:13:37 +02:00
slotId - = 1 ;
2018-01-22 20:32:22 +01:00
}
}
}
2017-09-25 17:04:39 +02:00
}
2018-01-22 20:32:22 +01:00
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 ) ;
2017-09-25 17:04:39 +02:00
}
2017-10-08 16:33:43 +02:00
Object packet = null ;
2017-09-25 17:04:39 +02:00
try {
2018-01-22 20:32:22 +01:00
packet = windowClickPacketClass . getDeclaredConstructor ( ) . newInstance ( ) ;
Object nmsItem = itemstack = = null ? null : nmsItemMethod . invoke ( null , itemstack ) ;
2017-10-08 16:33:43 +02:00
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 ) ;
2021-03-26 12:51:38 +01:00
int protocolId = Via . getAPI ( ) . getServerVersion ( ) . lowestSupportedVersion ( ) ;
2020-10-16 18:21:45 +02:00
if ( protocolId = = ProtocolVersion . v1_8 . getVersion ( ) ) {
2017-10-08 16:33:43 +02:00
ReflectionUtil . set ( packet , " shift " , 1 ) ;
2020-10-16 18:21:45 +02:00
} else if ( protocolId > = ProtocolVersion . v1_9 . getVersion ( ) ) { // 1.9+
2017-10-08 16:33:43 +02:00
ReflectionUtil . set ( packet , " shift " , clickTypeEnum ) ;
2017-09-25 20:49:43 +02:00
}
2017-09-25 17:04:39 +02:00
} catch ( Exception e ) {
2018-01-22 20:32:22 +01:00
Via . getPlatform ( ) . getLogger ( ) . log ( Level . SEVERE , " Failed to create a window click packet. Please report this issue to the ViaVersion Github: " + e . getMessage ( ) , e ) ;
2017-09-25 17:04:39 +02:00
}
2017-10-08 16:33:43 +02:00
return packet ;
2017-09-25 17:04:39 +02:00
}
2018-01-22 20:32:22 +01:00
public boolean sendPacketToServer ( Player p , Object packet ) {
2017-09-25 17:04:39 +02:00
if ( packet = = null ) {
2018-01-22 20:32:22 +01:00
// let the other packets pass through
return true ;
2017-09-25 17:04:39 +02:00
}
try {
2017-10-08 16:33:43 +02:00
Object entityPlayer = craftPlayerHandle . invoke ( p ) ;
Object playerConnection = connection . get ( entityPlayer ) ;
2017-09-25 17:04:39 +02:00
// send
2017-10-08 16:33:43 +02:00
packetMethod . invoke ( playerConnection , packet ) ;
2017-09-25 17:04:39 +02:00
} catch ( IllegalAccessException | InvocationTargetException e ) {
2024-03-09 13:57:41 +01:00
Via . getPlatform ( ) . getLogger ( ) . log ( Level . SEVERE , " Failed to send packet to server " , e ) ;
2017-09-25 17:04:39 +02:00
return false ;
}
return true ;
2017-09-24 20:35:38 +02:00
}
2017-09-25 15:13:56 +02:00
public void onTaskExecuted ( UUID uuid ) {
2019-08-29 22:24:01 +02:00
updateTasks . remove ( uuid ) ;
2017-09-25 15:13:56 +02:00
}
2017-09-25 17:04:39 +02:00
private void setupReflection ( ) {
if ( ! supported ) {
return ;
}
try {
2017-10-08 16:33:43 +02:00
this . windowClickPacketClass = NMSUtil . nms ( " PacketPlayInWindowClick " ) ;
2021-03-26 12:51:38 +01:00
int protocolId = Via . getAPI ( ) . getServerVersion ( ) . lowestSupportedVersion ( ) ;
2020-10-16 18:21:45 +02:00
if ( protocolId > = ProtocolVersion . v1_9 . getVersion ( ) ) {
2017-09-28 19:44:49 +02:00
Class < ? > eclassz = NMSUtil . nms ( " InventoryClickType " ) ;
Object [ ] constants = eclassz . getEnumConstants ( ) ;
this . clickTypeEnum = constants [ 1 ] ; // QUICK_MOVE
}
2017-10-08 16:33:43 +02:00
Class < ? > craftItemStack = NMSUtil . obc ( " inventory.CraftItemStack " ) ;
this . nmsItemMethod = craftItemStack . getDeclaredMethod ( " asNMSCopy " , ItemStack . class ) ;
2017-09-25 17:04:39 +02:00
} catch ( Exception e ) {
2017-09-28 19:44:49 +02:00
throw new RuntimeException ( " Couldn't find required inventory classes " , e ) ;
2017-09-25 17:04:39 +02:00
}
try {
2017-10-08 16:33:43 +02:00
this . craftPlayerHandle = NMSUtil . obc ( " entity.CraftPlayer " ) . getDeclaredMethod ( " getHandle " ) ;
2017-09-25 17:04:39 +02:00
} 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 {
2017-10-08 16:33:43 +02:00
this . packetMethod = NMSUtil . nms ( " PlayerConnection " ) . getDeclaredMethod ( " a " , windowClickPacketClass ) ;
2017-09-25 17:04:39 +02:00
} catch ( NoSuchMethodException | ClassNotFoundException e ) {
throw new RuntimeException ( " Couldn't find CraftPlayer " , e ) ;
}
}
private boolean isSupported ( ) {
2021-03-26 12:51:38 +01:00
int protocolId = Via . getAPI ( ) . getServerVersion ( ) . lowestSupportedVersion ( ) ;
2024-02-10 17:58:54 +01:00
return protocolId > = ProtocolVersion . v1_8 . getVersion ( ) & & protocolId < = ProtocolVersion . v1_11_1 . getVersion ( ) ; // 1.8-1.11.2, not needed with 1.12
2017-09-25 17:04:39 +02:00
}
2017-09-24 20:35:38 +02:00
}