Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-11-20 06:50:08 +01:00
Commit
8764d24a16
14
README.md
14
README.md
@ -1,9 +1,9 @@
|
|||||||
# ViaVersion 0.3.3
|
# ViaVersion 0.4.5
|
||||||
**Allows the connection of 1.8 clients to 1.9**
|
**Allows the connection of 1.8 clients to 1.9**
|
||||||
|
|
||||||
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
|
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
|
||||||
|
|
||||||
**Don't use late bind nor ProtocolLib**
|
**Don't use late bind*
|
||||||
|
|
||||||
**As of this point it doesn't have everything, I need to fix:**
|
**As of this point it doesn't have everything, I need to fix:**
|
||||||
|
|
||||||
@ -13,9 +13,6 @@ Remap spawn eggs
|
|||||||
|
|
||||||
If you have a bug with entities, please report the full stack trace
|
If you have a bug with entities, please report the full stack trace
|
||||||
|
|
||||||
Some items with JSON data cause crashing due to the change in how strict minecraft is.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
||||||
|
|
||||||
@ -31,10 +28,11 @@ Contributors:
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
**Myself** (harhar)
|
**Myself** (harhar)
|
||||||
|
**Matsv/StamBoom**
|
||||||
**Matsv/StatBoom**
|
|
||||||
|
|
||||||
**HugoDaBosss**
|
**HugoDaBosss**
|
||||||
|
**SanderGielisse**
|
||||||
|
**Paulomart**
|
||||||
|
**gigosaurus**
|
||||||
|
|
||||||
License:
|
License:
|
||||||
--------
|
--------
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.spacehq.mc.protocol.data.game.chunk;
|
package org.spacehq.mc.protocol.data.game.chunk;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import us.myles.ViaVersion.PacketUtil;
|
import us.myles.ViaVersion.util.PacketUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -39,7 +39,7 @@ public class NetUtil {
|
|||||||
|
|
||||||
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
|
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int expected = 0;
|
int expected = isFullChunk ? 256 : 0;
|
||||||
boolean sky = false;
|
boolean sky = false;
|
||||||
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||||
// 0 = Calculate expected length and determine if the packet has skylight.
|
// 0 = Calculate expected length and determine if the packet has skylight.
|
||||||
@ -85,7 +85,7 @@ public class NetUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(pass == 3) {
|
if(pass == 3) {
|
||||||
if(chunks[ind].getSkyLight() != null) {
|
if(sky) {
|
||||||
NibbleArray3d skylight = chunks[ind].getSkyLight();
|
NibbleArray3d skylight = chunks[ind].getSkyLight();
|
||||||
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
|
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
|
||||||
pos += skylight.getData().length;
|
pos += skylight.getData().length;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package us.myles.ViaVersion;
|
package us.myles.ViaVersion;
|
||||||
|
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import us.myles.ViaVersion.packets.State;
|
import us.myles.ViaVersion.packets.State;
|
||||||
@ -7,11 +8,17 @@ import us.myles.ViaVersion.packets.State;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ConnectionInfo {
|
public class ConnectionInfo {
|
||||||
private int protocol = 0;
|
private final SocketChannel channel;
|
||||||
private State state = State.HANDSHAKE;
|
|
||||||
private int compression = 0;
|
|
||||||
private Object lastPacket;
|
private Object lastPacket;
|
||||||
private java.util.UUID UUID;
|
private java.util.UUID UUID;
|
||||||
|
private State state = State.HANDSHAKE;
|
||||||
|
private int protocol = 0;
|
||||||
|
private int compression = 0;
|
||||||
|
private boolean active = true;
|
||||||
|
|
||||||
|
public ConnectionInfo(SocketChannel socketChannel) {
|
||||||
|
this.channel = socketChannel;
|
||||||
|
}
|
||||||
|
|
||||||
public int getProtocol() {
|
public int getProtocol() {
|
||||||
return protocol;
|
return protocol;
|
||||||
@ -56,4 +63,16 @@ public class ConnectionInfo {
|
|||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
return UUID == null ? null : Bukkit.getPlayer(UUID);
|
return UUID == null ? null : Bukkit.getPlayer(UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SocketChannel getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,30 +6,52 @@ import io.netty.channel.ChannelInitializer;
|
|||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.api.ViaVersion;
|
||||||
|
import us.myles.ViaVersion.api.ViaVersionAPI;
|
||||||
|
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||||
|
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class Core extends JavaPlugin {
|
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||||
|
|
||||||
|
private final Set<UUID> portedPlayers = Collections.newSetFromMap(new ConcurrentHashMap<UUID, Boolean>());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
ViaVersion.setInstance(this);
|
||||||
|
if(System.getProperty("ViaVersion") != null){
|
||||||
|
getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogger().info("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||||
try {
|
try {
|
||||||
injectPacketHandler();
|
injectPacketHandler();
|
||||||
|
System.setProperty("ViaVersion", getDescription().getVersion());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if(Bukkit.getPluginManager().getPlugin("ProtocolLib") != null){
|
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
|
||||||
System.out.println("This plugin is not compatible with protocol lib.");
|
|
||||||
}
|
|
||||||
System.out.println("Unable to inject handlers, are you on 1.8? ");
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||||
|
setPorted(e.getPlayer().getUniqueId(), false);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectPacketHandler() throws Exception {
|
public void injectPacketHandler() throws Exception {
|
||||||
@ -39,7 +61,7 @@ public class Core extends JavaPlugin {
|
|||||||
|
|
||||||
List<ChannelFuture> futures = ReflectionUtil.get(connection, "g", List.class);
|
List<ChannelFuture> futures = ReflectionUtil.get(connection, "g", List.class);
|
||||||
if (futures.size() == 0) {
|
if (futures.size() == 0) {
|
||||||
throw new Exception("Could not find server to inject (late bind?)");
|
throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ChannelFuture future : futures) {
|
for (ChannelFuture future : futures) {
|
||||||
@ -51,32 +73,22 @@ public class Core extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPorted(Player player) {
|
||||||
|
return portedPlayers.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
public static Entity getEntity(final UUID player, final int id) {
|
public void setPorted(UUID id, boolean value) {
|
||||||
try {
|
if (value) {
|
||||||
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<Entity>() {
|
portedPlayers.add(id);
|
||||||
@Override
|
} else {
|
||||||
public Entity call() throws Exception {
|
portedPlayers.remove(id);
|
||||||
Player p = Bukkit.getPlayer(player);
|
|
||||||
if (p == null) return null;
|
|
||||||
for (Entity e : p.getWorld().getEntities()) {
|
|
||||||
if (e.getEntityId() == id) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}).get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("Error fetching entity ");
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||||
try {
|
try {
|
||||||
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<ItemStack>() {
|
return Bukkit.getScheduler().callSyncMethod(getPlugin(ViaVersionPlugin.class), new Callable<ItemStack>() {
|
||||||
@Override
|
@Override
|
||||||
public ItemStack call() throws Exception {
|
public ItemStack call() throws Exception {
|
||||||
if (info.getPlayer() != null) {
|
if (info.getPlayer() != null) {
|
17
src/main/java/us/myles/ViaVersion/api/ViaVersion.java
Normale Datei
17
src/main/java/us/myles/ViaVersion/api/ViaVersion.java
Normale Datei
@ -0,0 +1,17 @@
|
|||||||
|
package us.myles.ViaVersion.api;
|
||||||
|
|
||||||
|
public class ViaVersion {
|
||||||
|
|
||||||
|
private static ViaVersionAPI INSTANCE;
|
||||||
|
|
||||||
|
public static void setInstance(ViaVersionAPI api) {
|
||||||
|
if (INSTANCE != null) {
|
||||||
|
throw new IllegalStateException("Instance already set.");
|
||||||
|
}
|
||||||
|
INSTANCE = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ViaVersionAPI getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java
Normale Datei
8
src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java
Normale Datei
@ -0,0 +1,8 @@
|
|||||||
|
package us.myles.ViaVersion.api;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface ViaVersionAPI {
|
||||||
|
|
||||||
|
boolean isPorted(Player player);
|
||||||
|
}
|
62
src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java
Normale Datei
62
src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java
Normale Datei
@ -0,0 +1,62 @@
|
|||||||
|
package us.myles.ViaVersion.handlers;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
import us.myles.ViaVersion.CancelException;
|
||||||
|
import us.myles.ViaVersion.ConnectionInfo;
|
||||||
|
import us.myles.ViaVersion.transformers.IncomingTransformer;
|
||||||
|
import us.myles.ViaVersion.util.PacketUtil;
|
||||||
|
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ViaDecodeHandler extends ByteToMessageDecoder {
|
||||||
|
private final IncomingTransformer incomingTransformer;
|
||||||
|
private final ByteToMessageDecoder minecraftDecoder;
|
||||||
|
private final ConnectionInfo info;
|
||||||
|
|
||||||
|
public ViaDecodeHandler(ConnectionInfo info, ByteToMessageDecoder minecraftDecoder) {
|
||||||
|
this.info = info;
|
||||||
|
this.minecraftDecoder = minecraftDecoder;
|
||||||
|
this.incomingTransformer = new IncomingTransformer(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
|
||||||
|
// use transformers
|
||||||
|
if (bytebuf.readableBytes() > 0) {
|
||||||
|
if (info.isActive()) {
|
||||||
|
int id = PacketUtil.readVarInt(bytebuf);
|
||||||
|
// Transform
|
||||||
|
ByteBuf newPacket = ctx.alloc().buffer();
|
||||||
|
try {
|
||||||
|
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||||
|
bytebuf = newPacket;
|
||||||
|
} catch (CancelException e) {
|
||||||
|
bytebuf.readBytes(bytebuf.readableBytes());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// call minecraft decoder
|
||||||
|
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
|
if (!(cause.getCause().getCause() instanceof CancelException)
|
||||||
|
&& !(cause.getCause().getCause() instanceof ClosedChannelException)) {
|
||||||
|
if (!(cause.getCause() instanceof CancelException)
|
||||||
|
&& !(cause.getCause() instanceof ClosedChannelException)) {
|
||||||
|
if (!(cause instanceof CancelException)
|
||||||
|
&& !(cause instanceof ClosedChannelException)) {
|
||||||
|
if (cause instanceof Exception){
|
||||||
|
cause.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
87
src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java
Normale Datei
87
src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java
Normale Datei
@ -0,0 +1,87 @@
|
|||||||
|
package us.myles.ViaVersion.handlers;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
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.nio.channels.ClosedChannelException;
|
||||||
|
|
||||||
|
public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||||
|
private final ConnectionInfo info;
|
||||||
|
private final MessageToByteEncoder minecraftEncoder;
|
||||||
|
private final OutgoingTransformer outgoingTransformer;
|
||||||
|
|
||||||
|
public ViaEncodeHandler(ConnectionInfo info, MessageToByteEncoder minecraftEncoder) {
|
||||||
|
this.info = info;
|
||||||
|
this.minecraftEncoder = minecraftEncoder;
|
||||||
|
this.outgoingTransformer = new OutgoingTransformer(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Object o, ByteBuf bytebuf) throws Exception {
|
||||||
|
// handle the packet type
|
||||||
|
if (!(o instanceof ByteBuf)) {
|
||||||
|
info.setLastPacket(o);
|
||||||
|
/* This transformer is more for fixing issues which we find hard at packet level :) */
|
||||||
|
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk") && info.isActive()) {
|
||||||
|
int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
||||||
|
int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
||||||
|
|
||||||
|
Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
|
||||||
|
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
|
||||||
|
Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
|
||||||
|
for (int i = 0; i < locX.length; i++) {
|
||||||
|
int x = locX[i];
|
||||||
|
int z = locZ[i];
|
||||||
|
// world invoke function
|
||||||
|
Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z);
|
||||||
|
Object packet = constructor.newInstance(chunk, true, 65535);
|
||||||
|
ctx.pipeline().writeAndFlush(packet);
|
||||||
|
}
|
||||||
|
bytebuf.readBytes(bytebuf.readableBytes());
|
||||||
|
throw new CancelException();
|
||||||
|
}
|
||||||
|
// call minecraft encoder
|
||||||
|
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
|
||||||
|
}
|
||||||
|
if (bytebuf.readableBytes() == 0) {
|
||||||
|
throw new CancelException();
|
||||||
|
}
|
||||||
|
if (info.isActive()) {
|
||||||
|
int id = PacketUtil.readVarInt(bytebuf);
|
||||||
|
// Transform
|
||||||
|
ByteBuf oldPacket = bytebuf.copy();
|
||||||
|
bytebuf.clear();
|
||||||
|
try {
|
||||||
|
outgoingTransformer.transform(id, oldPacket, bytebuf);
|
||||||
|
} catch (CancelException e) {
|
||||||
|
bytebuf.readBytes(bytebuf.readableBytes());
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
oldPacket.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
|
if (!(cause.getCause().getCause() instanceof CancelException)
|
||||||
|
&& !(cause.getCause().getCause() instanceof ClosedChannelException)) {
|
||||||
|
if (!(cause.getCause() instanceof CancelException)
|
||||||
|
&& !(cause.getCause() instanceof ClosedChannelException)) {
|
||||||
|
if (!(cause instanceof CancelException)
|
||||||
|
&& !(cause instanceof ClosedChannelException)) {
|
||||||
|
if (cause instanceof Exception)
|
||||||
|
cause.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
package us.myles.ViaVersion.handlers;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
||||||
import us.myles.ViaVersion.CancelException;
|
|
||||||
import us.myles.ViaVersion.ConnectionInfo;
|
|
||||||
import us.myles.ViaVersion.PacketUtil;
|
|
||||||
import us.myles.ViaVersion.transformers.IncomingTransformer;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class ViaInboundHandler extends ChannelInboundHandlerAdapter {
|
|
||||||
private final IncomingTransformer incomingTransformer;
|
|
||||||
private final ViaVersionInitializer init;
|
|
||||||
|
|
||||||
public ViaInboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) {
|
|
||||||
this.init = init;
|
|
||||||
this.incomingTransformer = new IncomingTransformer(c, info, init);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
||||||
boolean compression = ctx.pipeline().get("compress") != null;
|
|
||||||
|
|
||||||
if (msg instanceof ByteBuf) {
|
|
||||||
ByteBuf bytebuf = (ByteBuf) msg;
|
|
||||||
if (compression) {
|
|
||||||
// decompress :)
|
|
||||||
bytebuf = PacketUtil.decompress(ctx, bytebuf);
|
|
||||||
}
|
|
||||||
int id = PacketUtil.readVarInt(bytebuf);
|
|
||||||
// Transform
|
|
||||||
ByteBuf newPacket = ctx.alloc().buffer();
|
|
||||||
try {
|
|
||||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
|
||||||
} catch (CancelException e) {
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
bytebuf.release();
|
|
||||||
}
|
|
||||||
if (compression) {
|
|
||||||
// recompress :)
|
|
||||||
newPacket = PacketUtil.compress(ctx, newPacket);
|
|
||||||
}
|
|
||||||
msg = newPacket;
|
|
||||||
}
|
|
||||||
super.channelRead(ctx, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package us.myles.ViaVersion.handlers;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.*;
|
|
||||||
import us.myles.ViaVersion.CancelException;
|
|
||||||
import us.myles.ViaVersion.ConnectionInfo;
|
|
||||||
import us.myles.ViaVersion.PacketUtil;
|
|
||||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter {
|
|
||||||
private final OutgoingTransformer outgoingTransformer;
|
|
||||||
private final ViaVersionInitializer init;
|
|
||||||
|
|
||||||
public ViaOutboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) {
|
|
||||||
this.init = init;
|
|
||||||
this.outgoingTransformer = new OutgoingTransformer(c, info, init);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise channelPromise) throws Exception {
|
|
||||||
try {
|
|
||||||
if (channelPromise.isDone()) return; // don't break any <3s
|
|
||||||
boolean compression = ctx.pipeline().get("compress") != null;
|
|
||||||
if (msg instanceof ByteBuf) {
|
|
||||||
ByteBuf bytebuf = (ByteBuf) msg;
|
|
||||||
if (compression) {
|
|
||||||
// decompress :)
|
|
||||||
bytebuf = PacketUtil.decompress(ctx, bytebuf);
|
|
||||||
}
|
|
||||||
int id = PacketUtil.readVarInt(bytebuf);
|
|
||||||
// Transform
|
|
||||||
ByteBuf newPacket = ctx.alloc().buffer();
|
|
||||||
try {
|
|
||||||
outgoingTransformer.transform(id, bytebuf, newPacket);
|
|
||||||
} catch (CancelException e) {
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
bytebuf.release();
|
|
||||||
}
|
|
||||||
if (compression) {
|
|
||||||
// recompress :)
|
|
||||||
newPacket = PacketUtil.compress(ctx, newPacket);
|
|
||||||
}
|
|
||||||
msg = newPacket;
|
|
||||||
}
|
|
||||||
super.write(ctx, msg, channelPromise);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package us.myles.ViaVersion.handlers;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.*;
|
|
||||||
import us.myles.ViaVersion.ConnectionInfo;
|
|
||||||
import us.myles.ViaVersion.ReflectionUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter {
|
|
||||||
private final ConnectionInfo info;
|
|
||||||
|
|
||||||
public ViaOutboundPacketHandler(Channel c, ConnectionInfo info) {
|
|
||||||
this.info = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(ChannelHandlerContext channelHandlerContext, Object o, ChannelPromise channelPromise) throws Exception {
|
|
||||||
if (!(o instanceof ByteBuf)) {
|
|
||||||
info.setLastPacket(o);
|
|
||||||
/* This transformer is more for fixing issues which we find hard at byte level :) */
|
|
||||||
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
|
||||||
int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
|
||||||
int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
|
||||||
|
|
||||||
Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
|
|
||||||
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
|
|
||||||
Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
|
|
||||||
for (int i = 0; i < locX.length; i++) {
|
|
||||||
int x = locX[i];
|
|
||||||
int z = locZ[i];
|
|
||||||
// world invoke function
|
|
||||||
Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z);
|
|
||||||
Object packet = constructor.newInstance(chunk, true, 65535);
|
|
||||||
channelHandlerContext.write(packet);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.write(channelHandlerContext, o, channelPromise);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,8 @@ package us.myles.ViaVersion.handlers;
|
|||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
import us.myles.ViaVersion.ConnectionInfo;
|
import us.myles.ViaVersion.ConnectionInfo;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -10,11 +12,6 @@ import java.lang.reflect.Method;
|
|||||||
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
private final ChannelInitializer<SocketChannel> oldInit;
|
private final ChannelInitializer<SocketChannel> oldInit;
|
||||||
private Method method;
|
private Method method;
|
||||||
private ConnectionInfo info;
|
|
||||||
private ViaInboundHandler inbound;
|
|
||||||
private ViaOutboundHandler outbound;
|
|
||||||
private SocketChannel socketChannel;
|
|
||||||
private ViaOutboundPacketHandler outbound2;
|
|
||||||
|
|
||||||
public ViaVersionInitializer(ChannelInitializer<SocketChannel> oldInit) {
|
public ViaVersionInitializer(ChannelInitializer<SocketChannel> oldInit) {
|
||||||
this.oldInit = oldInit;
|
this.oldInit = oldInit;
|
||||||
@ -28,24 +25,14 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||||
info = new ConnectionInfo();
|
ConnectionInfo info = new ConnectionInfo(socketChannel);
|
||||||
// Add originals
|
// Add originals
|
||||||
this.method.invoke(this.oldInit, socketChannel);
|
this.method.invoke(this.oldInit, socketChannel);
|
||||||
// Add our transformers
|
// Add our transformers
|
||||||
this.socketChannel = socketChannel;
|
ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
|
||||||
this.inbound = new ViaInboundHandler(socketChannel, info, this);
|
ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
|
||||||
this.outbound = new ViaOutboundHandler(socketChannel, info, this);
|
|
||||||
this.outbound2 = new ViaOutboundPacketHandler(socketChannel, info);
|
|
||||||
socketChannel.pipeline().addBefore("decoder", "via_incoming", this.inbound);
|
|
||||||
socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", this.outbound2);
|
|
||||||
socketChannel.pipeline().addBefore("encoder", "via_outgoing", this.outbound);
|
|
||||||
|
|
||||||
|
socketChannel.pipeline().replace("encoder", "encoder", encoder);
|
||||||
|
socketChannel.pipeline().replace("decoder", "decoder", decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(){
|
|
||||||
socketChannel.pipeline().remove("via_incoming");
|
|
||||||
socketChannel.pipeline().remove("via_outgoing");
|
|
||||||
socketChannel.pipeline().remove("via_outgoing2");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public enum MetaIndex {
|
|||||||
// creeper
|
// creeper
|
||||||
CREEPER_FUSE(Creeper.class, 16, Type.Byte, 11, NewType.VarInt), // -1 idle, 1 is fuse
|
CREEPER_FUSE(Creeper.class, 16, Type.Byte, 11, NewType.VarInt), // -1 idle, 1 is fuse
|
||||||
CREEPER_ISPOWERED(Creeper.class, 17, Type.Byte, 12, NewType.Boolean),
|
CREEPER_ISPOWERED(Creeper.class, 17, Type.Byte, 12, NewType.Boolean),
|
||||||
CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean), // TODO: Write on wiki.vg for current prot
|
CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean),
|
||||||
// ghast
|
// ghast
|
||||||
GHAST_ISATTACKING(Ghast.class, 16, Type.Byte, 11, NewType.Boolean),
|
GHAST_ISATTACKING(Ghast.class, 16, Type.Byte, 11, NewType.Boolean),
|
||||||
// slime
|
// slime
|
||||||
@ -88,8 +88,10 @@ public enum MetaIndex {
|
|||||||
WITHER_TARGET2(Wither.class, 18, Type.Int, 12, NewType.VarInt),
|
WITHER_TARGET2(Wither.class, 18, Type.Int, 12, NewType.VarInt),
|
||||||
WITHER_TARGET3(Wither.class, 19, Type.Int, 13, NewType.VarInt),
|
WITHER_TARGET3(Wither.class, 19, Type.Int, 13, NewType.VarInt),
|
||||||
WITHER_INVULN_TIME(Wither.class, 20, Type.Int, 14, NewType.VarInt),
|
WITHER_INVULN_TIME(Wither.class, 20, Type.Int, 14, NewType.VarInt),
|
||||||
|
// wither skull
|
||||||
|
WITHERSKULL_INVULN(WitherSkull.class, 10, Type.Byte, 5, NewType.Boolean),
|
||||||
// guardian
|
// guardian
|
||||||
GUARDIAN_INFO(Guardian.class, 16, Type.Byte, 11, NewType.Byte),
|
GUARDIAN_INFO(Guardian.class, 16, Type.Int, 11, NewType.Byte),
|
||||||
GUARDIAN_TARGET(Guardian.class, 17, Type.Int, 12, NewType.VarInt),
|
GUARDIAN_TARGET(Guardian.class, 17, Type.Int, 12, NewType.VarInt),
|
||||||
// boat
|
// boat
|
||||||
BOAT_SINCEHIT(Boat.class, 17, Type.Int, 5, NewType.VarInt),
|
BOAT_SINCEHIT(Boat.class, 17, Type.Int, 5, NewType.VarInt),
|
||||||
@ -159,16 +161,6 @@ public enum MetaIndex {
|
|||||||
return this.clazz;
|
return this.clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaIndex getIndex(Entity entity, int index) {
|
|
||||||
EntityType type;
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
type = EntityType.PLAYER;
|
|
||||||
} else {
|
|
||||||
type = entity.getType();
|
|
||||||
}
|
|
||||||
return getIndex(type, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MetaIndex getIndex(EntityType type, int index) {
|
public static MetaIndex getIndex(EntityType type, int index) {
|
||||||
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
|
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
|
||||||
if(entityClass == null){
|
if(entityClass == null){
|
||||||
|
183
src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java
Normale Datei
183
src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java
Normale Datei
@ -0,0 +1,183 @@
|
|||||||
|
package us.myles.ViaVersion.metadata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.util.EulerAngle;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.util.PacketUtil;
|
||||||
|
|
||||||
|
public class MetadataRewriter {
|
||||||
|
|
||||||
|
public static void writeMetadata1_9(EntityType type, List<Entry> list, ByteBuf output) {
|
||||||
|
short id = -1;
|
||||||
|
int data = -1;
|
||||||
|
Iterator<Entry> iterator = list.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Entry entry = iterator.next(); //
|
||||||
|
MetaIndex metaIndex = entry.index;
|
||||||
|
try {
|
||||||
|
if (metaIndex.getNewType() != NewType.Discontinued) {
|
||||||
|
if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts
|
||||||
|
output.writeByte(metaIndex.getNewIndex());
|
||||||
|
output.writeByte(metaIndex.getNewType().getTypeID());
|
||||||
|
}
|
||||||
|
Object value = entry.value;
|
||||||
|
switch (metaIndex.getNewType()) {
|
||||||
|
case Byte:
|
||||||
|
// convert from int, byte
|
||||||
|
if (metaIndex.getOldType() == Type.Byte) {
|
||||||
|
output.writeByte(((Byte) value).byteValue());
|
||||||
|
}
|
||||||
|
if (metaIndex.getOldType() == Type.Int) {
|
||||||
|
output.writeByte(((Integer) value).byteValue());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OptUUID:
|
||||||
|
String owner = (String) value;
|
||||||
|
UUID toWrite = null;
|
||||||
|
if (owner.length() != 0) {
|
||||||
|
try {
|
||||||
|
toWrite = UUID.fromString(owner);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.writeBoolean(toWrite != null);
|
||||||
|
if (toWrite != null)
|
||||||
|
PacketUtil.writeUUID((UUID) toWrite, output);
|
||||||
|
break;
|
||||||
|
case BlockID:
|
||||||
|
// if we have both sources :))
|
||||||
|
if (metaIndex.getOldType() == Type.Byte) {
|
||||||
|
data = ((Byte) value).byteValue();
|
||||||
|
}
|
||||||
|
if (metaIndex.getOldType() == Type.Short) {
|
||||||
|
id = ((Short) value).shortValue();
|
||||||
|
}
|
||||||
|
if (id != -1 && data != -1) {
|
||||||
|
int combined = id << 4 | data;
|
||||||
|
data = -1;
|
||||||
|
id = -1;
|
||||||
|
PacketUtil.writeVarInt(combined, output);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VarInt:
|
||||||
|
// convert from int, short, byte
|
||||||
|
if (metaIndex.getOldType() == Type.Byte) {
|
||||||
|
PacketUtil.writeVarInt(((Byte) value).intValue(), output);
|
||||||
|
}
|
||||||
|
if (metaIndex.getOldType() == Type.Short) {
|
||||||
|
PacketUtil.writeVarInt(((Short) value).intValue(), output);
|
||||||
|
}
|
||||||
|
if (metaIndex.getOldType() == Type.Int) {
|
||||||
|
PacketUtil.writeVarInt(((Integer) value).intValue(), output);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Float:
|
||||||
|
output.writeFloat(((Float) value).floatValue());
|
||||||
|
break;
|
||||||
|
case String:
|
||||||
|
PacketUtil.writeString((String) value, output);
|
||||||
|
break;
|
||||||
|
case Boolean:
|
||||||
|
output.writeBoolean(((Byte) value).byteValue() != 0);
|
||||||
|
break;
|
||||||
|
case Slot:
|
||||||
|
PacketUtil.writeItem(value, output);
|
||||||
|
break;
|
||||||
|
case Position:
|
||||||
|
Vector vector = (Vector) value;
|
||||||
|
output.writeInt((int) vector.getX());
|
||||||
|
output.writeInt((int) vector.getY());
|
||||||
|
output.writeInt((int) vector.getZ());
|
||||||
|
break;
|
||||||
|
case Vector3F:
|
||||||
|
EulerAngle angle = (EulerAngle) value;
|
||||||
|
output.writeFloat((float) angle.getX());
|
||||||
|
output.writeFloat((float) angle.getY());
|
||||||
|
output.writeFloat((float) angle.getZ());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("[Out] Unhandled MetaDataType: " + metaIndex.getNewType());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (type != null) {
|
||||||
|
System.out.println("An error occurred with entity meta data for " + type);
|
||||||
|
if (metaIndex != null) {
|
||||||
|
System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex());
|
||||||
|
System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.writeByte(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Entry> readMetadata1_8(EntityType entityType, ByteBuf buf) {
|
||||||
|
List<Entry> entries = new ArrayList<>();
|
||||||
|
byte item;
|
||||||
|
while ((item = buf.readByte()) != 127) {
|
||||||
|
Type type = Type.byId((item & 0xE0) >> 5);
|
||||||
|
int id = item & 0x1F;
|
||||||
|
MetaIndex index = MetaIndex.getIndex(entityType, id);
|
||||||
|
switch (type) {
|
||||||
|
case Byte:
|
||||||
|
entries.add(new Entry(index, buf.readByte()));
|
||||||
|
break;
|
||||||
|
case Short:
|
||||||
|
entries.add(new Entry(index, buf.readShort()));
|
||||||
|
break;
|
||||||
|
case Int:
|
||||||
|
entries.add(new Entry(index, buf.readInt()));
|
||||||
|
break;
|
||||||
|
case Float:
|
||||||
|
entries.add(new Entry(index, buf.readFloat()));
|
||||||
|
break;
|
||||||
|
case String:
|
||||||
|
entries.add(new Entry(index, PacketUtil.readString(buf)));
|
||||||
|
break;
|
||||||
|
case Slot:
|
||||||
|
entries.add(new Entry(index, PacketUtil.readItem(buf)));
|
||||||
|
break;
|
||||||
|
case Position: {
|
||||||
|
int x = buf.readInt();
|
||||||
|
int y = buf.readInt();
|
||||||
|
int z = buf.readInt();
|
||||||
|
entries.add(new Entry(index, new Vector(x, y, z)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Rotation: {
|
||||||
|
float x = buf.readFloat();
|
||||||
|
float y = buf.readFloat();
|
||||||
|
float z = buf.readFloat();
|
||||||
|
entries.add(new Entry(index, new EulerAngle(x, y, z)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
System.out.println("[Out] Unhandled MetaDataType: " + type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Entry {
|
||||||
|
|
||||||
|
private MetaIndex index;
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
private Entry(MetaIndex index, Object value) {
|
||||||
|
this.index = index;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,4 +18,8 @@ public enum Type {
|
|||||||
public int getTypeID() {
|
public int getTypeID() {
|
||||||
return typeID;
|
return typeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type byId(int id) {
|
||||||
|
return values()[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public enum PacketType {
|
|||||||
/* Play serverbound */
|
/* Play serverbound */
|
||||||
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
|
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
|
||||||
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
|
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
|
||||||
PLAY_CLICHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
|
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
|
||||||
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
|
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
|
||||||
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
|
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
|
||||||
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
|
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
|
||||||
@ -96,7 +96,7 @@ public enum PacketType {
|
|||||||
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
|
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
|
||||||
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
|
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
|
||||||
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
|
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
|
||||||
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x2F, 0x2F),
|
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F),
|
||||||
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
|
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
|
||||||
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
|
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
|
||||||
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
|
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
package us.myles.ViaVersion.transformers;
|
package us.myles.ViaVersion.transformers;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import us.myles.ViaVersion.CancelException;
|
||||||
import us.myles.ViaVersion.*;
|
import us.myles.ViaVersion.ConnectionInfo;
|
||||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||||
import us.myles.ViaVersion.packets.PacketType;
|
import us.myles.ViaVersion.packets.PacketType;
|
||||||
import us.myles.ViaVersion.packets.State;
|
import us.myles.ViaVersion.packets.State;
|
||||||
|
import us.myles.ViaVersion.util.PacketUtil;
|
||||||
|
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class IncomingTransformer {
|
public class IncomingTransformer {
|
||||||
private final Channel channel;
|
|
||||||
private final ConnectionInfo info;
|
private final ConnectionInfo info;
|
||||||
private final ViaVersionInitializer init;
|
|
||||||
|
|
||||||
public IncomingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) {
|
public IncomingTransformer(ConnectionInfo info) {
|
||||||
this.channel = channel;
|
|
||||||
this.info = info;
|
this.info = info;
|
||||||
this.init = init;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||||
@ -49,8 +44,8 @@ public class IncomingTransformer {
|
|||||||
PacketUtil.writeVarInt(protVer <= 102 ? protVer : 47, output); // pretend to be older
|
PacketUtil.writeVarInt(protVer <= 102 ? protVer : 47, output); // pretend to be older
|
||||||
|
|
||||||
if (protVer <= 102) {
|
if (protVer <= 102) {
|
||||||
// Not 1.9 remove pipes
|
// not 1.9, remove pipes
|
||||||
this.init.remove();
|
info.setActive(false);
|
||||||
}
|
}
|
||||||
String serverAddress = PacketUtil.readString(input);
|
String serverAddress = PacketUtil.readString(input);
|
||||||
PacketUtil.writeString(serverAddress, output);
|
PacketUtil.writeString(serverAddress, output);
|
||||||
@ -108,7 +103,7 @@ public class IncomingTransformer {
|
|||||||
try {
|
try {
|
||||||
Class<?> setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot");
|
Class<?> setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot");
|
||||||
Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null);
|
Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null);
|
||||||
channel.writeAndFlush(setSlotPacket); // slot is empty
|
info.getChannel().pipeline().writeAndFlush(setSlotPacket); // slot is empty
|
||||||
slot = -999; // we're evil, they'll throw item on the ground
|
slot = -999; // we're evil, they'll throw item on the ground
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -178,7 +173,7 @@ public class IncomingTransformer {
|
|||||||
output.writeByte(face);
|
output.writeByte(face);
|
||||||
int hand = PacketUtil.readVarInt(input);
|
int hand = PacketUtil.readVarInt(input);
|
||||||
|
|
||||||
ItemStack inHand = Core.getHandItem(info);
|
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
|
||||||
Object item = null;
|
Object item = null;
|
||||||
try {
|
try {
|
||||||
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
|
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
|
||||||
@ -205,12 +200,12 @@ public class IncomingTransformer {
|
|||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_USE_ITEM) {
|
if (packet == PacketType.PLAY_USE_ITEM) {
|
||||||
output.clear();
|
output.clear();
|
||||||
PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output);
|
PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output);
|
||||||
// Simulate using item :)
|
// Simulate using item :)
|
||||||
output.writeLong(-1L);
|
output.writeLong(-1L);
|
||||||
output.writeByte(255);
|
output.writeByte(255);
|
||||||
// write item in hand
|
// write item in hand
|
||||||
ItemStack inHand = Core.getHandItem(info);
|
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
|
||||||
Object item = null;
|
Object item = null;
|
||||||
try {
|
try {
|
||||||
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
|
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
|
||||||
@ -226,9 +221,9 @@ public class IncomingTransformer {
|
|||||||
}
|
}
|
||||||
PacketUtil.writeItem(item, output);
|
PacketUtil.writeItem(item, output);
|
||||||
|
|
||||||
output.writeByte(-1);
|
output.writeByte(-1);
|
||||||
output.writeByte(-1);
|
output.writeByte(-1);
|
||||||
output.writeByte(-1);
|
output.writeByte(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
output.writeBytes(input);
|
output.writeBytes(input);
|
||||||
|
@ -2,43 +2,37 @@ package us.myles.ViaVersion.transformers;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||||
import org.spacehq.mc.protocol.util.NetUtil;
|
import org.spacehq.mc.protocol.util.NetUtil;
|
||||||
|
import us.myles.ViaVersion.CancelException;
|
||||||
import us.myles.ViaVersion.*;
|
import us.myles.ViaVersion.ConnectionInfo;
|
||||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||||
import us.myles.ViaVersion.metadata.MetaIndex;
|
import us.myles.ViaVersion.api.ViaVersion;
|
||||||
import us.myles.ViaVersion.metadata.NewType;
|
import us.myles.ViaVersion.metadata.MetadataRewriter;
|
||||||
import us.myles.ViaVersion.sounds.SoundEffect;
|
|
||||||
import us.myles.ViaVersion.metadata.Type;
|
|
||||||
import us.myles.ViaVersion.packets.PacketType;
|
import us.myles.ViaVersion.packets.PacketType;
|
||||||
import us.myles.ViaVersion.packets.State;
|
import us.myles.ViaVersion.packets.State;
|
||||||
|
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.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static us.myles.ViaVersion.PacketUtil.*;
|
import static us.myles.ViaVersion.util.PacketUtil.*;
|
||||||
|
|
||||||
public class OutgoingTransformer {
|
public class OutgoingTransformer {
|
||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
private final Channel channel;
|
|
||||||
private final ConnectionInfo info;
|
private final ConnectionInfo info;
|
||||||
private final ViaVersionInitializer init;
|
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||||
private boolean cancel = false;
|
private boolean cancel = false;
|
||||||
private Map<Integer, UUID> uuidMap = new HashMap<Integer, UUID>();
|
private Map<Integer, UUID> uuidMap = new HashMap<Integer, UUID>();
|
||||||
|
private Map<Integer, EntityType> clientEntityTypes = new HashMap<Integer, EntityType>();
|
||||||
|
|
||||||
public OutgoingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) {
|
public OutgoingTransformer(ConnectionInfo info) {
|
||||||
this.channel = channel;
|
|
||||||
this.info = info;
|
this.info = info;
|
||||||
this.init = init;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||||
@ -60,14 +54,13 @@ public class OutgoingTransformer {
|
|||||||
// By default no transform
|
// By default no transform
|
||||||
PacketUtil.writeVarInt(packetID, output);
|
PacketUtil.writeVarInt(packetID, output);
|
||||||
if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) {
|
if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) {
|
||||||
String name = PacketUtil.readString(input);
|
String name = PacketUtil.readString(input);
|
||||||
SoundEffect effect = SoundEffect.getByName(name);
|
SoundEffect effect = SoundEffect.getByName(name);
|
||||||
int catid = 0;
|
int catid = 0;
|
||||||
String newname = name;
|
String newname = name;
|
||||||
if(effect != null)
|
if (effect != null) {
|
||||||
{
|
catid = effect.getCategory().getId();
|
||||||
catid = effect.getCategory().getId();
|
newname = effect.getNewName();
|
||||||
newname = effect.getNewName();
|
|
||||||
}
|
}
|
||||||
PacketUtil.writeString(newname, output);
|
PacketUtil.writeString(newname, output);
|
||||||
PacketUtil.writeVarInt(catid, output);
|
PacketUtil.writeVarInt(catid, output);
|
||||||
@ -77,22 +70,37 @@ public class OutgoingTransformer {
|
|||||||
int passenger = input.readInt();
|
int passenger = input.readInt();
|
||||||
int vehicle = input.readInt();
|
int vehicle = input.readInt();
|
||||||
boolean lead = input.readBoolean();
|
boolean lead = input.readBoolean();
|
||||||
if (!lead){
|
if (!lead) {
|
||||||
output.clear();
|
output.clear();
|
||||||
writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(),output);
|
writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(), output);
|
||||||
writeVarInt(vehicle,output);
|
writeVarInt(vehicle, output);
|
||||||
writeVarIntArray(Collections.singletonList(passenger),output);
|
writeVarIntArray(Collections.singletonList(passenger), output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
output.writeInt(passenger);
|
output.writeInt(passenger);
|
||||||
output.writeInt(vehicle);
|
output.writeInt(vehicle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_DISCONNECT){
|
if (packet == PacketType.PLAY_DISCONNECT) {
|
||||||
String reason = readString(input);
|
String reason = readString(input);
|
||||||
if (reason.startsWith("\""))
|
writeString(fixJson(reason), output);
|
||||||
reason = "{\"text\":" + reason + "}";
|
return;
|
||||||
writeString(reason,output);
|
}
|
||||||
|
if (packet == PacketType.PLAY_TITLE) {
|
||||||
|
int action = PacketUtil.readVarInt(input);
|
||||||
|
PacketUtil.writeVarInt(action, output);
|
||||||
|
if (action == 0 || action == 1) {
|
||||||
|
String text = PacketUtil.readString(input);
|
||||||
|
PacketUtil.writeString(fixJson(text), output);
|
||||||
|
}
|
||||||
|
output.writeBytes(input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet == PacketType.PLAY_PLAYER_LIST_HEADER_FOOTER) {
|
||||||
|
String header = readString(input);
|
||||||
|
String footer = readString(input);
|
||||||
|
writeString(fixJson(header), output);
|
||||||
|
writeString(fixJson(footer), output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_ENTITY_TELEPORT) {
|
if (packet == PacketType.PLAY_ENTITY_TELEPORT) {
|
||||||
@ -173,7 +181,9 @@ public class OutgoingTransformer {
|
|||||||
if (packet == PacketType.LOGIN_SUCCESS) {
|
if (packet == PacketType.LOGIN_SUCCESS) {
|
||||||
String uu = PacketUtil.readString(input);
|
String uu = PacketUtil.readString(input);
|
||||||
PacketUtil.writeString(uu, output);
|
PacketUtil.writeString(uu, output);
|
||||||
info.setUUID(UUID.fromString(uu));
|
UUID uniqueId = UUID.fromString(uu);
|
||||||
|
info.setUUID(uniqueId);
|
||||||
|
plugin.setPorted(uniqueId, true);
|
||||||
output.writeBytes(input);
|
output.writeBytes(input);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -198,30 +208,45 @@ public class OutgoingTransformer {
|
|||||||
int id = PacketUtil.readVarInt(input);
|
int id = PacketUtil.readVarInt(input);
|
||||||
PacketUtil.writeVarInt(id, output);
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
|
||||||
try {
|
transformMetadata(id, input, output);
|
||||||
List dw = ReflectionUtil.get(info.getLastPacket(), "b", List.class);
|
return;
|
||||||
// get entity via entityID, not preferred but we need it.
|
}
|
||||||
Entity entity = Core.getEntity(info.getUUID(), id);
|
|
||||||
if (entity != null) {
|
if (packet == PacketType.PLAY_SPAWN_GLOBAL_ENTITY) {
|
||||||
transformMetadata(entity, dw, output);
|
int id = PacketUtil.readVarInt(input);
|
||||||
} else {
|
PacketUtil.writeVarInt(id, output);
|
||||||
// Died before we could get to it. rip
|
|
||||||
throw new CancelException();
|
// only used for lightning
|
||||||
}
|
byte type = input.readByte();
|
||||||
} catch (NoSuchFieldException e) {
|
clientEntityTypes.put(id, EntityType.LIGHTNING);
|
||||||
e.printStackTrace();
|
output.writeByte(type);
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
double x = input.readInt();
|
||||||
|
output.writeDouble(x / 32D);
|
||||||
|
double y = input.readInt();
|
||||||
|
output.writeDouble(y / 32D);
|
||||||
|
double z = input.readInt();
|
||||||
|
output.writeDouble(z / 32D);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet == PacketType.PLAY_DESTROY_ENTITIES) {
|
||||||
|
int count = PacketUtil.readVarInt(input);
|
||||||
|
PacketUtil.writeVarInt(count, output);
|
||||||
|
|
||||||
|
int[] toDestroy = PacketUtil.readVarInts(count, input);
|
||||||
|
for (int entityID : toDestroy) {
|
||||||
|
clientEntityTypes.remove(entityID);
|
||||||
|
PacketUtil.writeVarInt(entityID, output);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_SPAWN_OBJECT) {
|
if (packet == PacketType.PLAY_SPAWN_OBJECT) {
|
||||||
int id = PacketUtil.readVarInt(input);
|
int id = PacketUtil.readVarInt(input);
|
||||||
PacketUtil.writeVarInt(id, output);
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
|
||||||
PacketUtil.writeUUID(getUUID(id), output);
|
PacketUtil.writeUUID(getUUID(id), output);
|
||||||
|
|
||||||
byte type = input.readByte();
|
byte type = input.readByte();
|
||||||
|
clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, true));
|
||||||
output.writeByte(type);
|
output.writeByte(type);
|
||||||
|
|
||||||
double x = input.readInt();
|
double x = input.readInt();
|
||||||
@ -239,11 +264,10 @@ public class OutgoingTransformer {
|
|||||||
output.writeInt(data);
|
output.writeInt(data);
|
||||||
|
|
||||||
short vX = 0, vY = 0, vZ = 0;
|
short vX = 0, vY = 0, vZ = 0;
|
||||||
if(data > 0)
|
if (data > 0) {
|
||||||
{
|
vX = input.readShort();
|
||||||
vX = input.readShort();
|
vY = input.readShort();
|
||||||
vY = input.readShort();
|
vZ = input.readShort();
|
||||||
vZ = input.readShort();
|
|
||||||
}
|
}
|
||||||
output.writeShort(vX);
|
output.writeShort(vX);
|
||||||
output.writeShort(vY);
|
output.writeShort(vY);
|
||||||
@ -251,10 +275,10 @@ public class OutgoingTransformer {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_SPAWN_XP_ORB) { // TODO: Verify
|
if (packet == PacketType.PLAY_SPAWN_XP_ORB) {
|
||||||
int id = PacketUtil.readVarInt(input);
|
int id = PacketUtil.readVarInt(input);
|
||||||
|
clientEntityTypes.put(id, EntityType.EXPERIENCE_ORB);
|
||||||
PacketUtil.writeVarInt(id, output);
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
|
||||||
double x = input.readInt();
|
double x = input.readInt();
|
||||||
output.writeDouble(x / 32D);
|
output.writeDouble(x / 32D);
|
||||||
double y = input.readInt();
|
double y = input.readInt();
|
||||||
@ -267,13 +291,43 @@ public class OutgoingTransformer {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (packet == PacketType.PLAY_SPAWN_PAINTING) {
|
||||||
|
int id = PacketUtil.readVarInt(input);
|
||||||
|
clientEntityTypes.put(id, EntityType.PAINTING);
|
||||||
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
|
||||||
|
PacketUtil.writeUUID(getUUID(id), output);
|
||||||
|
String title = PacketUtil.readString(input);
|
||||||
|
PacketUtil.writeString(title, output);
|
||||||
|
|
||||||
|
long[] position = PacketUtil.readBlockPosition(input);
|
||||||
|
PacketUtil.writeBlockPosition(output, position[0], position[1], position[2]);
|
||||||
|
|
||||||
|
byte direction = input.readByte();
|
||||||
|
output.writeByte(direction);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet == PacketType.PLAY_OPEN_WINDOW) {
|
||||||
|
int windowId = input.readUnsignedByte();
|
||||||
|
String type = readString(input);
|
||||||
|
String windowTitle = readString(input);
|
||||||
|
|
||||||
|
output.writeByte(windowId);
|
||||||
|
writeString(type, output);
|
||||||
|
writeString(fixJson(windowTitle), output);
|
||||||
|
output.writeBytes(input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (packet == PacketType.PLAY_SPAWN_MOB) {
|
if (packet == PacketType.PLAY_SPAWN_MOB) {
|
||||||
int id = PacketUtil.readVarInt(input);
|
int id = PacketUtil.readVarInt(input);
|
||||||
PacketUtil.writeVarInt(id, output);
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
|
||||||
PacketUtil.writeUUID(getUUID(id), output);
|
PacketUtil.writeUUID(getUUID(id), output);
|
||||||
short type = input.readUnsignedByte();
|
short type = input.readUnsignedByte();
|
||||||
|
clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, false));
|
||||||
output.writeByte(type);
|
output.writeByte(type);
|
||||||
|
|
||||||
double x = input.readInt();
|
double x = input.readInt();
|
||||||
output.writeDouble(x / 32D);
|
output.writeDouble(x / 32D);
|
||||||
double y = input.readInt();
|
double y = input.readInt();
|
||||||
@ -293,16 +347,8 @@ public class OutgoingTransformer {
|
|||||||
output.writeShort(vY);
|
output.writeShort(vY);
|
||||||
short vZ = input.readShort();
|
short vZ = input.readShort();
|
||||||
output.writeShort(vZ);
|
output.writeShort(vZ);
|
||||||
try {
|
|
||||||
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "l", ReflectionUtil.nms("DataWatcher"));
|
transformMetadata(id, input, output);
|
||||||
transformMetadata(dataWatcher, output);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet == PacketType.PLAY_UPDATE_SIGN) {
|
if (packet == PacketType.PLAY_UPDATE_SIGN) {
|
||||||
@ -310,21 +356,28 @@ public class OutgoingTransformer {
|
|||||||
output.writeLong(location);
|
output.writeLong(location);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
String line = PacketUtil.readString(input);
|
String line = PacketUtil.readString(input);
|
||||||
if (line == null || line.equalsIgnoreCase("null")) {
|
PacketUtil.writeString(fixJson(line), output);
|
||||||
line = "{\"text\":\"\"}";
|
|
||||||
} else {
|
|
||||||
if (!line.startsWith("\"") && !line.startsWith("{"))
|
|
||||||
line = "\"" + line + "\"";
|
|
||||||
if (line.startsWith("\""))
|
|
||||||
line = "{\"text\":" + line + "}";
|
|
||||||
}
|
|
||||||
PacketUtil.writeString(line, output);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (packet == PacketType.PLAY_CHAT_MESSAGE) {
|
||||||
|
String chat = PacketUtil.readString(input);
|
||||||
|
PacketUtil.writeString(fixJson(chat), output);
|
||||||
|
|
||||||
|
byte pos = input.readByte();
|
||||||
|
output.writeByte(pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet == PacketType.PLAY_JOIN_GAME) {
|
||||||
|
int id = input.readInt();
|
||||||
|
clientEntityTypes.put(id, EntityType.PLAYER);
|
||||||
|
output.writeInt(id);
|
||||||
|
output.writeBytes(input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
|
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
|
||||||
int id = PacketUtil.readVarInt(input);
|
int id = PacketUtil.readVarInt(input);
|
||||||
PacketUtil.writeVarInt(id, output);
|
PacketUtil.writeVarInt(id, output);
|
||||||
|
clientEntityTypes.put(id, EntityType.PLAYER);
|
||||||
UUID playerUUID = PacketUtil.readUUID(input);
|
UUID playerUUID = PacketUtil.readUUID(input);
|
||||||
PacketUtil.writeUUID(playerUUID, output);
|
PacketUtil.writeUUID(playerUUID, output);
|
||||||
|
|
||||||
@ -339,20 +392,15 @@ public class OutgoingTransformer {
|
|||||||
output.writeByte(pitch);
|
output.writeByte(pitch);
|
||||||
byte yaw = input.readByte();
|
byte yaw = input.readByte();
|
||||||
output.writeByte(yaw);
|
output.writeByte(yaw);
|
||||||
try {
|
|
||||||
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "i", ReflectionUtil.nms("DataWatcher"));
|
// next field is Current Item, this was removed in 1.9 so we'll ignore it
|
||||||
transformMetadata(dataWatcher, output);
|
input.readShort();
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
e.printStackTrace();
|
transformMetadata(id, input, output);
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(packet == PacketType.PLAY_MAP) {
|
if (packet == PacketType.PLAY_MAP) {
|
||||||
int damage = PacketUtil.readVarInt(input);
|
int damage = PacketUtil.readVarInt(input);
|
||||||
PacketUtil.writeVarInt(damage, output);
|
PacketUtil.writeVarInt(damage, output);
|
||||||
byte scale = input.readByte();
|
byte scale = input.readByte();
|
||||||
@ -430,141 +478,27 @@ public class OutgoingTransformer {
|
|||||||
output.writeBytes(input);
|
output.writeBytes(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformMetadata(Object dw, ByteBuf output) {
|
private String fixJson(String line) {
|
||||||
// get entity
|
if (line == null || line.equalsIgnoreCase("null")) {
|
||||||
try {
|
line = "{\"text\":\"\"}";
|
||||||
Class<?> nmsClass = ReflectionUtil.nms("Entity");
|
} else {
|
||||||
Object nmsEntity = ReflectionUtil.get(dw, "a", nmsClass);
|
if (!line.startsWith("\"") && !line.startsWith("{"))
|
||||||
Class<?> craftClass = ReflectionUtil.obc("entity.CraftEntity");
|
line = "\"" + line + "\"";
|
||||||
Method bukkitMethod = craftClass.getDeclaredMethod("getEntity", ReflectionUtil.obc("CraftServer"), nmsClass);
|
if (line.startsWith("\""))
|
||||||
|
line = "{\"text\":" + line + "}";
|
||||||
Object entity = bukkitMethod.invoke(null, Bukkit.getServer(), nmsEntity);
|
|
||||||
transformMetadata((Entity) entity, (List) ReflectionUtil.invoke(dw, "b"), output);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformMetadata(Entity entity, List dw, ByteBuf output) {
|
private void transformMetadata(int entityID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||||
if (dw != null) {
|
EntityType type = clientEntityTypes.get(entityID);
|
||||||
short id = -1;
|
if (type == null) {
|
||||||
int data = -1;
|
System.out.println("Unable to get entity for ID: " + entityID);
|
||||||
|
output.writeByte(255);
|
||||||
Iterator iterator = dw.iterator();
|
return;
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Object watchableObj = iterator.next(); //
|
|
||||||
MetaIndex metaIndex = null;
|
|
||||||
try {
|
|
||||||
metaIndex = MetaIndex.getIndex(entity, (int) ReflectionUtil.invoke(watchableObj, "a"));
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (metaIndex.getNewType() != NewType.Discontinued) {
|
|
||||||
if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts
|
|
||||||
output.writeByte(metaIndex.getNewIndex());
|
|
||||||
output.writeByte(metaIndex.getNewType().getTypeID());
|
|
||||||
}
|
|
||||||
Object value = ReflectionUtil.invoke(watchableObj, "b");
|
|
||||||
switch (metaIndex.getNewType()) {
|
|
||||||
case Byte:
|
|
||||||
// convert from int, byte
|
|
||||||
if (metaIndex.getOldType() == Type.Byte) {
|
|
||||||
output.writeByte(((Byte) value).byteValue());
|
|
||||||
}
|
|
||||||
if (metaIndex.getOldType() == Type.Int) {
|
|
||||||
output.writeByte(((Integer) value).byteValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OptUUID:
|
|
||||||
String owner = (String) value;
|
|
||||||
UUID toWrite = null;
|
|
||||||
if (owner.length() != 0) {
|
|
||||||
try {
|
|
||||||
toWrite = UUID.fromString(owner);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.writeBoolean(toWrite != null);
|
|
||||||
if (toWrite != null)
|
|
||||||
PacketUtil.writeUUID((UUID) toWrite, output);
|
|
||||||
break;
|
|
||||||
case BlockID:
|
|
||||||
// if we have both sources :))
|
|
||||||
if (metaIndex.getOldType() == Type.Byte) {
|
|
||||||
data = ((Byte) value).byteValue();
|
|
||||||
}
|
|
||||||
if (metaIndex.getOldType() == Type.Short) {
|
|
||||||
id = ((Short) value).shortValue();
|
|
||||||
}
|
|
||||||
if (id != -1 && data != -1) {
|
|
||||||
int combined = id << 4 | data;
|
|
||||||
data = -1;
|
|
||||||
id = -1;
|
|
||||||
PacketUtil.writeVarInt(combined, output);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VarInt:
|
|
||||||
// convert from int, short, byte
|
|
||||||
if (metaIndex.getOldType() == Type.Byte) {
|
|
||||||
PacketUtil.writeVarInt(((Byte) value).intValue(), output);
|
|
||||||
}
|
|
||||||
if (metaIndex.getOldType() == Type.Short) {
|
|
||||||
PacketUtil.writeVarInt(((Short) value).intValue(), output);
|
|
||||||
}
|
|
||||||
if (metaIndex.getOldType() == Type.Int) {
|
|
||||||
PacketUtil.writeVarInt(((Integer) value).intValue(), output);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Float:
|
|
||||||
output.writeFloat(((Float) value).floatValue());
|
|
||||||
break;
|
|
||||||
case String:
|
|
||||||
PacketUtil.writeString((String) value, output);
|
|
||||||
break;
|
|
||||||
case Boolean:
|
|
||||||
output.writeBoolean(((Byte) value).byteValue() != 0);
|
|
||||||
break;
|
|
||||||
case Slot:
|
|
||||||
PacketUtil.writeItem(value, output);
|
|
||||||
break;
|
|
||||||
case Position:
|
|
||||||
output.writeInt((int) ReflectionUtil.invoke(value, "getX"));
|
|
||||||
output.writeInt((int) ReflectionUtil.invoke(value, "getY"));
|
|
||||||
output.writeInt((int) ReflectionUtil.invoke(value, "getZ"));
|
|
||||||
break;
|
|
||||||
case Vector3F:
|
|
||||||
output.writeFloat((float) ReflectionUtil.invoke(value, "getX"));
|
|
||||||
output.writeFloat((float) ReflectionUtil.invoke(value, "getY"));
|
|
||||||
output.writeFloat((float) ReflectionUtil.invoke(value, "getZ"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (entity != null) {
|
|
||||||
System.out.println("An error occurred with entity meta data for " + entity.getType());
|
|
||||||
System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex());
|
|
||||||
System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType());
|
|
||||||
}
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
output.writeByte(255);
|
List<MetadataRewriter.Entry> list = MetadataRewriter.readMetadata1_8(type, input);
|
||||||
|
MetadataRewriter.writeMetadata1_9(type, list, output);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
91
src/main/java/us/myles/ViaVersion/util/EntityUtil.java
Normale Datei
91
src/main/java/us/myles/ViaVersion/util/EntityUtil.java
Normale Datei
@ -0,0 +1,91 @@
|
|||||||
|
package us.myles.ViaVersion.util;
|
||||||
|
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
public class EntityUtil {
|
||||||
|
public static EntityType getTypeFromID(int typeID, boolean isObject) {
|
||||||
|
if (isObject) {
|
||||||
|
return getObjectFromID(typeID);
|
||||||
|
} else {
|
||||||
|
return EntityType.fromId(typeID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on http://wiki.vg/index.php?title=Entities
|
||||||
|
public static EntityType getObjectFromID(int objectID) {
|
||||||
|
EntityType type;
|
||||||
|
switch (objectID) {
|
||||||
|
case 2:
|
||||||
|
type = EntityType.DROPPED_ITEM;
|
||||||
|
break;
|
||||||
|
case 77:
|
||||||
|
type = EntityType.LEASH_HITCH;
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
type = EntityType.ARROW;
|
||||||
|
break;
|
||||||
|
case 61:
|
||||||
|
type = EntityType.SNOWBALL;
|
||||||
|
break;
|
||||||
|
case 63:
|
||||||
|
type = EntityType.FIREBALL;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
type = EntityType.SMALL_FIREBALL;
|
||||||
|
break;
|
||||||
|
case 65:
|
||||||
|
type = EntityType.ENDER_PEARL;
|
||||||
|
break;
|
||||||
|
case 72:
|
||||||
|
type = EntityType.ENDER_SIGNAL;
|
||||||
|
break;
|
||||||
|
case 75:
|
||||||
|
type = EntityType.THROWN_EXP_BOTTLE;
|
||||||
|
break;
|
||||||
|
case 71:
|
||||||
|
type = EntityType.ITEM_FRAME;
|
||||||
|
break;
|
||||||
|
case 66:
|
||||||
|
type = EntityType.WITHER_SKULL;
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
type = EntityType.PRIMED_TNT;
|
||||||
|
break;
|
||||||
|
case 70:
|
||||||
|
type = EntityType.FALLING_BLOCK;
|
||||||
|
break;
|
||||||
|
case 76:
|
||||||
|
type = EntityType.FIREWORK;
|
||||||
|
break;
|
||||||
|
case 78:
|
||||||
|
type = EntityType.ARMOR_STAND;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
type = EntityType.BOAT;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
type = EntityType.MINECART;
|
||||||
|
break;
|
||||||
|
case 51:
|
||||||
|
type = EntityType.ENDER_CRYSTAL;
|
||||||
|
break;
|
||||||
|
case 73:
|
||||||
|
type = EntityType.SPLASH_POTION;
|
||||||
|
break;
|
||||||
|
case 62:
|
||||||
|
type = EntityType.EGG;
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
type = EntityType.FISHING_HOOK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = EntityType.fromId(objectID);
|
||||||
|
if (type == null) {
|
||||||
|
System.out.println("Unable to find entity type for " + objectID);
|
||||||
|
type = EntityType.UNKNOWN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package us.myles.ViaVersion;
|
package us.myles.ViaVersion.util;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
@ -42,29 +42,38 @@ public class PacketUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) {
|
public static List<Object> callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) {
|
||||||
ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress");
|
|
||||||
List<Object> output = new ArrayList<Object>();
|
List<Object> output = new ArrayList<Object>();
|
||||||
try {
|
try {
|
||||||
PacketUtil.DECODE_METHOD.invoke(x, ctx, msg, output);
|
PacketUtil.DECODE_METHOD.invoke(decoder, ctx, input, output);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void callEncode(MessageToByteEncoder encoder, ChannelHandlerContext ctx, Object msg, ByteBuf output) {
|
||||||
|
try {
|
||||||
|
PacketUtil.ENCODE_METHOD.invoke(encoder, ctx, msg, output);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvocationTargetException 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);
|
return output.size() == 0 ? null : (ByteBuf) output.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) {
|
public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||||
MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress");
|
MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress");
|
||||||
ByteBuf output = ctx.alloc().buffer();
|
ByteBuf output = ctx.alloc().buffer();
|
||||||
try {
|
callEncode(x, ctx, msg, output);
|
||||||
PacketUtil.ENCODE_METHOD.invoke(x, ctx, msg, output);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +194,9 @@ public class PacketUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeVarIntArray(List<Integer> integers, ByteBuf output) {
|
public static void writeVarIntArray(List<Integer> integers, ByteBuf output) {
|
||||||
writeVarInt(integers.size(),output);
|
writeVarInt(integers.size(), output);
|
||||||
for (Integer i : integers){
|
for (Integer i : integers) {
|
||||||
writeVarInt(i,output);
|
writeVarInt(i, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,4 +372,46 @@ public class PacketUtil {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Object readItem(ByteBuf output) {
|
||||||
|
try {
|
||||||
|
Class<?> serializer = ReflectionUtil.nms("PacketDataSerializer");
|
||||||
|
Object init = serializer.getDeclaredConstructor(ByteBuf.class).newInstance(output);
|
||||||
|
Method toCall = init.getClass().getDeclaredMethod("i");
|
||||||
|
return toCall.invoke(init);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long[] readBlockPosition(ByteBuf buf) {
|
||||||
|
long val = buf.readLong();
|
||||||
|
long x = (val >> 38); // signed
|
||||||
|
long y = (val >> 26) & 0xfff; // unsigned
|
||||||
|
// this shifting madness is used to preserve sign
|
||||||
|
long z = (val << 38) >> 38; // signed
|
||||||
|
return new long[]{x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeBlockPosition(ByteBuf buf, long x, long y, long z) {
|
||||||
|
buf.writeLong(((x & 0x3ffffff) << 38) | ((y & 0xfff) << 26) | (z & 0x3ffffff));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] readVarInts(int amount, ByteBuf input) {
|
||||||
|
int data[] = new int[amount];
|
||||||
|
for (int index = 0; index < amount; index++) {
|
||||||
|
data[index] = PacketUtil.readVarInt(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package us.myles.ViaVersion;
|
package us.myles.ViaVersion.util;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
name: ViaVersion
|
name: ViaVersion
|
||||||
main: us.myles.ViaVersion.Core
|
main: us.myles.ViaVersion.ViaVersionPlugin
|
||||||
author: _MylesC
|
author: _MylesC
|
||||||
version: 0.3.3
|
version: 0.4.5
|
||||||
|
load: startup
|
||||||
|
loadbefore: [ProtocolLib, ProxyPipe]
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren