Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e885a1cec2 | ||
|
|
e2de17e221 | ||
|
|
5738a992e5 | ||
|
|
122a7e05f6 | ||
|
|
546fdf95d4 | ||
|
|
e6a9cc4776 | ||
|
|
c641f02d7e | ||
|
|
bf11edfaa9 | ||
|
|
3cc6bf4c51 | ||
|
|
156f42318c | ||
|
|
1a1ffe4265 | ||
|
|
3fa4b7a7b2 | ||
|
|
59b270beda | ||
|
|
135942c033 | ||
|
|
0930420844 | ||
|
|
16e546803c | ||
|
|
f6b297fea4 | ||
|
|
3f0b5c8a2b | ||
|
|
dff7d98dd5 |
No files matched your search
@@ -0,0 +1,37 @@
|
||||
# ViaVersion 0.3
|
||||
**Allows the connection of 1.8 clients to 1.9**
|
||||
|
||||
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
|
||||
|
||||
**Don't use late bind nor ProtocolLib**
|
||||
|
||||
**As of this point it doesn't have everything, I need to fix:**
|
||||
|
||||
Attempt to make boats nicer when they don't work
|
||||
|
||||
Remap spawn eggs
|
||||
|
||||
|
||||
|
||||
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
||||
|
||||
|
||||
Credits:
|
||||
--------
|
||||
|
||||
**wiki.vg** (Protocol info, though I did have to find a lot myself)
|
||||
|
||||
**MCProtocolLib** (used for chunk reading & some of writing.)
|
||||
|
||||
**Myself** (harhar)
|
||||
|
||||
**Matsv** (Movement Fix)
|
||||
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
You may modify this work, all rights are still owned by myself excluding any files in the spacebase package as they belong to the respected contributors. Please credit the original author (myself) when forking etc. (or if it helped you understand the new protocol or netty!)
|
||||
|
||||
|
||||
Thanks!
|
||||
@@ -21,12 +21,28 @@
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<!-- Sorry about this path >:( I promise to fix it when I stop using NMS :) -->
|
||||
<groupId>org.spigot</groupId>
|
||||
<artifactId>spigot1.8</artifactId>
|
||||
<version>1.8</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>E:/spigot_server.jar</systemPath>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.8.8-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.20.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -5,18 +5,13 @@ import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.minecraft.server.v1_8_R3.Entity;
|
||||
import net.minecraft.server.v1_8_R3.MinecraftServer;
|
||||
import net.minecraft.server.v1_8_R3.ServerConnection;
|
||||
import net.minecraft.server.v1_8_R3.WorldServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -26,49 +21,49 @@ public class Core extends JavaPlugin {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
/* Obvious message here:
|
||||
If loading this plugin nobody will be on 1.9 cause only 1.8 so we're fine, as for reloading ugh.
|
||||
Clients might crash cause of it being a bum maybe? :P
|
||||
*/
|
||||
try {
|
||||
injectPacketHandler();
|
||||
} catch (Exception e) {
|
||||
if(Bukkit.getPluginManager().getPlugin("ProtocolLib") != null){
|
||||
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();
|
||||
System.out.println("Unable to inject handlers, this version only supports 1.8.");
|
||||
}
|
||||
}
|
||||
|
||||
public void injectPacketHandler() throws NoSuchFieldException, IllegalAccessException {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
ServerConnection connection = server.getServerConnection();
|
||||
public void injectPacketHandler() throws Exception {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = serverClazz.getDeclaredMethod("getServerConnection").invoke(server);
|
||||
|
||||
List<ChannelFuture> futures = getPrivateField(connection, "g", List.class);
|
||||
List<ChannelFuture> futures = ReflectionUtil.get(connection, "g", List.class);
|
||||
if (futures.size() == 0) {
|
||||
throw new Exception("Could not find server to inject (late bind?)");
|
||||
}
|
||||
|
||||
for (ChannelFuture future : futures) {
|
||||
ChannelPipeline pipeline = future.channel().pipeline();
|
||||
ChannelHandler bootstrapAcceptor = pipeline.first();
|
||||
ChannelInitializer<SocketChannel> oldInit = getPrivateField(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
setPrivateField(bootstrapAcceptor, "childHandler", newInit);
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Entity getEntity(final UUID player, final int id) {
|
||||
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<Entity>() {
|
||||
@Override
|
||||
public Entity call() throws Exception {
|
||||
Player p = Bukkit.getPlayer(player);
|
||||
if (p == null) return null;
|
||||
WorldServer ws = ((CraftWorld) p.getWorld()).getHandle();
|
||||
for (Entity e : ws.entityList) {
|
||||
if (e.getId() == id) {
|
||||
for (Entity e : p.getWorld().getEntities()) {
|
||||
if (e.getEntityId() == id) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
System.out.println("Couldn't find in the world!!");
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
@@ -77,24 +72,6 @@ public class Core extends JavaPlugin {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T> T getPrivateField(Object o, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(o);
|
||||
}
|
||||
|
||||
public static void setPrivateField(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
field.set(o, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||
|
||||
@@ -338,4 +338,22 @@ public class PacketUtil {
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void writeItem(Object value, ByteBuf output) {
|
||||
try {
|
||||
Class<?> serializer = ReflectionUtil.nms("PacketDataSerializer");
|
||||
Object init = serializer.getDeclaredConstructor(ByteBuf.class).newInstance(output);
|
||||
Method toCall = init.getClass().getDeclaredMethod("a", ReflectionUtil.nms("ItemStack"));
|
||||
toCall.invoke(init, value);
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ReflectionUtil {
|
||||
private static String BASE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
private static String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
|
||||
public static Class<?> nms(String className) throws ClassNotFoundException {
|
||||
return Class.forName(NMS + "." + className);
|
||||
}
|
||||
public static Class<?> obc(String className) throws ClassNotFoundException {
|
||||
return Class.forName(BASE + "." + className);
|
||||
}
|
||||
|
||||
public static Object invokeStatic(Class<?> clazz, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Method m = clazz.getDeclaredMethod(method);
|
||||
return m.invoke(null);
|
||||
}
|
||||
|
||||
public static Object invoke(Object o, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Method m = o.getClass().getDeclaredMethod(method);
|
||||
return m.invoke(o);
|
||||
}
|
||||
|
||||
public static <T> T get(Object o, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(o);
|
||||
}
|
||||
|
||||
public static void set(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
field.set(o, value);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import us.myles.ViaVersion.chunks.ByteWriter;
|
||||
|
||||
public class Testing {
|
||||
public static void main(String[] args) {
|
||||
// Map<NewType, Set<Type>> conversions = new HashMap<>();
|
||||
// for (MetaIndex metaIndex : MetaIndex.values()) {
|
||||
// if (!conversions.containsKey(metaIndex.getNewType())) {
|
||||
// conversions.put(metaIndex.getNewType(), new HashSet<Type>());
|
||||
// }
|
||||
// conversions.get(metaIndex.getNewType()).add(metaIndex.getOldType());
|
||||
// }
|
||||
// for (Map.Entry<NewType, Set<Type>> conv : conversions.entrySet()) {
|
||||
// System.out.println("Type: " + conv.getKey().name());
|
||||
// for (Type t : conv.getValue()) {
|
||||
// System.out.println(" -> " + t.name());
|
||||
// }
|
||||
// }
|
||||
ByteWriter bytes = new ByteWriter(2);
|
||||
int id = 255;
|
||||
int data = 4;
|
||||
bytes.writeByte(0, 1);
|
||||
bytes.writeFullByte(id);
|
||||
bytes.writeByte(data, 4);
|
||||
|
||||
System.out.println("output bytes: ");
|
||||
for(byte b:bytes.getOutput()){
|
||||
System.out.println("B: " + b + " I: " + ((int)b & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static byte writeByte(byte a, byte b, int index){
|
||||
int intA = (int) a & 0xff;
|
||||
int intB = (int) b & 0xff;
|
||||
System.out.println("As int: " + (intA | (intB >> index)));
|
||||
return (byte) (intA | (intB >> index));
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ public class ViaInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
} finally {
|
||||
bytebuf.release();
|
||||
}
|
||||
if (compression) {
|
||||
// recompress :)
|
||||
|
||||
@@ -35,6 +35,8 @@ public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter {
|
||||
outgoingTransformer.transform(id, bytebuf, newPacket);
|
||||
} catch (CancelException e) {
|
||||
return;
|
||||
} finally {
|
||||
bytebuf.release();
|
||||
}
|
||||
if (compression) {
|
||||
// recompress :)
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import net.minecraft.server.v1_8_R3.Packet;
|
||||
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunkBulk;
|
||||
import net.minecraft.server.v1_8_R3.World;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.Core;
|
||||
import us.myles.ViaVersion.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter {
|
||||
private final ConnectionInfo info;
|
||||
@@ -17,21 +16,26 @@ public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter {
|
||||
public ViaOutboundPacketHandler(Channel c, ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext channelHandlerContext, Object o, ChannelPromise channelPromise) throws Exception {
|
||||
if(o instanceof Packet){
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
info.setLastPacket(o);
|
||||
/* This transformer is more for fixing issues which we find hard at byte level :) */
|
||||
if(o instanceof PacketPlayOutMapChunkBulk){
|
||||
PacketPlayOutMapChunkBulk bulk = (PacketPlayOutMapChunkBulk) o;
|
||||
int[] locX = Core.getPrivateField(bulk, "a", int[].class);
|
||||
int[] locZ = Core.getPrivateField(bulk, "b", int[].class);
|
||||
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
||||
int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
||||
int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
||||
|
||||
World world = Core.getPrivateField(bulk, "world", World.class);
|
||||
for(int i = 0;i<locX.length;i++){
|
||||
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];
|
||||
channelHandlerContext.write(new PacketPlayOutMapChunk(world.getChunkAt(x, z), true, 65535)); // magic was 65535
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import net.minecraft.server.v1_8_R3.Entity;
|
||||
import net.minecraft.server.v1_8_R3.EntityPlayer;
|
||||
import net.minecraft.server.v1_8_R3.EntityTypes;
|
||||
import org.bukkit.entity.*;
|
||||
|
||||
public enum MetaIndex {
|
||||
@@ -164,18 +161,20 @@ public enum MetaIndex {
|
||||
|
||||
public static MetaIndex getIndex(Entity entity, int index) {
|
||||
EntityType type;
|
||||
if (entity instanceof EntityPlayer) {
|
||||
if (entity instanceof Player) {
|
||||
type = EntityType.PLAYER;
|
||||
} else {
|
||||
int entityID = EntityTypes.a(entity);
|
||||
type = EntityType.fromId(entityID);
|
||||
type = entity.getType();
|
||||
}
|
||||
return getIndex(type, index);
|
||||
}
|
||||
|
||||
public static MetaIndex getIndex(EntityType type, int index) {
|
||||
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
|
||||
|
||||
if(entityClass == null){
|
||||
System.out.println("Could not get entity class for " + type);
|
||||
return null;
|
||||
}
|
||||
for (MetaIndex mi : MetaIndex.values()) {
|
||||
if (mi.getIndex() == index) {
|
||||
if (mi.getApplicableClass().isAssignableFrom(entityClass) ||
|
||||
|
||||
@@ -114,7 +114,7 @@ public enum PacketType {
|
||||
PLAY_UPDATE_HEALTH(State.PLAY, Direction.OUTGOING, 0x06, 0x3E),
|
||||
PLAY_SCOREBOARD_OBJ(State.PLAY, Direction.OUTGOING, 0x3B, 0x3F),
|
||||
PLAY_SET_PASSENGERS(State.PLAY, Direction.OUTGOING, -1, 0x40),
|
||||
PLAY_TEAMS(State.PLAY, Direction.OUTGOING, 0x3E, 0x41),
|
||||
PLAY_TEAM(State.PLAY, Direction.OUTGOING, 0x3E, 0x41),
|
||||
PLAY_UPDATE_SCORE(State.PLAY, Direction.OUTGOING, 0x3C, 0x42),
|
||||
PLAY_SPAWN_POSITION(State.PLAY, Direction.OUTGOING, 0x05, 0x43),
|
||||
PLAY_TIME_UPDATE(State.PLAY, Direction.OUTGOING, 0x03, 0x44),
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
package us.myles.ViaVersion.transformers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import net.minecraft.server.v1_8_R3.PacketDataSerializer;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.Core;
|
||||
import us.myles.ViaVersion.PacketUtil;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import us.myles.ViaVersion.*;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class IncomingTransformer {
|
||||
private static Gson gson = new Gson();
|
||||
private final Channel channel;
|
||||
private final ConnectionInfo info;
|
||||
private final ViaVersionInitializer init;
|
||||
@@ -36,21 +33,18 @@ public class IncomingTransformer {
|
||||
if (packet.getPacketID() != -1) {
|
||||
packetID = packet.getPacketID();
|
||||
}
|
||||
if (packet != PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST && packet != PacketType.PLAY_KEEP_ALIVE_REQUEST && packet != PacketType.PLAY_PLAYER_POSITION_REQUEST && packet != PacketType.PLAY_PLAYER_LOOK_REQUEST) {
|
||||
System.out.println("Packet Type: " + packet + " New ID: " + packetID + " Original: " + original);
|
||||
}
|
||||
// if (packet != PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST && packet != PacketType.PLAY_KEEP_ALIVE_REQUEST && packet != PacketType.PLAY_PLAYER_POSITION_REQUEST && packet != PacketType.PLAY_PLAYER_LOOK_REQUEST) {
|
||||
// System.out.println("Packet Type: " + packet + " New ID: " + packetID + " Original: " + original);
|
||||
// }
|
||||
if (packet == PacketType.PLAY_TP_CONFIRM) {
|
||||
System.out.println("Cancelling TP Confirm");
|
||||
throw new CancelException();
|
||||
}
|
||||
PacketUtil.writeVarInt(packetID, output);
|
||||
if (packet == PacketType.HANDSHAKE) {
|
||||
System.out.println("Readable Bytes: " + input.readableBytes());
|
||||
int protVer = PacketUtil.readVarInt(input);
|
||||
info.setProtocol(protVer);
|
||||
PacketUtil.writeVarInt(protVer <= 102 ? protVer : 47, output); // pretend to be older
|
||||
|
||||
System.out.println("Incoming prot ver: " + protVer);
|
||||
if (protVer <= 102) {
|
||||
// Not 1.9 remove pipes
|
||||
this.init.remove();
|
||||
@@ -72,7 +66,17 @@ public class IncomingTransformer {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(packet == PacketType.PLAY_TAB_COMPLETE_REQUEST){
|
||||
if (packet == PacketType.PLAY_UPDATE_SIGN_REQUEST) {
|
||||
Long location = input.readLong();
|
||||
output.writeLong(location);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
String line = PacketUtil.readString(input);
|
||||
line = "{\"text\":\"" + line + "\"}";
|
||||
PacketUtil.writeString(line, output);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_TAB_COMPLETE_REQUEST) {
|
||||
String text = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(text, output);
|
||||
input.readBoolean(); // assume command
|
||||
@@ -92,6 +96,39 @@ public class IncomingTransformer {
|
||||
output.writeByte(face);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_CLICK_WINDOW) {
|
||||
// if placed in new slot, reject :)
|
||||
int windowID = input.readUnsignedByte();
|
||||
short slot = input.readShort();
|
||||
|
||||
byte button = input.readByte();
|
||||
short action = input.readShort();
|
||||
byte mode = input.readByte();
|
||||
if (slot == 45 && windowID == 0) {
|
||||
try {
|
||||
Class<?> setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot");
|
||||
Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null);
|
||||
channel.writeAndFlush(setSlotPacket); // slot is empty
|
||||
slot = -999; // we're evil, they'll throw item on the ground
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
output.writeByte(windowID);
|
||||
output.writeShort(slot);
|
||||
output.writeByte(button);
|
||||
output.writeShort(action);
|
||||
output.writeByte(mode);
|
||||
output.writeBytes(input);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_CLIENT_SETTINGS) {
|
||||
String locale = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(locale, output);
|
||||
@@ -108,13 +145,11 @@ public class IncomingTransformer {
|
||||
short skinParts = input.readUnsignedByte();
|
||||
output.writeByte(skinParts);
|
||||
|
||||
int mainHand = PacketUtil.readVarInt(input);
|
||||
System.out.println("Main hand: " + mainHand);
|
||||
PacketUtil.readVarInt(input);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_ANIMATION_REQUEST) {
|
||||
int hand = PacketUtil.readVarInt(input);
|
||||
System.out.println("Animation request " + hand);
|
||||
PacketUtil.readVarInt(input);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_USE_ENTITY) {
|
||||
@@ -132,7 +167,7 @@ public class IncomingTransformer {
|
||||
output.writeFloat(targetZ);
|
||||
}
|
||||
if (type == 0 || type == 2) {
|
||||
int hand = PacketUtil.readVarInt(input); // lel
|
||||
PacketUtil.readVarInt(input);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -142,9 +177,23 @@ public class IncomingTransformer {
|
||||
int face = PacketUtil.readVarInt(input);
|
||||
output.writeByte(face);
|
||||
int hand = PacketUtil.readVarInt(input);
|
||||
System.out.println("hand: " + hand);
|
||||
// write item in hand
|
||||
output.writeShort(-1);
|
||||
|
||||
ItemStack inHand = Core.getHandItem(info);
|
||||
Object item = null;
|
||||
try {
|
||||
Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class);
|
||||
item = m.invoke(null, inHand);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
PacketUtil.writeItem(item, output);
|
||||
|
||||
short curX = input.readUnsignedByte();
|
||||
output.writeByte(curX);
|
||||
@@ -160,8 +209,6 @@ public class IncomingTransformer {
|
||||
// Simulate using item :)
|
||||
output.writeLong(-1L);
|
||||
output.writeByte(-1);
|
||||
int hand = PacketUtil.readVarInt(input);
|
||||
System.out.println("hand: " + hand);
|
||||
// write item in hand
|
||||
output.writeShort(-1);
|
||||
|
||||
|
||||
@@ -4,16 +4,11 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import net.minecraft.server.v1_8_R3.*;
|
||||
import net.minecraft.server.v1_8_R3.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||
import org.spacehq.mc.protocol.util.NetUtil;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.Core;
|
||||
import us.myles.ViaVersion.PacketUtil;
|
||||
import us.myles.ViaVersion.*;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
import us.myles.ViaVersion.metadata.MetaIndex;
|
||||
import us.myles.ViaVersion.metadata.NewType;
|
||||
@@ -22,6 +17,9 @@ import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
public class OutgoingTransformer {
|
||||
@@ -48,8 +46,8 @@ public class OutgoingTransformer {
|
||||
if (packet == null) {
|
||||
throw new RuntimeException("Outgoing Packet not found? " + packetID + " State: " + info.getState() + " Version: " + info.getProtocol());
|
||||
}
|
||||
if (packet != PacketType.PLAY_CHUNK_DATA && packet != PacketType.PLAY_KEEP_ALIVE && packet != PacketType.PLAY_TIME_UPDATE)
|
||||
System.out.println("Packet Type: " + packet + " Original ID: " + packetID + " State:" + info.getState());
|
||||
// if (packet != PacketType.PLAY_CHUNK_DATA && packet != PacketType.PLAY_KEEP_ALIVE && packet != PacketType.PLAY_TIME_UPDATE && (!packet.name().toLowerCase().contains("move") && !packet.name().contains("look")))
|
||||
// System.out.println("Packet Type: " + packet + " Original ID: " + packetID + " State:" + info.getState());
|
||||
if (packet.getPacketID() != -1) {
|
||||
packetID = packet.getNewPacketID();
|
||||
}
|
||||
@@ -58,7 +56,6 @@ public class OutgoingTransformer {
|
||||
PacketUtil.writeVarInt(packetID, output);
|
||||
if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) {
|
||||
// TODO: Port this over
|
||||
System.out.println("cancelling");
|
||||
throw new CancelException();
|
||||
}
|
||||
if (packet == PacketType.PLAY_ATTACH_ENTITY) {
|
||||
@@ -94,11 +91,11 @@ public class OutgoingTransformer {
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
int x = input.readByte();
|
||||
output.writeShort(x);
|
||||
output.writeShort(x * 128);
|
||||
int y = input.readByte();
|
||||
output.writeShort(y);
|
||||
output.writeShort(y * 128);
|
||||
int z = input.readByte();
|
||||
output.writeShort(z);
|
||||
output.writeShort(z * 128);
|
||||
|
||||
byte yaw = input.readByte();
|
||||
output.writeByte(yaw);
|
||||
@@ -113,12 +110,12 @@ public class OutgoingTransformer {
|
||||
if (packet == PacketType.PLAY_ENTITY_RELATIVE_MOVE) {
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
int x = input.readByte();
|
||||
output.writeShort(x);
|
||||
int y = input.readByte();
|
||||
output.writeShort(y);
|
||||
int z = input.readByte();
|
||||
output.writeShort(z);
|
||||
short x = (short) (input.readByte());
|
||||
output.writeShort(x * 128);
|
||||
short y = (short) (input.readByte());
|
||||
output.writeShort(y * 128);
|
||||
short z = (short) (input.readByte());
|
||||
output.writeShort(z * 128);
|
||||
|
||||
boolean onGround = input.readBoolean();
|
||||
output.writeBoolean(onGround);
|
||||
@@ -172,7 +169,7 @@ public class OutgoingTransformer {
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
|
||||
try {
|
||||
List dw = Core.getPrivateField(info.getLastPacket(), "b", List.class);
|
||||
List dw = ReflectionUtil.get(info.getLastPacket(), "b", List.class);
|
||||
// get entity via entityID, not preferred but we need it.
|
||||
Entity entity = Core.getEntity(info.getUUID(), id);
|
||||
if (entity != null) {
|
||||
@@ -263,15 +260,28 @@ public class OutgoingTransformer {
|
||||
short vZ = input.readShort();
|
||||
output.writeShort(vZ);
|
||||
try {
|
||||
DataWatcher dw = Core.getPrivateField(info.getLastPacket(), "l", DataWatcher.class);
|
||||
transformMetadata(dw, output);
|
||||
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "l", ReflectionUtil.nms("DataWatcher"));
|
||||
transformMetadata(dataWatcher, output);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(packet == PacketType.PLAY_UPDATE_SIGN){
|
||||
Long location = input.readLong();
|
||||
output.writeLong(location);
|
||||
for(int i = 0;i<4;i++){
|
||||
String line = PacketUtil.readString(input);
|
||||
if(line.startsWith("\"")){
|
||||
line = "{\"text\":" + line + "}";
|
||||
}
|
||||
PacketUtil.writeString(line, output);
|
||||
}
|
||||
}
|
||||
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
@@ -290,22 +300,46 @@ public class OutgoingTransformer {
|
||||
output.writeByte(pitch);
|
||||
byte yaw = input.readByte();
|
||||
output.writeByte(yaw);
|
||||
// We don't use currentItem short lel
|
||||
|
||||
// transform entity meta data ugh
|
||||
// get data watcher
|
||||
try {
|
||||
System.out.println("Last Packet Type: " + info.getLastPacket().getClass().getName());
|
||||
DataWatcher dw = Core.getPrivateField(info.getLastPacket(), "i", DataWatcher.class);
|
||||
transformMetadata(dw, output);
|
||||
Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "i", ReflectionUtil.nms("DataWatcher"));
|
||||
transformMetadata(dataWatcher, output);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if(packet == PacketType.PLAY_MAP) {
|
||||
int damage = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(damage, output);
|
||||
byte scale = input.readByte();
|
||||
output.writeByte(scale);
|
||||
output.writeBoolean(true);
|
||||
output.writeBytes(input);
|
||||
return;
|
||||
}
|
||||
if(packet == PacketType.PLAY_TEAM) {
|
||||
String teamName = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(teamName, output);
|
||||
byte mode = input.readByte();
|
||||
output.writeByte(mode);
|
||||
if(mode == 0 || mode == 2){
|
||||
PacketUtil.writeString(PacketUtil.readString(input), output);
|
||||
PacketUtil.writeString(PacketUtil.readString(input), output);
|
||||
PacketUtil.writeString(PacketUtil.readString(input), output);
|
||||
|
||||
output.writeByte(input.readByte());
|
||||
PacketUtil.writeString(PacketUtil.readString(input), output);
|
||||
|
||||
PacketUtil.writeString("", output); // collission rule :)
|
||||
}
|
||||
output.writeBytes(input);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_CHUNK_DATA) {
|
||||
// We need to catch unloading chunk packets as defined by wiki.vg
|
||||
// To unload chunks, send this packet with Ground-Up Continuous=true and no 16^3 chunks (eg. Primary Bit Mask=0)
|
||||
@@ -332,10 +366,9 @@ public class OutgoingTransformer {
|
||||
byte[] data = new byte[size];
|
||||
input.readBytes(data);
|
||||
boolean sk = false;
|
||||
if (info.getLastPacket() instanceof PacketPlayOutMapChunkBulk) {
|
||||
|
||||
if (info.getLastPacket().getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
||||
try {
|
||||
sk = Core.getPrivateField(info.getLastPacket(), "d", boolean.class);
|
||||
sk = ReflectionUtil.get(info.getLastPacket(), "d", boolean.class);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
@@ -358,115 +391,124 @@ public class OutgoingTransformer {
|
||||
output.writeBytes(input);
|
||||
}
|
||||
|
||||
private void transformMetadata(DataWatcher dw, ByteBuf output) {
|
||||
private void transformMetadata(Object dw, ByteBuf output) {
|
||||
// get entity
|
||||
try {
|
||||
transformMetadata(Core.getPrivateField(dw, "a", Entity.class), dw.b(), output);
|
||||
Class<?> nmsClass = ReflectionUtil.nms("Entity");
|
||||
Object nmsEntity = ReflectionUtil.get(dw, "a", nmsClass);
|
||||
Class<?> craftClass = ReflectionUtil.obc("entity.CraftEntity");
|
||||
Method bukkitMethod = craftClass.getDeclaredMethod("getEntity", ReflectionUtil.obc("CraftServer"), nmsClass);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private void transformMetadata(Entity entity, List<DataWatcher.WatchableObject> dw, ByteBuf output) {
|
||||
PacketDataSerializer packetdataserializer = new PacketDataSerializer(output);
|
||||
private void transformMetadata(Entity entity, List dw, ByteBuf output) {
|
||||
try {
|
||||
if (dw != null) {
|
||||
short id = -1;
|
||||
int data = -1;
|
||||
|
||||
if (dw != null) {
|
||||
short id = -1;
|
||||
int data = -1;
|
||||
|
||||
Iterator<DataWatcher.WatchableObject> iterator = dw.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
DataWatcher.WatchableObject obj = iterator.next();
|
||||
MetaIndex metaIndex = MetaIndex.getIndex(entity, obj.a());
|
||||
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());
|
||||
}
|
||||
switch (metaIndex.getNewType()) {
|
||||
case Byte:
|
||||
// convert from int, byte
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
packetdataserializer.writeByte(((Byte) obj.b()).byteValue());
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
packetdataserializer.writeByte(((Integer) obj.b()).byteValue());
|
||||
}
|
||||
break;
|
||||
case OptUUID:
|
||||
String owner = (String) obj.b();
|
||||
UUID toWrite = null;
|
||||
if (owner.length() != 0) {
|
||||
try {
|
||||
toWrite = UUID.fromString(owner);
|
||||
} catch (Exception ignored) {
|
||||
Iterator iterator = dw.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object watchableObj = iterator.next(); //
|
||||
MetaIndex metaIndex = MetaIndex.getIndex(entity, (int) ReflectionUtil.invoke(watchableObj, "a"));
|
||||
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());
|
||||
}
|
||||
}
|
||||
packetdataserializer.writeBoolean(toWrite != null);
|
||||
if (toWrite != null)
|
||||
packetdataserializer.a(toWrite);
|
||||
break;
|
||||
case BlockID:
|
||||
// if we have both sources :))
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
data = ((Byte) obj.b()).byteValue();
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Short) {
|
||||
id = ((Short) obj.b()).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) obj.b()).intValue(), output);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Short) {
|
||||
PacketUtil.writeVarInt(((Short) obj.b()).intValue(), output);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
PacketUtil.writeVarInt(((Integer) obj.b()).intValue(), output);
|
||||
}
|
||||
break;
|
||||
case Float:
|
||||
packetdataserializer.writeFloat(((Float) obj.b()).floatValue());
|
||||
break;
|
||||
case String:
|
||||
packetdataserializer.a((String) obj.b());
|
||||
break;
|
||||
case Boolean:
|
||||
packetdataserializer.writeBoolean(((Byte) obj.b()).byteValue() != 0);
|
||||
break;
|
||||
case Slot:
|
||||
ItemStack itemstack = (ItemStack) obj.b();
|
||||
packetdataserializer.a(itemstack);
|
||||
break;
|
||||
case Position:
|
||||
BlockPosition blockposition = (BlockPosition) obj.b();
|
||||
|
||||
packetdataserializer.writeInt(blockposition.getX());
|
||||
packetdataserializer.writeInt(blockposition.getY());
|
||||
packetdataserializer.writeInt(blockposition.getZ());
|
||||
break;
|
||||
case Vector3F:
|
||||
Vector3f vector3f = (Vector3f) obj.b();
|
||||
|
||||
packetdataserializer.writeFloat(vector3f.getX());
|
||||
packetdataserializer.writeFloat(vector3f.getY());
|
||||
packetdataserializer.writeFloat(vector3f.getZ());
|
||||
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) value, 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output.writeByte(255);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
output.writeByte(255);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
Howdy Partner:
|
||||
|
||||
[-] Fix arrows going down.
|
||||
[-] Make teleport relative if not 1024 diff.
|
||||
[-] Fix boats / minecarts if possible otherwise tell them nicely they don't work.
|
||||
[-] Cry because how long it took to get this far haha.
|
||||
[-] Clean up so we're not using NMS and more reflection based :)
|
||||
@@ -1,4 +1,4 @@
|
||||
name: ViaVersion
|
||||
main: us.myles.ViaVersion.Core
|
||||
author: _MylesC
|
||||
version: 0.1
|
||||
version: 0.3
|
||||
Reference in new issue
Block a user