Skip to content

Commit

Permalink
Merge pull request ViaVersion#754 from mmxw11/master
Browse files Browse the repository at this point in the history
Improve inventory handling for Bukkit servers
  • Loading branch information
Myles authored Oct 11, 2017
2 parents 358267d + e372a56 commit 875e5d7
Show file tree
Hide file tree
Showing 17 changed files with 332 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public TaskId runSync(Runnable runnable) {
}
}

@Override
public TaskId runSync(Runnable runnable, Long ticks) {
return new BukkitTaskId(getServer().getScheduler().runTaskLater(this, runnable, ticks).getTaskId());
}

@Override
public TaskId runRepeatingSync(Runnable runnable, Long ticks) {
return new BukkitTaskId(getServer().getScheduler().runTaskTimer(this, runnable, ticks, 0).getTaskId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ public boolean isForceJsonTransform() {
public boolean is1_12NBTArrayFix() {
return getBoolean("chat-nbt-fix", true);
}

@Override
public boolean is1_12QuickMoveActionFix() {
return getBoolean("quick-move-action-fix", true);
}

@Override
public List<Integer> getBlockedProtocols() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
package us.myles.ViaVersion.bukkit.platform;

import lombok.AllArgsConstructor;

import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;

import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.platform.ViaPlatformLoader;
import us.myles.ViaVersion.bukkit.listeners.UpdateListener;
import us.myles.ViaVersion.bukkit.listeners.protocol1_9to1_8.*;
import us.myles.ViaVersion.bukkit.providers.BukkitInventoryQuickMoveProvider;
import us.myles.ViaVersion.bukkit.providers.BukkitViaBulkChunkTranslator;
import us.myles.ViaVersion.bukkit.providers.BukkitViaMovementTransmitter;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
Expand Down Expand Up @@ -60,6 +64,9 @@ public void onPlayerQuit(PlayerQuitEvent e) {
/* Providers */
Via.getManager().getProviders().use(BulkChunkTranslatorProvider.class, new BukkitViaBulkChunkTranslator());
Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BukkitViaMovementTransmitter());
if (plugin.getConf().is1_12QuickMoveActionFix()) {
Via.getManager().getProviders().use(InventoryQuickMoveProvider.class, new BukkitInventoryQuickMoveProvider());
}
Via.getManager().getProviders().use(HandItemProvider.class, new HandItemProvider() {
@Override
public Item getHandItem(final UserConnection info) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package us.myles.ViaVersion.bukkit.providers;

import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.bukkit.tasks.protocol1_12to1_11_1.BukkitInventoryUpdateTask;
import us.myles.ViaVersion.bukkit.util.NMSUtil;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage.ItemTransaction;
import us.myles.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;

public class BukkitInventoryQuickMoveProvider extends InventoryQuickMoveProvider {

private static Map<UUID, BukkitInventoryUpdateTask> updateTasks = new ConcurrentHashMap<UUID, BukkitInventoryUpdateTask>();
private 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 registerQuickMove(short windowId, short slotId, short actionId, UserConnection userConnection) {
if (!supported) {
return false;
}
ProtocolInfo info = userConnection.get(ProtocolInfo.class);
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().runSync(updateTask, 5L);
}
return true;
}

public Object buildWindowClickPacket(Player p, ItemTransaction storage) {
if (!supported) {
return null;
}
InventoryView inv = p.getOpenInventory();
short slotId = storage.getSlotId();
if (slotId < 0) { // clicked out of inv slot
return null;
}
if (slotId > inv.countSlots()) {
return null; // wrong container open?
}
ItemStack itemstack = inv.getItem(slotId);
if (itemstack == null) {
return null;
}
Object packet = null;
try {
packet = windowClickPacketClass.newInstance();
Object nmsItem = 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 = ProtocolRegistry.SERVER_PROTOCOL;
if (protocolId == ProtocolVersion.v1_8.getId()) {
ReflectionUtil.set(packet, "shift", 1);
} else if (protocolId >= ProtocolVersion.v1_9.getId()) { // 1.9+
ReflectionUtil.set(packet, "shift", clickTypeEnum);
}
} catch (Exception e) {
e.printStackTrace();
}
return packet;
}

public boolean sendPlayer(Player p, Object packet) {
if (packet == null) {
return false;
}
try {
Object entityPlayer = craftPlayerHandle.invoke(p);
Object playerConnection = connection.get(entityPlayer);
// send
packetMethod.invoke(playerConnection, packet);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
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 = ProtocolRegistry.SERVER_PROTOCOL;
if (protocolId >= ProtocolVersion.v1_9.getId()) {
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 = ProtocolRegistry.SERVER_PROTOCOL;
if (protocolId >= ProtocolVersion.v1_8.getId() && protocolId <= ProtocolVersion.v1_11_1.getId()) {
return true; // 1.8-1.11.2
}
// this is not needed on 1.12+ servers
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package us.myles.ViaVersion.bukkit.tasks.protocol1_12to1_11_1;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import us.myles.ViaVersion.bukkit.providers.BukkitInventoryQuickMoveProvider;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage.ItemTransaction;

public class BukkitInventoryUpdateTask implements Runnable {

private BukkitInventoryQuickMoveProvider provider;
private final UUID uuid;
private final List<ItemTransaction> items;

public BukkitInventoryUpdateTask(BukkitInventoryQuickMoveProvider provider, UUID uuid) {
this.provider = provider;
this.uuid = uuid;
this.items = Collections.synchronizedList(new ArrayList<ItemTransaction>());
}

public void addItem(short windowId, short slotId, short actionId) {
ItemTransaction storage = new ItemTransaction(windowId, slotId, actionId);
items.add(storage);
}

@Override
public void run() {
Player p = Bukkit.getServer().getPlayer(uuid);
if (p == null) {
provider.onTaskExecuted(uuid);
return;
}
try {
synchronized (items) {
for (ItemTransaction storage : items) {
Object packet = provider.buildWindowClickPacket(p, storage);
boolean result = provider.sendPlayer(p, packet);
if (!result) {
break;
}
}
items.clear();
}
} finally {
provider.onTaskExecuted(uuid);
}
}
}
5 changes: 5 additions & 0 deletions bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public TaskId runSync(Runnable runnable) {
return runAsync(runnable);
}

@Override
public TaskId runSync(Runnable runnable, Long ticks) {
return new BungeeTaskId(getProxy().getScheduler().schedule(this, runnable, ticks * 50, TimeUnit.MILLISECONDS).getId());
}

@Override
public TaskId runRepeatingSync(Runnable runnable, Long ticks) {
return new BungeeTaskId(getProxy().getScheduler().schedule(this, runnable, 0, ticks * 50, TimeUnit.MILLISECONDS).getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import java.util.*;

public class BungeeConfigAPI extends Config implements ViaVersionConfig {
private static List<String> UNSUPPORTED = Arrays.asList("nms-player-ticking", "item-cache", "anti-xray-patch");
private static List<String> UNSUPPORTED = Arrays.asList("nms-player-ticking", "item-cache", "anti-xray-patch", "quick-move-action-fix");

public BungeeConfigAPI(File configFile) {
super(new File(configFile, "config.yml"));
Expand Down Expand Up @@ -203,6 +203,11 @@ public boolean isForceJsonTransform() {
public boolean is1_12NBTArrayFix() {
return getBoolean("chat-nbt-fix", true);
}

@Override
public boolean is1_12QuickMoveActionFix() {
return false;
}

@Override
public List<Integer> getBlockedProtocols() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,11 @@ public <T> T read(Type<T> type) throws Exception {
}
} else {
Pair<Type, Object> read = readableObjects.poll();
if (read.getKey().equals(type) || (type.getBaseClass().equals(read.getKey().getBaseClass()) && type.getOutputClass().equals(read.getKey().getOutputClass()))) {
Type rtype = read.getKey();
if (rtype.equals(type) || (type.getBaseClass().equals(rtype.getBaseClass()) && type.getOutputClass().equals(rtype.getOutputClass()))) {
return (T) read.getValue();
} else {
if (type == Type.NOTHING) {
if (rtype == Type.NOTHING) {
return read(type); // retry
} else {
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ public interface ViaVersionConfig {
* @return True if enabled
*/
boolean is1_12NBTArrayFix();


/**
* Should we fix shift quick move action for 1.12 clients
*
* @return True if enabled
*/
boolean is1_12QuickMoveActionFix();

/**
* Get the blocked protocols
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ public interface ViaPlatform<T> {
*/
TaskId runSync(Runnable runnable);

/**
* Run a task Sync after a interval
* This must be only used after plugin enable.
*
* @param runnable The task to run
* @param ticks The interval to run it after
* @return The Task ID
*/
TaskId runSync(Runnable runnable, Long ticks);

/**
* Run a task at a repeating interval.
* Initial interval is the same as repeat.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
import us.myles.ViaVersion.api.entities.Entity1_12Types;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
import us.myles.ViaVersion.api.platform.providers.ViaProviders;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.version.Types1_12;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.packets.InventoryPackets;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage.EntityTracker;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.types.Chunk1_9_3_4Type;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
Expand Down Expand Up @@ -384,6 +386,11 @@ private int getNewSoundId(int id) { //TODO Make it better, suggestions are welco
newId += 3;
return newId;
}

@Override
protected void register(ViaProviders providers) {
providers.register(InventoryQuickMoveProvider.class, new InventoryQuickMoveProvider());
}

@Override
public void init(UserConnection userConnection) {
Expand Down
Loading

0 comments on commit 875e5d7

Please sign in to comment.