Fix commandblocks not able to change command on every-tick commandblock update

This commit is contained in:
Mats 2016-03-13 11:06:18 +01:00
parent 8d2189cb8e
commit 62d210de68
3 changed files with 55 additions and 22 deletions

View File

@ -1,6 +1,7 @@
package us.myles.ViaVersion.listeners; package us.myles.ViaVersion.listeners;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@ -12,12 +13,15 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.spacehq.opennbt.tag.builtin.ByteTag;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.ViaVersionPlugin; import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.packets.PacketType; import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.util.PacketUtil; import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil; import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.InvocationTargetException; import java.io.DataOutput;
import java.io.DataOutputStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@RequiredArgsConstructor @RequiredArgsConstructor
@ -41,22 +45,58 @@ public class CommandBlockListener implements Listener {
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && plugin.isPorted(e.getPlayer()) && e.getPlayer().isOp()) { if (e.getAction() == Action.RIGHT_CLICK_BLOCK && plugin.isPorted(e.getPlayer()) && e.getPlayer().isOp()) {
try { try {
sendCommandBlockPacket(e.getClickedBlock(), e.getPlayer()); sendCommandBlockPacket(e.getClickedBlock(), e.getPlayer());
} catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e1) { } catch (Exception ex) {
e1.printStackTrace(); ex.printStackTrace();
} }
} }
} }
private void sendCommandBlockPacket(Block b, Player player) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException { private void sendCommandBlockPacket(Block b, Player player) throws Exception {
if (!(b.getState() instanceof CommandBlock)) if (!(b.getState() instanceof CommandBlock))
return; return;
CommandBlock cmd = (CommandBlock) b.getState(); CommandBlock cmd = (CommandBlock) b.getState();
Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand")); Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand"));
Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket"); Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket");
Object nmsPlayer = ReflectionUtil.invoke(player, "getHandle"); ByteBuf buf = packetToByteBuf(updatePacket);
Object playerConnection = ReflectionUtil.get(nmsPlayer, "playerConnection", ReflectionUtil.nms("PlayerConnection")); plugin.sendRawPacket(player, buf);
Method sendPacket = playerConnection.getClass().getMethod("sendPacket", ReflectionUtil.nms("Packet")); }
sendPacket.invoke(playerConnection, updatePacket); //Let the transformer do the work
private ByteBuf packetToByteBuf(Object updatePacket) throws Exception {
ByteBuf buf = Unpooled.buffer();
PacketUtil.writeVarInt(PacketType.PLAY_UPDATE_BLOCK_ENTITY.getNewPacketID(), buf); //Packet ID
long[] pos = getPosition(ReflectionUtil.get(updatePacket, "a", ReflectionUtil.nms("BlockPosition")));
PacketUtil.writeBlockPosition(buf, pos[0], pos[1], pos[2]); //Block position
buf.writeByte(2); //Action id always 2
CompoundTag nbt = getNBT(ReflectionUtil.get(updatePacket, "c", ReflectionUtil.nms("NBTTagCompound")));
if (nbt == null) {
buf.writeByte(0); //If nbt is null. Use 0 as nbt
return buf;
}
nbt.put(new ByteTag("powered", (byte) 0));
nbt.put(new ByteTag("auto", (byte) 0));
nbt.put(new ByteTag("conditionMet", (byte) 0));
PacketUtil.writeNBT(buf, nbt); //NBT tag
return buf;
}
private long[] getPosition(Object obj) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
return new long[]{
(long) ReflectionUtil.getSuper(obj, "a", int.class), //X
(long) ReflectionUtil.getSuper(obj, "c", int.class), //Y
(long) ReflectionUtil.getSuper(obj, "d", int.class) //Z
};
}
private CompoundTag getNBT(Object obj) throws Exception {
ByteBuf buf = Unpooled.buffer();
Method m = ReflectionUtil.nms("NBTCompressedStreamTools").getMethod("a", ReflectionUtil.nms("NBTTagCompound"), DataOutput.class);
m.invoke(null, obj, new DataOutputStream(new ByteBufOutputStream(buf)));
try {
return PacketUtil.readNBT(buf);
} finally {
buf.release();
}
} }
} }

View File

@ -759,20 +759,7 @@ public class OutgoingTransformer {
return; return;
} }
if (action == 2) { //Update commandblock if (action == 2) { //Update commandblock
try { throw new CancelException(); //Only update if player interact with commandblock (The commandblock window will update every time this packet is sent, this would prevent you from change things that update every tick)
CompoundTag nbt = readNBT(input);
if (nbt == null)
throw new CancelException();
//Thanks http://www.minecraftforum.net/forums/minecraft-discussion/redstone-discussion-and/command-blocks/2488148-1-9-nbt-changes-and-additions#TileAllCommandBlocks
nbt.put(new ByteTag("powered", (byte) 0));
nbt.put(new ByteTag("auto", (byte) 0));
nbt.put(new ByteTag("conditionMet", (byte) 0));
writeNBT(output, nbt);
return;
} catch (IOException e) {
e.printStackTrace();
throw new CancelException();
}
} }
output.writeBytes(input, input.readableBytes()); output.writeBytes(input, input.readableBytes());
return; return;

View File

@ -37,6 +37,12 @@ public class ReflectionUtil {
return (T) field.get(null); return (T) field.get(null);
} }
public static <T> T getSuper(Object o, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
Field field = o.getClass().getSuperclass().getDeclaredField(f);
field.setAccessible(true);
return (T) field.get(o);
}
public static <T> T get(Object instance, Class<?> clazz, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException { public static <T> T get(Object instance, Class<?> clazz, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
Field field = clazz.getDeclaredField(f); Field field = clazz.getDeclaredField(f);
field.setAccessible(true); field.setAccessible(true);