Compare commits

...
26 Commits
Author SHA1 Message Date
Myles b1b35fe025 Bump version 2016-03-18 21:59:13 +00:00
Myles 11f3844d63 Use GSON instead, should fix memory leak issue with JSON. Also make sure that we use a copy of players for the bossbar. 2016-03-18 18:14:58 +00:00
Mats d40c5851e9 Merge pull request #239 from Matsv/master
Add api method to get player's protocol id
2016-03-17 12:38:51 +01:00
Mats d0e573613b Add api method to get player's protocol id, 2016-03-16 21:01:04 +01:00
Myles 0d3ba67233 Remove compress / decompress, rethrow errors if they occur from another handler, Fixes #233 2016-03-16 13:42:17 +00:00
Myles 9d57521b29 Bump Version 2016-03-15 21:57:38 +00:00
Myles d98d4ff466 Merge pull request #236 from Matsv/meta11
Discountinue player metadata 11.
2016-03-15 21:06:37 +00:00
Myles 71e27b4799 Merge pull request #232 from Matsv/dropcreative
Throw items when you try to put things in your second hand in creative.
2016-03-15 21:06:04 +00:00
Myles f1354edf20 Merge pull request #231 from Matsv/effect
Make it possible to use the new effect indicators in 1.9
2016-03-15 21:05:55 +00:00
Mats 4d82112286 Discountinue metadata 11. 2016-03-15 21:42:40 +01:00
Mats ba5a44d2d2 Remove debug message 2016-03-15 20:40:26 +01:00
Mats 597ddc7f82 Throw items when you try to put things in your second hand in creative. 2016-03-15 20:37:45 +01:00
Mats 6a7bd209a3 Make it possible to use the new effect indicators in 1.9 2016-03-15 20:20:39 +01:00
Myles 722f28f086 Merge pull request #228 from Matsv/slp
Make compatible with ServerListPlus custom outdated message. Fix #227
2016-03-15 17:51:56 +00:00
Mats daed15d61f Make compatible with ServerListPlus custom outdated message. Fix #227 2016-03-15 14:14:10 +01:00
Myles 7cfba0b602 Merge pull request #210 from Matsv/fixarmortjes
Fix #209 woops my fault
2016-03-13 17:02:07 +00:00
Mats 78ab7c840a Fix #209 woops my fault 2016-03-13 16:48:07 +01:00
Myles 669ec3f453 Merge pull request #208 from Matsv/drop
Drop SpaceHQ mcprotocol
2016-03-13 15:17:20 +00:00
Mats dd32b46055 Remove unused imports 2016-03-13 16:13:45 +01:00
Mats 7daa43146e Drop spacehq MC protocol since it's no longer used 2016-03-13 16:12:31 +01:00
Myles 89d5f2cc4f Merge pull request #207 from Matsv/armorattfix
Fix armor not showing on Respawn (With keepinventory true) & world change
2016-03-13 14:30:56 +00:00
Mats 53fb753284 Fix armor not showing on Respawn (With keepinventory true) & world change 2016-03-13 15:22:23 +01:00
Myles 469d81df72 Merge pull request #206 from Matsv/cmdfix
Fix unable to open commandblock after respawn/world change
2016-03-13 13:18:22 +00:00
Mats 7d7f5a0bda Fix unable to open commandblock after respawn/world change 2016-03-13 14:02:10 +01:00
Myles 9954195a8f Merge pull request #205 from Matsv/master
Fix commandblocks not able to change command on update
2016-03-13 12:26:40 +00:00
Mats 62d210de68 Fix commandblocks not able to change command on every-tick commandblock update 2016-03-13 11:06:18 +01:00
21 changed files with 188 additions and 604 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
# ViaVersion 0.6.4
# ViaVersion 0.6.6
[![Build Status](https://travis-ci.org/MylesIsCool/ViaVersion.svg?branch=master)](https://travis-ci.org/MylesIsCool/ViaVersion)
**Allows the connection of 1.9 clients to 1.8**
+15 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.6.5-SNAPSHOT</version>
<version>0.6.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ViaVersion</name>
@@ -101,6 +101,10 @@
<pattern>org.spacehq.opennbt</pattern>
<shadedPattern>us.myles.viaversion.libs.opennbt</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>us.myles.viaversion.libs.gson</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
@@ -162,6 +166,15 @@
<optional>true</optional>
</dependency>
<!-- GSON (JSON Library) -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- Netty (Network Library) -->
<dependency>
<groupId>io.netty</groupId>
@@ -170,6 +183,7 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
@@ -1,126 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.util.PacketUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BlockStorage {
private int bitsPerEntry;
private List<Integer> states;
private FlexibleStorage storage;
public BlockStorage() {
this.bitsPerEntry = 4;
this.states = new ArrayList<>();
this.states.add(0);
this.storage = new FlexibleStorage(this.bitsPerEntry, 4096);
}
public BlockStorage(ByteBuf in) throws IOException {
this.bitsPerEntry = in.readUnsignedByte();
this.states = new ArrayList<>();
int stateCount = PacketUtil.readVarInt(in);
for (int i = 0; i < stateCount; i++) {
this.states.add(PacketUtil.readVarInt(in));
}
this.storage = new FlexibleStorage(this.bitsPerEntry, PacketUtil.readLongs(PacketUtil.readVarInt(in), in));
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
public void write(ByteBuf out) throws IOException {
out.writeByte(this.bitsPerEntry);
PacketUtil.writeVarInt(this.states.size(), out);
for (int state : this.states) {
PacketUtil.writeVarInt(state, out);
}
long[] data = this.storage.getData();
PacketUtil.writeVarInt(data.length, out);
PacketUtil.writeLongs(data, out);
}
public int getBitsPerEntry() {
return this.bitsPerEntry;
}
public List<Integer> getStates() {
return Collections.unmodifiableList(this.states);
}
public FlexibleStorage getStorage() {
return this.storage;
}
public int get(int x, int y, int z) {
int id = this.storage.get(index(x, y, z));
return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.states.size() ? this.states.get(id) : 0) : id;
}
public void set(int x, int y, int z, int state) {
set(index(x, y, z), state);
}
public void set(int ind, int state) {
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
if (id == -1) {
this.states.add(state);
if (this.states.size() > 1 << this.bitsPerEntry) {
this.bitsPerEntry++;
List<Integer> oldStates = this.states;
if (this.bitsPerEntry > 8) {
oldStates = new ArrayList<>(this.states);
this.states.clear();
this.bitsPerEntry = 13;
}
FlexibleStorage oldStorage = this.storage;
this.storage = new FlexibleStorage(this.bitsPerEntry, this.storage.getSize());
for (int index = 0; index < this.storage.getSize(); index++) {
int value = oldStorage.get(index);
this.storage.set(index, this.bitsPerEntry <= 8 ? value : oldStates.get(value));
}
}
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
}
this.storage.set(ind, id);
}
public boolean isEmpty() {
for (int index = 0; index < this.storage.getSize(); index++) {
if (this.storage.get(index) != 0) {
return false;
}
}
return true;
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof BlockStorage && this.bitsPerEntry == ((BlockStorage) o).bitsPerEntry && this.states.equals(((BlockStorage) o).states) && this.storage.equals(((BlockStorage) o).storage));
}
@Override
public int hashCode() {
int result = this.bitsPerEntry;
result = 31 * result + this.states.hashCode();
result = 31 * result + this.storage.hashCode();
return result;
}
}
@@ -1,46 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
public class Chunk {
private BlockStorage blocks;
private NibbleArray3d blocklight;
private NibbleArray3d skylight;
public Chunk(boolean skylight) {
this(new BlockStorage(), new NibbleArray3d(2048), skylight ? new NibbleArray3d(2048) : null);
}
public Chunk(BlockStorage blocks, NibbleArray3d blocklight, NibbleArray3d skylight) {
this.blocks = blocks;
this.blocklight = blocklight;
this.skylight = skylight;
}
public BlockStorage getBlocks() {
return this.blocks;
}
public NibbleArray3d getBlockLight() {
return this.blocklight;
}
public NibbleArray3d getSkyLight() {
return this.skylight;
}
public boolean isEmpty() {
return this.blocks.isEmpty();
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof Chunk && this.blocks.equals(((Chunk) o).blocks) && this.blocklight.equals(((Chunk) o).blocklight) && ((this.skylight == null && (((Chunk) o).skylight == null)) || (this.skylight != null && this.skylight.equals(((Chunk) o).skylight))));
}
@Override
public int hashCode() {
int result = this.blocks.hashCode();
result = 31 * result + this.blocklight.hashCode();
result = 31 * result + (this.skylight != null ? this.skylight.hashCode() : 0);
return result;
}
}
@@ -1,59 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Column {
private int x;
private int z;
private Chunk chunks[];
private byte biomeData[];
private boolean skylight;
public Column(int x, int z, Chunk chunks[]) {
this(x, z, chunks, null);
}
public Column(int x, int z, Chunk chunks[], byte biomeData[]) {
if(chunks.length != 16) {
throw new IllegalArgumentException("Chunk array length must be 16.");
}
if(biomeData != null && biomeData.length != 256) {
throw new IllegalArgumentException("Biome data array length must be 256.");
}
this.skylight = false;
boolean noSkylight = false;
for (Chunk chunk : chunks) {
if (chunk != null) {
if (chunk.getSkyLight() == null) {
noSkylight = true;
} else {
this.skylight = true;
}
}
}
if(noSkylight && this.skylight) {
throw new IllegalArgumentException("Either all chunks must have skylight values or none must have them.");
}
this.x = x;
this.z = z;
this.chunks = chunks;
this.biomeData = biomeData;
}
public boolean hasBiomeData() {
return this.biomeData != null;
}
public boolean hasSkylight() {
return this.skylight;
}
}
@@ -1,104 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import java.util.Arrays;
public class FlexibleStorage {
private final long[] data;
private final int bitsPerEntry;
private final int size;
private final long maxEntryValue;
public FlexibleStorage(int bitsPerEntry, int size) {
this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]);
}
public FlexibleStorage(int bitsPerEntry, long[] data) {
if(bitsPerEntry < 1 || bitsPerEntry > 32) {
throw new IllegalArgumentException("BitsPerEntry cannot be outside of accepted range.");
}
this.bitsPerEntry = bitsPerEntry;
this.data = data;
this.size = this.data.length * 64 / this.bitsPerEntry;
this.maxEntryValue = (1L << this.bitsPerEntry) - 1;
}
public long[] getData() {
return this.data;
}
public int getBitsPerEntry() {
return this.bitsPerEntry;
}
public int getSize() {
return this.size;
}
public int get(int index) {
if(index < 0 || index > this.size - 1) {
throw new IndexOutOfBoundsException();
}
int bitIndex = index * this.bitsPerEntry;
int startIndex = bitIndex / 64;
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
int startBitSubIndex = bitIndex % 64;
if(startIndex == endIndex) {
return (int) (this.data[startIndex] >>> startBitSubIndex & this.maxEntryValue);
} else {
int endBitSubIndex = 64 - startBitSubIndex;
return (int) ((this.data[startIndex] >>> startBitSubIndex | this.data[endIndex] << endBitSubIndex) & this.maxEntryValue);
}
}
public void set(int index, int value) {
if(index < 0 || index > this.size - 1) {
throw new IndexOutOfBoundsException();
}
if(value < 0 || value > this.maxEntryValue) {
throw new IllegalArgumentException("Value cannot be outside of accepted range.");
}
int bitIndex = index * this.bitsPerEntry;
int startIndex = bitIndex / 64;
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
int startBitSubIndex = bitIndex % 64;
this.data[startIndex] = this.data[startIndex] & ~(this.maxEntryValue << startBitSubIndex) | ((long) value & this.maxEntryValue) << startBitSubIndex;
if(startIndex != endIndex) {
int endBitSubIndex = 64 - startBitSubIndex;
this.data[endIndex] = this.data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & this.maxEntryValue) >> endBitSubIndex;
}
}
private static int roundToNearest(int value, int roundTo) {
if(roundTo == 0) {
return 0;
} else if(value == 0) {
return roundTo;
} else {
if(value < 0) {
roundTo *= -1;
}
int remainder = value % roundTo;
return remainder != 0 ? value + roundTo - remainder : value;
}
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof FlexibleStorage && Arrays.equals(this.data, ((FlexibleStorage) o).data) && this.bitsPerEntry == ((FlexibleStorage) o).bitsPerEntry && this.size == ((FlexibleStorage) o).size && this.maxEntryValue == ((FlexibleStorage) o).maxEntryValue);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(this.data);
result = 31 * result + this.bitsPerEntry;
result = 31 * result + this.size;
result = 31 * result + (int) this.maxEntryValue;
return result;
}
}
@@ -1,71 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.util.Arrays;
public class NibbleArray3d {
private byte[] data;
public NibbleArray3d(int size) {
this.data = new byte[size];
}
public NibbleArray3d(byte[] array) {
this.data = array;
}
public NibbleArray3d(ByteBuf in, int size) throws IOException {
this.data = new byte[size];
in.readBytes(this.data);
}
public void write(ByteBuf out) throws IOException {
out.writeBytes(this.data);
}
public byte[] getData() {
return this.data;
}
public int get(int x, int y, int z) {
int key = y << 8 | z << 4 | x;
int index = key >> 1;
int part = key & 1;
return part == 0 ? this.data[index] & 15 : this.data[index] >> 4 & 15;
}
public void set(int x, int y, int z, int val) {
int key = y << 8 | z << 4 | x;
int index = key >> 1;
int part = key & 1;
if(part == 0) {
this.data[index] = (byte) (this.data[index] & 240 | val & 15);
} else {
this.data[index] = (byte) (this.data[index] & 15 | (val & 15) << 4);
}
}
public void fill(int val) {
for(int index = 0; index < this.data.length << 1; index++) {
int ind = index >> 1;
int part = index & 1;
if(part == 0) {
this.data[ind] = (byte) (this.data[ind] & 240 | val & 15);
} else {
this.data[ind] = (byte) (this.data[ind] & 15 | (val & 15) << 4);
}
}
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof NibbleArray3d && Arrays.equals(this.data, ((NibbleArray3d) o).data));
}
@Override
public int hashCode() {
return Arrays.hashCode(this.data);
}
}
@@ -1,117 +0,0 @@
package org.spacehq.mc.protocol.util;
import io.netty.buffer.ByteBuf;
import org.spacehq.mc.protocol.data.game.chunk.Chunk;
import org.spacehq.mc.protocol.data.game.chunk.Column;
import org.spacehq.mc.protocol.data.game.chunk.NibbleArray3d;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
/* From https://github.com/Steveice10/MCProtocolLib/ */
/* No credit taken for writing this code, and used accordance to it's license
Original by Steveice10, modified to suit this plugin.
*/
public class NetUtil {
public static int writeNewColumn(ByteBuf out, Column column, boolean fullChunk, boolean hasSkylight) throws IOException {
int mask = 0;
Chunk chunks[] = column.getChunks();
for (int index = 0; index < chunks.length; index++) {
Chunk chunk = chunks[index];
if (chunk != null && (!fullChunk || !chunk.isEmpty())) {
mask |= 1 << index;
chunk.getBlocks().write(out);
chunk.getBlockLight().write(out);
if (hasSkylight || column.hasSkylight()) {
chunk.getSkyLight().write(out); // TODO: Make a PR to original lib to correct this
}
}
}
if (fullChunk && column.getBiomeData() != null) {
out.writeBytes(column.getBiomeData());
}
return mask;
}
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
int pos = 0;
int expected = isFullChunk ? 256 : 0;
boolean sky = false;
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// 0 = Calculate expected length and determine if the packet has skylight.
// 1 = Create chunks from mask and get blocks.
// 2 = Get block light.
// 3 = Get sky light.
Chunk[] chunks = new Chunk[16];
int chunkCount = 0;
for (int pass = 0; pass < 4; pass++) {
if(pass == 1){
if(chunkCount == 0) return null;
}
for (int ind = 0; ind < 16; ind++) {
if ((bitmask & 1 << ind) != 0) {
if (pass == 0) {
chunkCount++;
// Block length + Blocklight length
expected += (4096 * 2) + 2048;
}
if (pass == 1) {
chunks[ind] = new Chunk(sky || hasSkyLight);
buf.position(pos / 2);
int buffPos = buf.position();
// convert short array to new one
for (int index = 0; index < 4096; index++) {
short ss = buf.get(buffPos + index);
// s is 16 bits, 12 bits id and 4 bits data
int data = ss & 0xF;
int id = (ss >> 4) << 4 | data;
int newCombined = id; // test
chunks[ind].getBlocks().set(index, newCombined);
}
pos += 4096 * 2;
}
if (pass == 2) {
NibbleArray3d blocklight = chunks[ind].getBlockLight();
System.arraycopy(input, pos, blocklight.getData(), 0, blocklight.getData().length);
pos += blocklight.getData().length;
}
if (pass == 3) {
if (sky) {
NibbleArray3d skylight = chunks[ind].getSkyLight();
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
pos += skylight.getData().length;
}
}
}
}
if (pass == 0) {
// If we have more data than blocks and blocklight combined, there must be skylight data as well.
if (input.length > expected) {
sky = checkForSky;
}
}
}
byte biomeData[] = null;
if (isFullChunk && (pos + 256 <= input.length)) {
biomeData = new byte[256];
System.arraycopy(input, pos, biomeData, 0, biomeData.length);
}
Column column = new Column(x, z, chunks, biomeData);
return column;
}
}
@@ -5,6 +5,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -195,6 +196,13 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return isPorted(player.getUniqueId());
}
@Override
public int getPlayerVersion(@NonNull Player player) {
if (!isPorted(player))
return 47;
return portedPlayers.get(player.getUniqueId()).getProtocol();
}
@Override
public boolean isPorted(UUID playerUUID) {
return portedPlayers.containsKey(playerUUID);
@@ -239,6 +247,10 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return getConfig().getBoolean("prevent-collision", true);
}
public boolean isNewEffectIndicator(){
return getConfig().getBoolean("use-new-effect-indicator",true);
}
public boolean isSuppressMetadataErrors() {
return getConfig().getBoolean("suppress-metadata-errors", false);
}
@@ -10,13 +10,20 @@ import java.util.UUID;
public interface ViaVersionAPI {
/**
* Is player using 1.9?
* Is the player connection modified by ViaVersion?
*
* @param player
* @return True if the client is on 1.9
* @param player Bukkit player object
* @return True if the client is modified (At the moment it also means version 1.9 and higher)
*/
boolean isPorted(Player player);
/**
* Get protocol number from a player
* @param player Bukkit player object
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
*/
int getPlayerVersion(Player player);
/**
* Is player using 1.9?
*
@@ -11,8 +11,10 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.CraftingInventory;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
@@ -89,6 +91,18 @@ public class ArmorListener implements Listener {
sendDelayedArmorUpdate(e.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onRespawn(PlayerRespawnEvent e) {
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
sendDelayedArmorUpdate(e.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldChange(PlayerChangedWorldEvent e) {
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
sendArmorUpdate(e.getPlayer());
}
public void sendDelayedArmorUpdate(final Player player) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
@@ -15,10 +15,7 @@ import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.*;
@Getter
public class ViaBossBar implements BossBar {
@@ -142,7 +139,7 @@ public class ViaBossBar implements BossBar {
private void sendPacket(UpdateAction action) {
ByteBuf buf = getPacket(action);
for (UUID uuid : players)
for (UUID uuid : new ArrayList<>(players))
sendPacket(uuid, buf);
}
@@ -8,6 +8,7 @@ import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.transformers.IncomingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class ViaDecodeHandler extends ByteToMessageDecoder {
@@ -40,7 +41,13 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
}
}
// call minecraft decoder
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
try {
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
}
}
}
@@ -7,9 +7,7 @@ import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ViaEncodeHandler extends MessageToByteEncoder {
@@ -29,7 +27,13 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
// handle the packet type
if (!(o instanceof ByteBuf)) {
// call minecraft encoder
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
try {
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
}
}
if (bytebuf.readableBytes() == 0) {
throw new CancelException();
@@ -1,8 +1,10 @@
package us.myles.ViaVersion.listeners;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.CommandBlock;
import org.bukkit.entity.Player;
@@ -10,14 +12,19 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.spacehq.opennbt.tag.builtin.ByteTag;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.InvocationTargetException;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.lang.reflect.Method;
@RequiredArgsConstructor
@@ -26,14 +33,23 @@ public class CommandBlockListener implements Listener {
private final ViaVersionPlugin plugin;
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onJoin(final PlayerJoinEvent e) {
if (e.getPlayer().isOp() && plugin.isPorted(e.getPlayer())) {
ByteBuf buf = Unpooled.buffer();
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_STATUS.getNewPacketID(), buf);
buf.writeInt(e.getPlayer().getEntityId());
buf.writeByte(26);
plugin.sendRawPacket(e.getPlayer(), buf);
}
public void onJoin(PlayerJoinEvent e) {
sendOp(e.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onRespawn(final PlayerRespawnEvent e) {
Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
@Override
public void run() {
sendOp(e.getPlayer());
}
}, 1L);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onWorldChange(PlayerChangedWorldEvent e) {
sendOp(e.getPlayer());
}
@EventHandler(ignoreCancelled = true)
@@ -41,22 +57,68 @@ public class CommandBlockListener implements Listener {
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && plugin.isPorted(e.getPlayer()) && e.getPlayer().isOp()) {
try {
sendCommandBlockPacket(e.getClickedBlock(), e.getPlayer());
} catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e1) {
e1.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
private void sendCommandBlockPacket(Block b, Player player) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
private void sendOp(Player p) {
if (p.isOp() && plugin.isPorted(p)) {
ByteBuf buf = Unpooled.buffer();
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_STATUS.getNewPacketID(), buf);
buf.writeInt(p.getEntityId());
buf.writeByte(26);
plugin.sendRawPacket(p, buf);
}
}
private void sendCommandBlockPacket(Block b, Player player) throws Exception {
if (!(b.getState() instanceof CommandBlock))
return;
CommandBlock cmd = (CommandBlock) b.getState();
Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand"));
Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket");
Object nmsPlayer = ReflectionUtil.invoke(player, "getHandle");
Object playerConnection = ReflectionUtil.get(nmsPlayer, "playerConnection", ReflectionUtil.nms("PlayerConnection"));
Method sendPacket = playerConnection.getClass().getMethod("sendPacket", ReflectionUtil.nms("Packet"));
sendPacket.invoke(playerConnection, updatePacket); //Let the transformer do the work
ByteBuf buf = packetToByteBuf(updatePacket);
plugin.sendRawPacket(player, buf);
}
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();
}
}
}
@@ -34,6 +34,7 @@ public enum MetaIndex {
PLAYER_ADDITIONAL_HEARTS(HumanEntity.class, 17, Type.Float, 10, NewType.Float),
PLAYER_SCORE(HumanEntity.class, 18, Type.Int, 11, NewType.VarInt),
PLAYER_HAND(HumanEntity.class, -1, Type.NonExistent, 5, NewType.Byte), // new in 1.9
SOMETHING_ANTICHEAT_PLUGINS_FOR_SOME_REASON_USE(HumanEntity.class, 11, Type.Byte, NewType.Discontinued), //For what we know, This doesn't exists. If you think it exists and knows what it does. Please tell us.
// horse
HORSE_INFO(Horse.class, 16, Type.Int, 12, NewType.Byte),
HORSE_TYPE(Horse.class, 19, Type.Byte, 13, NewType.VarInt),
@@ -329,8 +329,17 @@ public class IncomingTransformer {
}
if (packet == PacketType.PLAY_CREATIVE_INVENTORY_ACTION) {
short slot = input.readShort();
if (slot == 45) {
ByteBuf buf = info.getChannel().alloc().buffer();
PacketUtil.writeVarInt(PacketType.PLAY_SET_SLOT.getNewPacketID(), buf);
buf.writeByte(0);
buf.writeShort(slot);
buf.writeShort(-1); // empty
info.sendRawPacket(buf);
// Continue the packet simulating throw
slot = -999;
}
output.writeShort(slot);
ItemSlotRewriter.rewrite1_9To1_8(input, output);
}
output.writeBytes(input);
@@ -1,14 +1,11 @@
package us.myles.ViaVersion.transformers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import io.netty.buffer.ByteBuf;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.spacehq.mc.protocol.data.game.chunk.Column;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.opennbt.tag.builtin.ByteTag;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import org.spacehq.opennbt.tag.builtin.StringTag;
import us.myles.ViaVersion.CancelException;
@@ -29,7 +26,6 @@ import us.myles.ViaVersion.slot.ItemSlotRewriter;
import us.myles.ViaVersion.sounds.SoundEffect;
import us.myles.ViaVersion.util.EntityUtil;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.io.IOException;
import java.util.*;
@@ -38,6 +34,7 @@ import static us.myles.ViaVersion.util.PacketUtil.*;
public class OutgoingTransformer {
private static Gson gson = new GsonBuilder().create();
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
@@ -60,16 +57,16 @@ public class OutgoingTransformer {
line = "{\"text\":\"\"}";
} else {
if ((!line.startsWith("\"") || !line.endsWith("\"")) && (!line.startsWith("{") || !line.endsWith("}"))) {
JSONObject obj = new JSONObject();
obj.put("text", line);
return obj.toJSONString();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("text", line);
return gson.toJson(jsonObject);
}
if (line.startsWith("\"") && line.endsWith("\"")) {
line = "{\"text\":" + line + "}";
}
}
try {
new JSONParser().parse(line);
gson.fromJson(line, JsonObject.class);
} catch (Exception e) {
System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github: " + e.getMessage());
return "{\"text\":\"\"}";
@@ -301,11 +298,12 @@ public class OutgoingTransformer {
if (packet == PacketType.STATUS_RESPONSE) {
String originalStatus = PacketUtil.readString(input);
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
version.put("protocol", info.getProtocol());
PacketUtil.writeString(json.toJSONString(), output);
} catch (ParseException e) {
JsonObject jsonObject = gson.fromJson(originalStatus, JsonObject.class);
JsonObject version = jsonObject.get("version").getAsJsonObject();
if (version.get("protocol").getAsInt() != 9999) //Fix ServerListPlus custom outdated message
version.addProperty("protocol", info.getProtocol());
PacketUtil.writeString(gson.toJson(jsonObject), output);
} catch (Exception e) {
e.printStackTrace();
}
return;
@@ -682,7 +680,7 @@ public class OutgoingTransformer {
PacketUtil.writeVarInt(duration, output);
// we need to write as a byte instead of boolean
boolean hideParticles = input.readBoolean();
output.writeByte(hideParticles ? 1 : 0);
output.writeByte(hideParticles ? plugin.isNewEffectIndicator() ? 2 : 1 : 0);
return;
}
if (packet == PacketType.PLAY_TEAM) {
@@ -759,20 +757,7 @@ public class OutgoingTransformer {
return;
}
if (action == 2) { //Update commandblock
try {
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();
}
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)
}
output.writeBytes(input, input.readableBytes());
return;
@@ -781,7 +766,7 @@ public class OutgoingTransformer {
// Read chunk
ChunkManager chunkManager = info.getChunkManager();
Chunk chunk = chunkManager.readChunk(input);
if(chunk == null) {
if (chunk == null) {
throw new CancelException();
}
@@ -74,37 +74,24 @@ public class PacketUtil {
}
}
public static List<Object> callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) {
public static List<Object> callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) throws InvocationTargetException {
List<Object> output = new ArrayList<>();
try {
PacketUtil.DECODE_METHOD.invoke(decoder, ctx, input, output);
} catch (IllegalAccessException | InvocationTargetException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return output;
}
public static void callEncode(MessageToByteEncoder encoder, ChannelHandlerContext ctx, Object msg, ByteBuf output) {
public static void callEncode(MessageToByteEncoder encoder, ChannelHandlerContext ctx, Object msg, ByteBuf output) throws InvocationTargetException {
try {
PacketUtil.ENCODE_METHOD.invoke(encoder, ctx, msg, output);
} catch (IllegalAccessException | InvocationTargetException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) {
ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress");
List<Object> output = callDecode(x, ctx, msg);
return output.size() == 0 ? null : (ByteBuf) output.get(0);
}
public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) {
MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress");
ByteBuf output = ctx.alloc().buffer();
callEncode(x, ctx, msg, output);
return output;
}
/* I take no credit, these are taken from BungeeCord */
// https://github.com/SpigotMC/BungeeCord/blob/master/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java
public static void writeString(String s, ByteBuf buf) {
@@ -37,6 +37,12 @@ public class ReflectionUtil {
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 {
Field field = clazz.getDeclaredField(f);
field.setAccessible(true);
+3 -1
View File
@@ -19,4 +19,6 @@ simulate-pt: true
# Should we patch boss bars so they work? (Default: true, disable if you're having issues)
bossbar-patch: true
# If your boss bar flickers on 1.9, set this to 'true'. It will keep all boss bars on 100% (not recommended)
bossbar-anti-flicker: false
bossbar-anti-flicker: false
# This will show the new effect indicator in the top-right corner for 1.9 players.
use-new-effect-indicator: true