Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d2189cb8e | ||
|
|
44a044848f | ||
|
|
4f133366e1 | ||
|
|
7c66507960 | ||
|
|
e436988303 | ||
|
|
8b65efc4bd | ||
|
|
ae2592f3d1 | ||
|
|
c009827622 | ||
|
|
afe18379e0 | ||
|
|
5ac61fa71d | ||
|
|
3511f9a7b2 | ||
|
|
e3f7493616 | ||
|
|
a769599448 | ||
|
|
5df8a1e8e7 | ||
|
|
a3d124b552 | ||
|
|
6d95af3963 | ||
|
|
c08a38ff21 | ||
|
|
4a804bae7c | ||
|
|
fd42c7eba6 | ||
|
|
1af86d1354 | ||
|
|
1b77e1c89f | ||
|
|
c42b9351b8 | ||
|
|
ab8c8cd7c8 | ||
|
|
83518b92fe | ||
|
|
1bf1e8981c | ||
|
|
b1f076936d | ||
|
|
319e793cd6 | ||
|
|
c9e1360951 | ||
|
|
c11ad7930f | ||
|
|
7b36a9de51 | ||
|
|
d5e9170109 | ||
|
|
47b447f195 | ||
|
|
e2d27df1a8 | ||
|
|
9a781d940f | ||
|
|
fed36c9487 | ||
|
|
463327c2a7 | ||
|
|
b0050d4a8d | ||
|
|
a743895c3f | ||
|
|
f65666b7a6 | ||
|
|
3a217503fc | ||
|
|
c71b75e13b | ||
|
|
0c890c3a30 | ||
|
|
f38bb0ca22 | ||
|
|
2144fa1837 | ||
|
|
fc4a617da7 | ||
|
|
126ee6e03f | ||
|
|
498867032f | ||
|
|
aef8eca790 | ||
|
|
d8bbb51721 | ||
|
|
6e4fd5a63b | ||
|
|
e7bd6245c2 | ||
|
|
6242cb0b47 | ||
|
|
bf5072e1cf | ||
|
|
c24b673550 | ||
|
|
dfc1e1cf51 | ||
|
|
c600ab6dd1 | ||
|
|
29754c0787 | ||
|
|
9144980474 | ||
|
|
6a449f8db1 | ||
|
|
3780210f76 | ||
|
|
67bad0dec8 | ||
|
|
79291fd7d9 | ||
|
|
ee9d8d6365 | ||
|
|
cc354db464 | ||
|
|
2b29d27b0d | ||
|
|
75db62ee6f | ||
|
|
7cd7b5ba24 | ||
|
|
a3bfcefdca | ||
|
|
098b787355 | ||
|
|
abb9665813 | ||
|
|
012eec47b5 | ||
|
|
2d40afa3a1 | ||
|
|
a92a7d6e02 | ||
|
|
fc7cdb4244 | ||
|
|
d5b375b264 | ||
|
|
99eadbe343 | ||
|
|
38d33dba16 | ||
|
|
b3de4de0cd | ||
|
|
6b5c166a22 | ||
|
|
1aea00036f | ||
|
|
1262847a0e | ||
|
|
21cfb1297f | ||
|
|
0b1cb61986 | ||
|
|
44907b3e8d |
No files matched your search
@@ -1,4 +1,4 @@
|
||||
# ViaVersion 0.5.6
|
||||
# ViaVersion 0.6.4
|
||||
[](https://travis-ci.org/MylesIsCool/ViaVersion)
|
||||
**Allows the connection of 1.9 clients to 1.8**
|
||||
|
||||
@@ -9,6 +9,8 @@ This plugin modifies netty to allow connection of 1.9 clients to 1.8,
|
||||
|
||||
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
||||
|
||||
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
|
||||
|
||||
|
||||
Sources:
|
||||
--------
|
||||
@@ -38,9 +40,25 @@ Contributors:
|
||||
|
||||
**fillefilip8**
|
||||
|
||||
**Chat:** https://gitter.im/MylesIsCool/ViaVersion
|
||||
**lenis0012**
|
||||
|
||||
We use it to collaborate and solve errors, sign in with github.
|
||||
|
||||
|
||||
Releases / Dev Builds:
|
||||
--------
|
||||
You can find official releases here:
|
||||
|
||||
https://www.spigotmc.org/resources/viaversion.19254/history
|
||||
|
||||
|
||||
----------
|
||||
|
||||
You can find unofficial dev builds here:
|
||||
|
||||
**fillefilip8's Build Server:** http://build.fillecraft.com:8080/job/ViaVersion
|
||||
|
||||
**sgdc3's Build Server:** http://ci.xephi.fr/job/ViaVersion/
|
||||
**sgdc3's Maven:** http://ci.xephi.fr/plugin/repository/everything/
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion</artifactId>
|
||||
<version>0.5.7-SNAPSHOT</version>
|
||||
<version>0.6.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ViaVersion</name>
|
||||
@@ -53,13 +53,13 @@
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<!--
|
||||
<!--
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
-->
|
||||
-->
|
||||
|
||||
<plugins>
|
||||
<!-- Maven Java Compiler -->
|
||||
@@ -70,10 +70,10 @@
|
||||
<configuration>
|
||||
<source>${jdkVersion}</source>
|
||||
<target>${jdkVersion}</target>
|
||||
<!--
|
||||
<!--
|
||||
<testSource>${testJreVersion}</testSource>
|
||||
<testTarget>${testJreVersion}</testTarget>
|
||||
-->
|
||||
-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Unit Test Plugin
|
||||
@@ -96,12 +96,12 @@
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.spacehq.opennbt</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.opennbt</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.spacehq.opennbt</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.opennbt</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -122,7 +122,7 @@
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
|
||||
<!-- SpaceHQ Repo-->
|
||||
<!-- SpaceHQ Repo-->
|
||||
<repository>
|
||||
<id>spacehq-repo</id>
|
||||
<url>https://repo.spacehq.org/content/repositories/releases/</url>
|
||||
@@ -153,16 +153,16 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- NBT Edit Library -->
|
||||
<!-- NBT Edit Library -->
|
||||
<dependency>
|
||||
<groupId>org.spacehq</groupId>
|
||||
<artifactId>opennbt</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Netty (Network Library) -->
|
||||
<!-- Netty (Network Library) -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
@@ -170,5 +170,12 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.16.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -17,7 +17,7 @@ public class BlockStorage {
|
||||
public BlockStorage() {
|
||||
this.bitsPerEntry = 4;
|
||||
|
||||
this.states = new ArrayList<Integer>();
|
||||
this.states = new ArrayList<>();
|
||||
this.states.add(0);
|
||||
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, 4096);
|
||||
@@ -26,7 +26,7 @@ public class BlockStorage {
|
||||
public BlockStorage(ByteBuf in) throws IOException {
|
||||
this.bitsPerEntry = in.readUnsignedByte();
|
||||
|
||||
this.states = new ArrayList<Integer>();
|
||||
this.states = new ArrayList<>();
|
||||
int stateCount = PacketUtil.readVarInt(in);
|
||||
for (int i = 0; i < stateCount; i++) {
|
||||
this.states.add(PacketUtil.readVarInt(in));
|
||||
@@ -35,6 +35,10 @@ public class BlockStorage {
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, PacketUtil.readLongs(PacketUtil.readVarInt(in), in));
|
||||
}
|
||||
|
||||
private static int index(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
}
|
||||
|
||||
public void write(ByteBuf out) throws IOException {
|
||||
out.writeByte(this.bitsPerEntry);
|
||||
|
||||
@@ -78,7 +82,7 @@ public class BlockStorage {
|
||||
|
||||
List<Integer> oldStates = this.states;
|
||||
if (this.bitsPerEntry > 8) {
|
||||
oldStates = new ArrayList<Integer>(this.states);
|
||||
oldStates = new ArrayList<>(this.states);
|
||||
this.states.clear();
|
||||
this.bitsPerEntry = 13;
|
||||
}
|
||||
@@ -107,10 +111,6 @@ public class BlockStorage {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int index(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof BlockStorage && this.bitsPerEntry == ((BlockStorage) o).bitsPerEntry && this.states.equals(((BlockStorage) o).states) && this.storage.equals(((BlockStorage) o).storage));
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class Column {
|
||||
private int x;
|
||||
private int z;
|
||||
@@ -23,9 +28,9 @@ public class Column {
|
||||
|
||||
this.skylight = false;
|
||||
boolean noSkylight = false;
|
||||
for(int index = 0; index < chunks.length; index++) {
|
||||
if(chunks[index] != null) {
|
||||
if(chunks[index].getSkyLight() == null) {
|
||||
for (Chunk chunk : chunks) {
|
||||
if (chunk != null) {
|
||||
if (chunk.getSkyLight() == null) {
|
||||
noSkylight = true;
|
||||
} else {
|
||||
this.skylight = true;
|
||||
@@ -43,26 +48,11 @@ public class Column {
|
||||
this.biomeData = biomeData;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public Chunk[] getChunks() {
|
||||
return this.chunks;
|
||||
}
|
||||
|
||||
public boolean hasBiomeData() {
|
||||
return this.biomeData != null;
|
||||
}
|
||||
|
||||
public byte[] getBiomeData() {
|
||||
return this.biomeData;
|
||||
}
|
||||
|
||||
public boolean hasSkylight() {
|
||||
return this.skylight;
|
||||
}
|
||||
|
||||
@@ -18,19 +18,19 @@ public class NetUtil {
|
||||
public static int writeNewColumn(ByteBuf out, Column column, boolean fullChunk, boolean hasSkylight) throws IOException {
|
||||
int mask = 0;
|
||||
Chunk chunks[] = column.getChunks();
|
||||
for(int index = 0; index < chunks.length; index++) {
|
||||
for (int index = 0; index < chunks.length; index++) {
|
||||
Chunk chunk = chunks[index];
|
||||
if(chunk != null && (!fullChunk || !chunk.isEmpty())) {
|
||||
if (chunk != null && (!fullChunk || !chunk.isEmpty())) {
|
||||
mask |= 1 << index;
|
||||
chunk.getBlocks().write(out);
|
||||
chunk.getBlockLight().write(out);
|
||||
if(hasSkylight || column.hasSkylight()) {
|
||||
if (hasSkylight || column.hasSkylight()) {
|
||||
chunk.getSkyLight().write(out); // TODO: Make a PR to original lib to correct this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fullChunk && column.getBiomeData() != null) {
|
||||
if (fullChunk && column.getBiomeData() != null) {
|
||||
out.writeBytes(column.getBiomeData());
|
||||
}
|
||||
|
||||
@@ -47,45 +47,47 @@ public class NetUtil {
|
||||
// 2 = Get block light.
|
||||
// 3 = Get sky light.
|
||||
Chunk[] chunks = new Chunk[16];
|
||||
|
||||
for(int pass = 0; pass < 4; pass++) {
|
||||
for(int ind = 0; ind < 16; ind++) {
|
||||
if((bitmask & 1 << ind) != 0) {
|
||||
if(pass == 0) {
|
||||
int chunkCount = 0;
|
||||
for (int pass = 0; pass < 4; pass++) {
|
||||
if(pass == 1){
|
||||
if(chunkCount == 0) return null;
|
||||
}
|
||||
for (int ind = 0; ind < 16; ind++) {
|
||||
if ((bitmask & 1 << ind) != 0) {
|
||||
if (pass == 0) {
|
||||
chunkCount++;
|
||||
// Block length + Blocklight length
|
||||
expected += (4096 * 2) + 2048;
|
||||
}
|
||||
|
||||
if(pass == 1) {
|
||||
if (pass == 1) {
|
||||
chunks[ind] = new Chunk(sky || hasSkyLight);
|
||||
|
||||
buf.position(pos / 2);
|
||||
short[] tempData = new short[4096];
|
||||
buf.get(tempData, 0, tempData.length);
|
||||
int buffPos = buf.position();
|
||||
// convert short array to new one
|
||||
int index = 0;
|
||||
for(short ss:tempData){
|
||||
|
||||
for (int index = 0; index < 4096; index++) {
|
||||
short ss = buf.get(buffPos + index);
|
||||
// s is 16 bits, 12 bits id and 4 bits data
|
||||
int data = ss & 0xF;
|
||||
int id = (ss >> 4) << 4 | data;
|
||||
|
||||
int newCombined = id ; // test
|
||||
int newCombined = id; // test
|
||||
|
||||
chunks[ind].getBlocks().set(index, newCombined);
|
||||
index++;
|
||||
}
|
||||
pos += tempData.length * 2;
|
||||
pos += 4096 * 2;
|
||||
|
||||
}
|
||||
|
||||
if(pass == 2) {
|
||||
if (pass == 2) {
|
||||
NibbleArray3d blocklight = chunks[ind].getBlockLight();
|
||||
System.arraycopy(input, pos, blocklight.getData(), 0, blocklight.getData().length);
|
||||
pos += blocklight.getData().length;
|
||||
}
|
||||
|
||||
if(pass == 3) {
|
||||
if(sky) {
|
||||
if (pass == 3) {
|
||||
if (sky) {
|
||||
NibbleArray3d skylight = chunks[ind].getSkyLight();
|
||||
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
|
||||
pos += skylight.getData().length;
|
||||
@@ -94,16 +96,16 @@ public class NetUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if(pass == 0) {
|
||||
if (pass == 0) {
|
||||
// If we have more data than blocks and blocklight combined, there must be skylight data as well.
|
||||
if(input.length > expected) {
|
||||
if (input.length > expected) {
|
||||
sky = checkForSky;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte biomeData[] = null;
|
||||
if(isFullChunk && (pos + 256 <= input.length)) {
|
||||
if (isFullChunk && (pos + 256 <= input.length)) {
|
||||
|
||||
biomeData = new byte[256];
|
||||
System.arraycopy(input, pos, biomeData, 0, biomeData.length);
|
||||
|
||||
@@ -3,110 +3,71 @@ package us.myles.ViaVersion;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.chunks.ChunkManager;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ConnectionInfo {
|
||||
private static final long IDLE_PACKET_DELAY = 50L; // Update every 50ms (20tps)
|
||||
private static final long IDLE_PACKET_LIMIT = 20; // Max 20 ticks behind
|
||||
|
||||
private final SocketChannel channel;
|
||||
private final ChunkManager chunkManager;
|
||||
private Object lastPacket;
|
||||
private java.util.UUID UUID;
|
||||
private State state = State.HANDSHAKE;
|
||||
private String openWindow;
|
||||
private int protocol = 0;
|
||||
private int compression = 0;
|
||||
private int entityID;
|
||||
private boolean active = true;
|
||||
private String username;
|
||||
private long nextIdlePacket = 0L;
|
||||
|
||||
public ConnectionInfo(SocketChannel socketChannel) {
|
||||
this.channel = socketChannel;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(int protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setCompression(int compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public int getCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
public void setLastPacket(Object lastPacket) {
|
||||
this.lastPacket = lastPacket;
|
||||
}
|
||||
|
||||
public Object getLastPacket() {
|
||||
return lastPacket;
|
||||
}
|
||||
|
||||
public void setUUID(UUID UUID) {
|
||||
this.UUID = UUID;
|
||||
}
|
||||
|
||||
public java.util.UUID getUUID() {
|
||||
return UUID;
|
||||
this.chunkManager = new ChunkManager(this);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
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;
|
||||
this.nextIdlePacket = System.currentTimeMillis() + IDLE_PACKET_DELAY; // Update every 50 ticks
|
||||
}
|
||||
|
||||
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
if (currentThread) {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
} else {
|
||||
channel.eventLoop().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void sendRawPacket(final ByteBuf packet) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
channel.eventLoop().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getOpenWindow() {
|
||||
return openWindow;
|
||||
}
|
||||
|
||||
public void setOpenWindow(String openWindow) {
|
||||
this.openWindow = openWindow;
|
||||
sendRawPacket(packet, false);
|
||||
}
|
||||
|
||||
public void closeWindow() {
|
||||
this.openWindow = null;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
public void incrementIdlePacket() {
|
||||
// Notify of next update
|
||||
// Allow a maximum lag spike of 1 second (20 ticks/updates)
|
||||
this.nextIdlePacket = Math.max(nextIdlePacket + IDLE_PACKET_DELAY, System.currentTimeMillis() - IDLE_PACKET_DELAY * IDLE_PACKET_LIMIT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ViaIdleThread extends BukkitRunnable {
|
||||
private final Map<UUID, ConnectionInfo> portedPlayers;
|
||||
private final Class<?> idlePacketClass;
|
||||
|
||||
public ViaIdleThread(Map<UUID, ConnectionInfo> portedPlayers) {
|
||||
this.portedPlayers = portedPlayers;
|
||||
try {
|
||||
this.idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying");
|
||||
} catch(ClassNotFoundException e) {
|
||||
throw new RuntimeException("Couldn't find player idle packet, help!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for(ConnectionInfo info : portedPlayers.values()) {
|
||||
long nextIdleUpdate = info.getNextIdlePacket();
|
||||
if(nextIdleUpdate <= System.currentTimeMillis()) {
|
||||
try {
|
||||
Object packet = idlePacketClass.newInstance();
|
||||
info.getChannel().pipeline().fireChannelRead(packet);
|
||||
} catch(InstantiationException | IllegalAccessException e) {
|
||||
System.out.println("Failed to create idle packet.");
|
||||
if(ViaVersion.getInstance().isDebug()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
info.incrementIdlePacket();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -25,10 +24,13 @@ import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
import us.myles.ViaVersion.listeners.CommandBlockListener;
|
||||
import us.myles.ViaVersion.update.UpdateListener;
|
||||
import us.myles.ViaVersion.update.UpdateUtil;
|
||||
import us.myles.ViaVersion.util.Configuration;
|
||||
import us.myles.ViaVersion.util.ListWrapper;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -39,13 +41,32 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
|
||||
private final Map<UUID, ConnectionInfo> portedPlayers = new ConcurrentHashMap<UUID, ConnectionInfo>();
|
||||
private final Map<UUID, ConnectionInfo> portedPlayers = new ConcurrentHashMap<>();
|
||||
private boolean debug = false;
|
||||
|
||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<ItemStack>() {
|
||||
@Override
|
||||
public ItemStack call() throws Exception {
|
||||
if (info.getPlayer() != null) {
|
||||
return info.getPlayer().getItemInHand();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error fetching hand item: " + e.getClass().getName());
|
||||
if (ViaVersion.getInstance().isDebug())
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
ViaVersion.setInstance(this);
|
||||
saveDefaultConfig();
|
||||
generateConfig();
|
||||
if (System.getProperty("ViaVersion") != null) {
|
||||
getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update.");
|
||||
getLogger().severe("Some features may not work.");
|
||||
@@ -53,13 +74,10 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
}
|
||||
|
||||
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
try {
|
||||
injectPacketHandler();
|
||||
System.setProperty("ViaVersion", getDescription().getVersion());
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
injectPacketHandler();
|
||||
if (getConfig().getBoolean("simulate-pt", true))
|
||||
new ViaIdleThread(portedPlayers).runTaskTimerAsynchronously(this, 1L, 1L); // Updates player's idle status
|
||||
|
||||
if (getConfig().getBoolean("checkforupdates"))
|
||||
UpdateUtil.sendUpdateMessage(this);
|
||||
|
||||
@@ -77,39 +95,98 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
|
||||
}
|
||||
|
||||
public void injectPacketHandler() throws Exception {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = serverClazz.getDeclaredMethod("getServerConnection").invoke(server);
|
||||
// loop through all fields checking if list
|
||||
boolean injected = false;
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
ChannelFuture future = (ChannelFuture) o;
|
||||
ChannelPipeline pipeline = future.channel().pipeline();
|
||||
ChannelHandler bootstrapAcceptor = pipeline.first();
|
||||
try {
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
public void generateConfig() {
|
||||
File file = new File(getDataFolder(), "config.yml");
|
||||
if (file.exists()) {
|
||||
// Update config options
|
||||
Configuration oldConfig = new Configuration(file);
|
||||
oldConfig.reload(false); // Load current options from config
|
||||
file.delete(); // Delete old config
|
||||
saveDefaultConfig(); // Generate new config
|
||||
Configuration newConfig = new Configuration(file);
|
||||
newConfig.reload(true); // Load default options
|
||||
for (String key : oldConfig.getKeys(false)) {
|
||||
// Set option in new config if exists
|
||||
if (newConfig.contains(key)) {
|
||||
newConfig.set(key, oldConfig.get(key));
|
||||
}
|
||||
}
|
||||
newConfig.save();
|
||||
} else {
|
||||
saveDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// field not found
|
||||
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName());
|
||||
public void injectPacketHandler() {
|
||||
try {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = null;
|
||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
injected = true;
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connection == null) {
|
||||
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
|
||||
return;
|
||||
}
|
||||
if (connection != null) {
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
List wrapper = new ListWrapper((List) value) {
|
||||
@Override
|
||||
public synchronized void handleAdd(Object o) {
|
||||
synchronized (this) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
field.set(connection, wrapper);
|
||||
// Iterate through current list
|
||||
synchronized (wrapper) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.setProperty("ViaVersion", getDescription().getVersion());
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!injected) {
|
||||
throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)");
|
||||
}
|
||||
|
||||
private void inject(ChannelFuture future) {
|
||||
try {
|
||||
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
|
||||
try {
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// field not found
|
||||
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Have you got late-bind enabled with something else? (ProtocolLib?)");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,23 +231,42 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSyncedChunks() {
|
||||
return getConfig().getBoolean("sync-chunks", true);
|
||||
public void setDebug(boolean value) {
|
||||
this.debug = value;
|
||||
}
|
||||
|
||||
public boolean isPreventCollision() {
|
||||
return getConfig().getBoolean("prevent-collision", true);
|
||||
}
|
||||
|
||||
public boolean isAutoTeam() {
|
||||
// Collision has to be enabled first
|
||||
if(!isPreventCollision()) return false;
|
||||
return getConfig().getBoolean("auto-team", true);
|
||||
public boolean isSuppressMetadataErrors() {
|
||||
return getConfig().getBoolean("suppress-metadata-errors", false);
|
||||
}
|
||||
|
||||
public void setDebug(boolean value) {
|
||||
this.debug = value;
|
||||
public boolean isShieldBlocking() {
|
||||
return getConfig().getBoolean("shield-blocking", true);
|
||||
}
|
||||
|
||||
public boolean isHologramPatch() {
|
||||
return getConfig().getBoolean("hologram-patch", false);
|
||||
}
|
||||
|
||||
public boolean isBossbarPatch() {
|
||||
return getConfig().getBoolean("bossbar-patch", true);
|
||||
}
|
||||
|
||||
public boolean isBossbarAntiflicker() {
|
||||
return getConfig().getBoolean("bossbar-anti-flicker", false);
|
||||
}
|
||||
|
||||
public double getHologramYOffset() {
|
||||
return getConfig().getDouble("hologram-y", -1D);
|
||||
}
|
||||
|
||||
public boolean isAutoTeam() {
|
||||
// Collision has to be enabled first
|
||||
if (!isPreventCollision()) return false;
|
||||
return getConfig().getBoolean("auto-team", true);
|
||||
}
|
||||
|
||||
public void addPortedClient(ConnectionInfo info) {
|
||||
@@ -181,24 +277,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
portedPlayers.remove(clientID);
|
||||
}
|
||||
|
||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<ItemStack>() {
|
||||
@Override
|
||||
public ItemStack call() throws Exception {
|
||||
if (info.getPlayer() != null) {
|
||||
return info.getPlayer().getItemInHand();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error fetching hand item ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void run(final Runnable runnable, boolean wait) {
|
||||
try {
|
||||
Future f = Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<Boolean>() {
|
||||
@@ -212,8 +290,9 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
f.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to run task.");
|
||||
e.printStackTrace();
|
||||
System.out.println("Failed to run task: " + e.getClass().getName());
|
||||
if (ViaVersion.getInstance().isDebug())
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
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;
|
||||
}
|
||||
@Getter
|
||||
@Setter
|
||||
private static ViaVersionAPI instance;
|
||||
}
|
||||
@@ -77,11 +77,4 @@ public interface ViaVersionAPI {
|
||||
* @return true if debug is enabled
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Obtains if syncing chunks is on
|
||||
*
|
||||
* @return true if it is
|
||||
*/
|
||||
boolean isSyncedChunks();
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossColor {
|
||||
PINK(0),
|
||||
BLUE(1),
|
||||
@@ -10,12 +15,4 @@ public enum BossColor {
|
||||
WHITE(6);
|
||||
|
||||
private final int id;
|
||||
|
||||
BossColor(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossFlag {
|
||||
DARKEN_SKY(1),
|
||||
PLAY_BOSS_MUSIC(2);
|
||||
|
||||
private final int id;
|
||||
|
||||
BossFlag(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossStyle {
|
||||
SOLID(0),
|
||||
SEGMENTED_6(1),
|
||||
@@ -8,12 +13,4 @@ public enum BossStyle {
|
||||
SEGMENTED_20(4);
|
||||
|
||||
private final int id;
|
||||
|
||||
BossStyle(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package us.myles.ViaVersion.armor;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -21,14 +22,27 @@ import java.util.UUID;
|
||||
|
||||
import static us.myles.ViaVersion.util.PacketUtil.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ArmorListener implements Listener {
|
||||
|
||||
private static final UUID ARMOR_ATTRIBUTE = UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150");
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
private static UUID armorAttribute = UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150");
|
||||
public static void sendArmorUpdate(Player player) {
|
||||
int armor = ArmorType.calculateArmorPoints(player.getInventory().getArmorContents());
|
||||
|
||||
public ArmorListener(ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
writeVarInt(PacketType.PLAY_ENTITY_PROPERTIES.getNewPacketID(), buf);
|
||||
writeVarInt(player.getEntityId(), buf);
|
||||
buf.writeInt(1); // only 1 property
|
||||
writeString("generic.armor", buf);
|
||||
buf.writeDouble(0); //default 0 armor
|
||||
writeVarInt(1, buf); // 1 modifier
|
||||
writeUUID(ARMOR_ATTRIBUTE, buf); // armor modifier uuid
|
||||
buf.writeDouble((double) armor); // the modifier value
|
||||
buf.writeByte(0); // the modifier operation, 0 is add number
|
||||
|
||||
ViaVersion.getInstance().sendRawPacket(player, buf);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@@ -45,7 +59,6 @@ public class ArmorListener implements Listener {
|
||||
}
|
||||
if (e.getRawSlot() >= 5 && e.getRawSlot() <= 8) {
|
||||
sendDelayedArmorUpdate(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,21 +99,4 @@ public class ArmorListener implements Listener {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void sendArmorUpdate(Player player) {
|
||||
int armor = ArmorType.calculateArmorPoints(player.getInventory().getArmorContents());
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
writeVarInt(PacketType.PLAY_ENTITY_PROPERTIES.getNewPacketID(), buf);
|
||||
writeVarInt(player.getEntityId(), buf);
|
||||
buf.writeInt(1); // only 1 property
|
||||
writeString("generic.armor", buf);
|
||||
buf.writeDouble(0); //default 0 armor
|
||||
writeVarInt(1, buf); // 1 modifier
|
||||
writeUUID(armorAttribute, buf); // armor modifier uuid
|
||||
buf.writeDouble((double) armor); // the modifier value
|
||||
buf.writeByte(0); // the modifier operation, 0 is add number
|
||||
|
||||
ViaVersion.getInstance().sendRawPacket(player, buf);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package us.myles.ViaVersion.armor;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum ArmorType {
|
||||
|
||||
LEATHER_HELMET(1, 298, Material.LEATHER_HELMET),
|
||||
@@ -27,27 +31,9 @@ public enum ArmorType {
|
||||
GOLD_BOOTS(1, 317, Material.GOLD_BOOTS),
|
||||
NONE(0, 0, Material.AIR);
|
||||
|
||||
private int armorpoints;
|
||||
private int id;
|
||||
private Material type;
|
||||
|
||||
ArmorType(int armor, int id, Material type) {
|
||||
this.armorpoints = armor;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getArmorPoints() {
|
||||
return this.armorpoints;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Material getType() {
|
||||
return this.type;
|
||||
}
|
||||
private final int armorPoints;
|
||||
private final int id;
|
||||
private final Material type;
|
||||
|
||||
public static ArmorType findByType(Material type) {
|
||||
for(ArmorType a : ArmorType.values())
|
||||
@@ -58,9 +44,9 @@ public enum ArmorType {
|
||||
|
||||
public static int calculateArmorPoints(ItemStack[] armor) {
|
||||
int total = 0;
|
||||
for(int i = 0; i < armor.length; i++) {
|
||||
if(armor[i] != null)
|
||||
total += findByType(armor[i].getType()).getArmorPoints();
|
||||
for (ItemStack anArmor : armor) {
|
||||
if (anArmor != null)
|
||||
total += findByType(anArmor.getType()).getArmorPoints();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -81,11 +67,15 @@ public enum ArmorType {
|
||||
|
||||
public static int calculateArmorPoints(int[] armor) {
|
||||
int total = 0;
|
||||
for(int i = 0; i < armor.length; i++) {
|
||||
if(armor[i] != -1)
|
||||
total += findById(armor[i]).getArmorPoints();
|
||||
for (int anArmor : armor) {
|
||||
if (anArmor != -1)
|
||||
total += findById(anArmor).getArmorPoints();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public Material getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package us.myles.ViaVersion.boss;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
@@ -18,6 +20,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public class ViaBossBar implements BossBar {
|
||||
private UUID uuid;
|
||||
private String title;
|
||||
@@ -48,11 +51,6 @@ public class ViaBossBar implements BossBar {
|
||||
sendPacket(UpdateAction.UPDATE_TITLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHealth(float health) {
|
||||
Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1");
|
||||
@@ -61,8 +59,8 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHealth() {
|
||||
return health;
|
||||
public BossColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,11 +70,6 @@ public class ViaBossBar implements BossBar {
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(BossStyle style) {
|
||||
Validate.notNull(style, "Style cannot be null");
|
||||
@@ -84,11 +77,6 @@ public class ViaBossBar implements BossBar {
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossStyle getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlayer(Player player) {
|
||||
if (player != null && !players.contains(player.getUniqueId())) {
|
||||
@@ -210,6 +198,8 @@ public class ViaBossBar implements BossBar {
|
||||
return OutgoingTransformer.fixJson(text);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
private enum UpdateAction {
|
||||
ADD(0),
|
||||
REMOVE(1),
|
||||
@@ -219,13 +209,5 @@ public class ViaBossBar implements BossBar {
|
||||
UPDATE_FLAGS(5);
|
||||
|
||||
private final int id;
|
||||
|
||||
UpdateAction(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class ByteWriter {
|
||||
private final byte[] bytes;
|
||||
private int byteIndex;
|
||||
private int bitIndex;
|
||||
|
||||
public ByteWriter(int size) {
|
||||
this.bytes = new byte[size];
|
||||
this.byteIndex = 0;
|
||||
this.bitIndex = 0;
|
||||
}
|
||||
|
||||
public byte[] getOutput() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
public void writeFullByte(int b){
|
||||
writeByte(b, 8);
|
||||
}
|
||||
|
||||
public void writeByte(int b, int length) {
|
||||
byte current = getCurrentByte();
|
||||
byte byteB = (byte)(int)((b) & 0xff);
|
||||
System.out.println("Input: " + byteB);
|
||||
System.out.println(Integer.toBinaryString(byteB));
|
||||
|
||||
int space = (8 - bitIndex);
|
||||
int written = space > length ? length : space;
|
||||
System.out.println("Written is " + written);
|
||||
bytes[byteIndex] = (byte) (current | (extractRange(byteB, 0, written) >> (bitIndex - 1)));
|
||||
System.out.println("output value: " + bytes[byteIndex]);
|
||||
this.bitIndex += length;
|
||||
if(this.bitIndex >= 8) {
|
||||
this.byteIndex += 1;
|
||||
this.bitIndex = bitIndex - 8;
|
||||
// write remaining into this
|
||||
System.out.println("Writing from " + written + " to " + (written + bitIndex));
|
||||
System.out.println("Value: " + extractRange(byteB, written, written + bitIndex));
|
||||
bytes[byteIndex] = (byte) (extractRange(byteB, written, written + bitIndex) << written);
|
||||
}
|
||||
}
|
||||
|
||||
private byte extractRange(int in, int begin, int end){
|
||||
return (byte) ((in >> begin) & ((1 << (end - begin)) - 1));
|
||||
}
|
||||
public byte getCurrentByte() {
|
||||
if(byteIndex == bytes.length) throw new RuntimeException("ByteWriter overflow!");
|
||||
|
||||
return bytes[byteIndex];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class Chunk {
|
||||
private final int x;
|
||||
private final int z;
|
||||
private final boolean groundUp;
|
||||
private final int primaryBitmask;
|
||||
private final ChunkSection[] sections;
|
||||
private final byte[] biomeData;
|
||||
private boolean unloadPacket = false;
|
||||
|
||||
/**
|
||||
* Chunk unload.
|
||||
*
|
||||
* @param x coord
|
||||
* @param z coord
|
||||
*/
|
||||
protected Chunk(int x, int z) {
|
||||
this(x, z, true, 0, new ChunkSection[16], null);
|
||||
this.unloadPacket = true;
|
||||
}
|
||||
|
||||
public boolean hasBiomeData() {
|
||||
return biomeData != null && groundUp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.bukkit.Bukkit;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil.ClassReflection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ChunkManager {
|
||||
/**
|
||||
* Amount of sections in a chunk.
|
||||
*/
|
||||
private static final int SECTION_COUNT = 16;
|
||||
/**
|
||||
* size of each chunk section (16x16x16).
|
||||
*/
|
||||
private static final int SECTION_SIZE = 16;
|
||||
/**
|
||||
* Length of biome data.
|
||||
*/
|
||||
private static final int BIOME_DATA_LENGTH = 256;
|
||||
|
||||
private final ConnectionInfo info;
|
||||
private final Set<Long> loadedChunks = Sets.newConcurrentHashSet();
|
||||
private final Set<Long> bulkChunks = Sets.newConcurrentHashSet();
|
||||
|
||||
// Reflection
|
||||
private static ClassReflection mapChunkBulkRef;
|
||||
private static ClassReflection mapChunkRef;
|
||||
|
||||
static {
|
||||
try {
|
||||
mapChunkBulkRef = new ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk"));
|
||||
mapChunkRef = new ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk"));
|
||||
} catch(Exception e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to initialise chunk reflection", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkManager(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a map chunk bulk in to separate map chunk packets.
|
||||
* These packets are registered so that they will never be seen as unload packets.
|
||||
*
|
||||
* @param packet to transform
|
||||
* @return List of chunk data packets
|
||||
*/
|
||||
public List<Object> transformMapChunkBulk(Object packet) {
|
||||
List<Object> list = Lists.newArrayList();
|
||||
try {
|
||||
int[] xcoords = mapChunkBulkRef.getFieldValue("a", packet, int[].class);
|
||||
int[] zcoords = mapChunkBulkRef.getFieldValue("b", packet, int[].class);
|
||||
Object[] chunkMaps = mapChunkBulkRef.getFieldValue("c", packet, Object[].class);
|
||||
for(int i = 0; i < chunkMaps.length; i++) {
|
||||
int x = xcoords[i];
|
||||
int z = zcoords[i];
|
||||
Object chunkMap = chunkMaps[i];
|
||||
Object chunkPacket = mapChunkRef.newInstance();
|
||||
mapChunkRef.setFieldValue("a", chunkPacket, x);
|
||||
mapChunkRef.setFieldValue("b", chunkPacket, z);
|
||||
mapChunkRef.setFieldValue("c", chunkPacket, chunkMap);
|
||||
mapChunkRef.setFieldValue("d", chunkPacket, true); // Chunk bulk chunks are always ground-up
|
||||
bulkChunks.add(toLong(x, z)); // Store for later
|
||||
list.add(chunkPacket);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to transform chunk bulk", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read chunk from 1.8 chunk data.
|
||||
*
|
||||
* @param input data
|
||||
* @return Chunk
|
||||
*/
|
||||
public Chunk readChunk(ByteBuf input) {
|
||||
// Primary data
|
||||
int chunkX = input.readInt();
|
||||
int chunkZ = input.readInt();
|
||||
long chunkHash = toLong(chunkX, chunkZ);
|
||||
boolean groundUp = input.readByte() != 0;
|
||||
int bitmask = input.readUnsignedShort();
|
||||
int dataLength = PacketUtil.readVarInt(input);
|
||||
|
||||
// Data to be read
|
||||
BitSet usedSections = new BitSet(16);
|
||||
ChunkSection[] sections = new ChunkSection[16];
|
||||
byte[] biomeData = null;
|
||||
|
||||
// Calculate section count from bitmask
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if((bitmask & (1 << i)) != 0) {
|
||||
usedSections.set(i);
|
||||
}
|
||||
}
|
||||
int sectionCount = usedSections.cardinality(); // the amount of sections set
|
||||
|
||||
// If the chunk is from a chunk bulk, it is never an unload packet
|
||||
// Other wise, if it has no data, it is :)
|
||||
boolean isBulkPacket = bulkChunks.remove(chunkHash);
|
||||
if(sectionCount == 0 && groundUp && !isBulkPacket && loadedChunks.contains(chunkHash)) {
|
||||
// This is a chunk unload packet
|
||||
loadedChunks.remove(chunkHash);
|
||||
return new Chunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
int startIndex = input.readerIndex();
|
||||
loadedChunks.add(chunkHash); // mark chunk as loaded
|
||||
|
||||
// Read blocks
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set
|
||||
ChunkSection section = new ChunkSection();
|
||||
sections[i] = section;
|
||||
|
||||
// Read block data and convert to short buffer
|
||||
byte[] blockData = new byte[ChunkSection.SIZE * 2];
|
||||
input.readBytes(blockData);
|
||||
ShortBuffer blockBuf = ByteBuffer.wrap(blockData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
|
||||
for(int j = 0; j < ChunkSection.SIZE; j++) {
|
||||
int mask = blockBuf.get();
|
||||
int type = mask >> 4;
|
||||
int data = mask & 0xF;
|
||||
section.setBlock(j, type, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Read block light
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set, has no light
|
||||
byte[] blockLightArray = new byte[ChunkSection.LIGHT_LENGTH];
|
||||
input.readBytes(blockLightArray);
|
||||
sections[i].setBlockLight(blockLightArray);
|
||||
}
|
||||
|
||||
// Read sky light
|
||||
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
|
||||
if(bytesLeft >= ChunkSection.LIGHT_LENGTH) {
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set, has no light
|
||||
byte[] skyLightArray = new byte[ChunkSection.LIGHT_LENGTH];
|
||||
input.readBytes(skyLightArray);
|
||||
sections[i].setSkyLight(skyLightArray);
|
||||
bytesLeft -= ChunkSection.LIGHT_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
// Read biome data
|
||||
if(bytesLeft >= BIOME_DATA_LENGTH) {
|
||||
biomeData = new byte[BIOME_DATA_LENGTH];
|
||||
input.readBytes(biomeData);
|
||||
bytesLeft -= BIOME_DATA_LENGTH;
|
||||
}
|
||||
|
||||
// Check remaining bytes
|
||||
if(bytesLeft > 0) {
|
||||
Bukkit.getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunk! (" + groundUp + ")");
|
||||
}
|
||||
|
||||
// Return chunk
|
||||
return new Chunk(chunkX, chunkZ, groundUp, bitmask, sections, biomeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write chunk over 1.9 protocol.
|
||||
*
|
||||
* @param chunk chunk
|
||||
* @param output output
|
||||
*/
|
||||
public void writeChunk(Chunk chunk, ByteBuf output) {
|
||||
if(chunk.isUnloadPacket()) {
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(0x1D, output);
|
||||
}
|
||||
|
||||
// Write primary info
|
||||
output.writeInt(chunk.getX());
|
||||
output.writeInt(chunk.getZ());
|
||||
if(chunk.isUnloadPacket()) return;
|
||||
output.writeByte(chunk.isGroundUp() ? 0x01 : 0x00);
|
||||
PacketUtil.writeVarInt(chunk.getPrimaryBitmask(), output);
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
ChunkSection section = chunk.getSections()[i];
|
||||
if(section == null) continue; // Section not set
|
||||
section.writeBlocks(buf);
|
||||
section.writeBlockLight(buf);
|
||||
if(!section.hasSkyLight()) continue; // No sky light, we're done here.
|
||||
section.writeSkyLight(buf);
|
||||
}
|
||||
buf.readerIndex(0);
|
||||
PacketUtil.writeVarInt(buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0), output);
|
||||
output.writeBytes(buf);
|
||||
buf.release(); // release buffer
|
||||
|
||||
// Write biome data
|
||||
if(chunk.hasBiomeData()) {
|
||||
output.writeBytes(chunk.getBiomeData());
|
||||
}
|
||||
}
|
||||
|
||||
private static long toLong(int msw, int lsw) {
|
||||
return ((long) msw << 32) + lsw - -2147483648L;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.bukkit.Material;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChunkSection {
|
||||
/**
|
||||
* Size (dimensions) of blocks in a chunk section.
|
||||
*/
|
||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
||||
/**
|
||||
* Length of the sky and block light nibble arrays.
|
||||
*/
|
||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
||||
/**
|
||||
* Length of the block data array.
|
||||
*/
|
||||
// public static final int BLOCK_LENGTH = 16 * 16 * 16 * 2; // size * size * size * 2 (char bit count)
|
||||
|
||||
private final List<Integer> palette = Lists.newArrayList();
|
||||
private final int[] blocks;
|
||||
private final NibbleArray blockLight;
|
||||
private NibbleArray skyLight;
|
||||
|
||||
public ChunkSection() {
|
||||
this.blocks = new int[SIZE];
|
||||
this.blockLight = new NibbleArray(SIZE);
|
||||
palette.add(0); // AIR
|
||||
}
|
||||
|
||||
public void setBlock(int x, int y, int z, int type, int data) {
|
||||
setBlock(index(x, y, z), type, data);
|
||||
}
|
||||
|
||||
public void setBlock(int idx, int type, int data) {
|
||||
int hash = type << 4 | (data & 0xF);
|
||||
int index = palette.indexOf(hash);
|
||||
if(index == -1) {
|
||||
index = palette.size();
|
||||
palette.add(hash);
|
||||
}
|
||||
|
||||
blocks[idx] = index;
|
||||
}
|
||||
|
||||
public void setBlockLight(byte[] data) {
|
||||
blockLight.setHandle(data);
|
||||
}
|
||||
|
||||
public void setSkyLight(byte[] data) {
|
||||
if(data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
||||
this.skyLight = new NibbleArray(data);
|
||||
}
|
||||
|
||||
private int index(int x, int y, int z) {
|
||||
return z << 8 | y << 4 | x;
|
||||
}
|
||||
|
||||
public void writeBlocks(ByteBuf output) {
|
||||
// Write bits per block
|
||||
int bitsPerBlock = 4;
|
||||
while(palette.size() > 1 << bitsPerBlock) {
|
||||
bitsPerBlock += 1;
|
||||
}
|
||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||
output.writeByte(bitsPerBlock);
|
||||
|
||||
// Write pallet (or not)
|
||||
PacketUtil.writeVarInt(palette.size(), output);
|
||||
for(int mappedId : palette) {
|
||||
PacketUtil.writeVarInt(mappedId, output);
|
||||
}
|
||||
|
||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
||||
PacketUtil.writeVarInt(length, output);
|
||||
long[] data = new long[length];
|
||||
for(int index = 0; index < blocks.length; index++) {
|
||||
int value = blocks[index];
|
||||
int bitIndex = index * bitsPerBlock;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
||||
if(startIndex != endIndex) {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
||||
}
|
||||
}
|
||||
PacketUtil.writeLongs(data, output);
|
||||
}
|
||||
|
||||
public void writeBlockLight(ByteBuf output) {
|
||||
output.writeBytes(blockLight.getHandle());
|
||||
}
|
||||
|
||||
public void writeSkyLight(ByteBuf output) {
|
||||
output.writeBytes(skyLight.getHandle());
|
||||
}
|
||||
|
||||
public boolean hasSkyLight() {
|
||||
return skyLight != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expected size of this chunk section.
|
||||
*
|
||||
* @return Amount of bytes sent by this section
|
||||
*/
|
||||
public int getExpectedSize() {
|
||||
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
|
||||
int bytes = 1; // bits per block
|
||||
bytes += paletteBytes(); // palette
|
||||
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
|
||||
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
|
||||
bytes += LIGHT_LENGTH; // block light
|
||||
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private int paletteBytes() {
|
||||
// Count bytes used by pallet
|
||||
int bytes = countBytes(palette.size());
|
||||
for(int mappedId : palette) {
|
||||
bytes += countBytes(mappedId);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private int countBytes(int value) {
|
||||
// Count amount of bytes that would be sent if the value were sent as a VarInt
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
PacketUtil.writeVarInt(value, buf);
|
||||
buf.readerIndex(0);
|
||||
int bitCount = buf.readableBytes();
|
||||
buf.release();
|
||||
return bitCount;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class MagicBitSet extends BitSet{
|
||||
private final int initLength;
|
||||
|
||||
public MagicBitSet(int nbits) {
|
||||
super(nbits);
|
||||
this.initLength = nbits;
|
||||
}
|
||||
|
||||
public int getTrueLength() {
|
||||
return length() == 0 ? initLength : length();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NibbleArray {
|
||||
private final byte[] handle;
|
||||
|
||||
public NibbleArray(int length) {
|
||||
if(length == 0 || length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
this.handle = new byte[length / 2];
|
||||
}
|
||||
|
||||
public NibbleArray(byte[] handle) {
|
||||
if(handle.length == 0 || handle.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public byte get(int x, int y, int z) {
|
||||
return get(y << 8 | z << 4 | x);
|
||||
}
|
||||
|
||||
public byte get(int index) {
|
||||
byte value = handle[index / 2];
|
||||
if(index % 2 == 0) {
|
||||
return (byte) (value & 0xF);
|
||||
} else {
|
||||
return (byte) ((value >> 4) & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int value) {
|
||||
set(y << 8 | z << 4 | x, value);
|
||||
}
|
||||
|
||||
public void set(int index, int value) {
|
||||
index /= 2;
|
||||
if(index % 2 == 0) {
|
||||
handle[index] = (byte) (handle[index] & 0xF0 | value & 0xF);
|
||||
} else {
|
||||
handle[index] = (byte) (handle[index] & 0xF | (value & 0xF) << 4);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return handle.length * 2;
|
||||
}
|
||||
|
||||
public int actualSize() {
|
||||
return handle.length;
|
||||
}
|
||||
|
||||
public void fill(byte value) {
|
||||
value &= 0xF; // Max nibble size (= 16)
|
||||
Arrays.fill(handle, (byte) ((value << 4) | value));
|
||||
}
|
||||
|
||||
public void setHandle(byte[] handle) {
|
||||
if(handle.length != this.handle.length) {
|
||||
throw new IllegalArgumentException("Length of handle must equal to size of nibble array!");
|
||||
}
|
||||
|
||||
System.arraycopy(handle, 0, this.handle, 0, handle.length);
|
||||
}
|
||||
|
||||
public byte[] getHandle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class PacketChunk {
|
||||
private PacketChunkData[] chunkData;
|
||||
private byte[] biomeData;
|
||||
|
||||
public PacketChunk(PacketChunkData[] chunkData, byte[] biomeData) {
|
||||
this.chunkData = chunkData;
|
||||
this.biomeData = biomeData;
|
||||
}
|
||||
|
||||
public PacketChunkData[] getChunkData() {
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
public byte[] getBiomeData() {
|
||||
return biomeData;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class PacketChunkData {
|
||||
private short[] blocks = new short[4096];
|
||||
private byte[] blockLight = new byte[2048];
|
||||
private byte[] skyLight;
|
||||
|
||||
public PacketChunkData(boolean isSkyLightData) {
|
||||
if(isSkyLightData){
|
||||
skyLight = new byte[2048];
|
||||
}
|
||||
}
|
||||
|
||||
public short[] getBlocks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
public byte[] getBlockLight() {
|
||||
return this.blockLight;
|
||||
}
|
||||
|
||||
public byte[] getSkyLight() {
|
||||
return this.skyLight;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package us.myles.ViaVersion.commands;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@@ -14,25 +15,20 @@ import java.util.List;
|
||||
/**
|
||||
* Created by fillefilip8 on 2016-03-03.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ViaVersionCommand implements CommandExecutor {
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public ViaVersionCommand(ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (sender.hasPermission("viaversion.admin")) {
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
|
||||
sender.sendMessage(color("&6Commands:"));
|
||||
sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients."));
|
||||
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
|
||||
sendHelp(sender);
|
||||
} else if (args.length == 1) {
|
||||
if (args[0].equalsIgnoreCase("list")) {
|
||||
List<String> portedPlayers = new ArrayList<String>();
|
||||
List<String> normalPlayers = new ArrayList<String>();
|
||||
List<String> portedPlayers = new ArrayList<>();
|
||||
List<String> normalPlayers = new ArrayList<>();
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (ViaVersion.getInstance().isPorted(p)) {
|
||||
portedPlayers.add(p.getName());
|
||||
@@ -43,16 +39,22 @@ public class ViaVersionCommand implements CommandExecutor {
|
||||
|
||||
sender.sendMessage(color("&8[&61.9&8]: &b" + portedPlayers.toString()));
|
||||
sender.sendMessage(color("&8[&61.8&8]: &b" + normalPlayers.toString()));
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("debug")) {
|
||||
} else if (args[0].equalsIgnoreCase("debug")) {
|
||||
plugin.setDebug(!plugin.isDebug());
|
||||
sender.sendMessage(color("&6Debug mode is now " + (plugin.isDebug() ? "&aenabled" : "&cdisabled")));
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("dontbugme")) {
|
||||
} else if (args[0].equalsIgnoreCase("dontbugme")) {
|
||||
boolean newValue = !plugin.getConfig().getBoolean("checkforupdates", true);
|
||||
plugin.getConfig().set("checkforupdates", newValue);
|
||||
plugin.saveConfig();
|
||||
sender.sendMessage(color("&6We will " + (newValue ? "&anotify you about updates." : "&cnot tell you about updates.")));
|
||||
} else if (args[0].equalsIgnoreCase("autoteam")) {
|
||||
boolean newValue = !plugin.getConfig().getBoolean("auto-team", true);
|
||||
plugin.getConfig().set("auto-team", newValue);
|
||||
plugin.saveConfig();
|
||||
sender.sendMessage(color("&6We will " + (newValue ? "&aautomatically team players" : "&cno longer auto team players")));
|
||||
sender.sendMessage(color("&6All players will need to re-login for the change to take place."));
|
||||
} else {
|
||||
sendHelp(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +62,15 @@ public class ViaVersionCommand implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void sendHelp(CommandSender sender){
|
||||
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
|
||||
sender.sendMessage(color("&6Commands:"));
|
||||
sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients."));
|
||||
sender.sendMessage(color("&2/viaversion debug &7- &6Toggle debug mode"));
|
||||
sender.sendMessage(color("&2/viaversion autoteam &7- &6Toggle automatically teaming to prevent colliding."));
|
||||
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
|
||||
}
|
||||
|
||||
public String color(String string) {
|
||||
return string.replace("&", "§");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class ViaChunkHandler extends MessageToMessageEncoder {
|
||||
private final ConnectionInfo info;
|
||||
|
||||
public ViaChunkHandler(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception {
|
||||
// Split chunk bulk packet up in to single chunk packets before it reached the encoder.
|
||||
// This will prevent issues with several plugins and other protocol handlers due to the chunk being sent twice.
|
||||
// It also sends the chunk in the right order possible resolving some issues with added chunk/block/entity data.
|
||||
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()) {
|
||||
list.addAll(info.getChunkManager().transformMapChunkBulk(o));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list.add(o);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import us.myles.ViaVersion.util.PacketUtil;
|
||||
import java.util.List;
|
||||
|
||||
public class ViaDecodeHandler extends ByteToMessageDecoder {
|
||||
|
||||
private final IncomingTransformer incomingTransformer;
|
||||
private final ByteToMessageDecoder minecraftDecoder;
|
||||
private final ConnectionInfo info;
|
||||
@@ -31,10 +32,10 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
|
||||
ByteBuf newPacket = ctx.alloc().buffer();
|
||||
try {
|
||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||
bytebuf.readBytes(bytebuf.readableBytes());
|
||||
bytebuf.clear();
|
||||
bytebuf = newPacket;
|
||||
} catch (CancelException e) {
|
||||
bytebuf.readBytes(bytebuf.readableBytes());
|
||||
} catch (Exception e) {
|
||||
bytebuf.clear();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ 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.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
@@ -30,51 +28,6 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
protected void encode(final ChannelHandlerContext ctx, Object o, final 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()) {
|
||||
final int[] locX = ReflectionUtil.get(o, "a", int[].class);
|
||||
final int[] locZ = ReflectionUtil.get(o, "b", int[].class);
|
||||
|
||||
final Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
|
||||
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
|
||||
final Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
|
||||
Runnable chunks = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
for (int i = 0; i < locX.length; i++) {
|
||||
int x = locX[i];
|
||||
int z = locZ[i];
|
||||
// world invoke function
|
||||
try {
|
||||
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);
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// Synced allows timings to work properly.
|
||||
// if (ViaVersion.getInstance().isSyncedChunks()) {
|
||||
// ((ViaVersionPlugin) ViaVersion.getInstance()).run(chunks, false);
|
||||
// } else {
|
||||
chunks.run();
|
||||
// }
|
||||
bytebuf.readBytes(bytebuf.readableBytes());
|
||||
throw new CancelException();
|
||||
}
|
||||
// call minecraft encoder
|
||||
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
|
||||
}
|
||||
@@ -88,8 +41,8 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
bytebuf.clear();
|
||||
try {
|
||||
outgoingTransformer.transform(id, oldPacket, bytebuf);
|
||||
} catch (CancelException e) {
|
||||
bytebuf.readBytes(bytebuf.readableBytes());
|
||||
} catch (Exception e) {
|
||||
bytebuf.clear();
|
||||
throw e;
|
||||
} finally {
|
||||
oldPacket.release();
|
||||
|
||||
@@ -10,6 +10,7 @@ import us.myles.ViaVersion.ConnectionInfo;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final ChannelInitializer<SocketChannel> oldInit;
|
||||
private Method method;
|
||||
|
||||
@@ -31,8 +32,10 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||
// Add our transformers
|
||||
ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
|
||||
ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
|
||||
ViaChunkHandler chunkHandler = new ViaChunkHandler(info);
|
||||
|
||||
socketChannel.pipeline().replace("encoder", "encoder", encoder);
|
||||
socketChannel.pipeline().replace("decoder", "decoder", decoder);
|
||||
socketChannel.pipeline().addAfter("packet_handler", "viaversion_chunk_handler", chunkHandler);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package us.myles.ViaVersion.listeners;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.CommandBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -19,12 +20,10 @@ import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandBlockListener implements Listener {
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public CommandBlockListener(ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onJoin(final PlayerJoinEvent e) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.*;
|
||||
|
||||
@Getter
|
||||
public enum MetaIndex {
|
||||
|
||||
// entity
|
||||
@@ -31,6 +33,7 @@ public enum MetaIndex {
|
||||
PLAYER_HUMAN_BYTE(HumanEntity.class, 16, Type.Byte, NewType.Discontinued), // unused on 1.8
|
||||
PLAYER_ADDITIONAL_HEARTS(HumanEntity.class, 17, Type.Float, 10, NewType.Float),
|
||||
PLAYER_SCORE(HumanEntity.class, 18, Type.Int, 11, NewType.VarInt),
|
||||
PLAYER_HAND(HumanEntity.class, -1, Type.NonExistent, 5, NewType.Byte), // new in 1.9
|
||||
// horse
|
||||
HORSE_INFO(Horse.class, 16, Type.Int, 12, NewType.Byte),
|
||||
HORSE_TYPE(Horse.class, 19, Type.Byte, 13, NewType.VarInt),
|
||||
@@ -88,6 +91,8 @@ public enum MetaIndex {
|
||||
WITHER_TARGET2(Wither.class, 18, Type.Int, 12, NewType.VarInt),
|
||||
WITHER_TARGET3(Wither.class, 19, Type.Int, 13, NewType.VarInt),
|
||||
WITHER_INVULN_TIME(Wither.class, 20, Type.Int, 14, NewType.VarInt),
|
||||
WITHER_PROPERTIES(Wither.class, 10, Type.Byte, NewType.Byte),
|
||||
WITHER_UNKNOWN(Wither.class, 11, Type.Byte, NewType.Discontinued),
|
||||
// wither skull
|
||||
WITHERSKULL_INVULN(WitherSkull.class, 10, Type.Byte, 5, NewType.Boolean),
|
||||
// guardian
|
||||
@@ -109,7 +114,7 @@ public enum MetaIndex {
|
||||
MINECART_COMMANDBLOCK_COMMAND(Minecart.class, 23, Type.String, 11, NewType.String),
|
||||
MINECART_COMMANDBLOCK_OUTPUT(Minecart.class, 24, Type.String, 12, NewType.Chat),
|
||||
// furnace cart
|
||||
FURNACECART_ISPOWERED(org.bukkit.entity.minecart.PoweredMinecart.class, 16, Type.Byte, 11, NewType.Boolean),
|
||||
FURNACECART_ISPOWERED(Minecart.class, 16, Type.Byte, 11, NewType.Boolean),
|
||||
// item drop
|
||||
ITEM_ITEM(Item.class, 10, Type.Slot, 5, NewType.Slot),
|
||||
// arrow
|
||||
@@ -120,7 +125,13 @@ public enum MetaIndex {
|
||||
ITEMFRAME_ITEM(ItemFrame.class, 8, Type.Slot, 5, NewType.Slot),
|
||||
ITEMFRAME_ROTATION(ItemFrame.class, 9, Type.Byte, 6, NewType.VarInt),
|
||||
// ender crystal
|
||||
ENDERCRYSTAL_HEALTH(EnderCrystal.class, 8, Type.Int, NewType.Discontinued),;
|
||||
ENDERCRYSTAL_HEALTH(EnderCrystal.class, 8, Type.Int, NewType.Discontinued),
|
||||
// Ender dragon boss bar issues
|
||||
ENDERDRAGON_UNKNOWN(EnderDragon.class, 5, Type.Byte, NewType.Discontinued),
|
||||
ENDERDRAGON_NAME(EnderDragon.class, 10, Type.String, NewType.Discontinued),
|
||||
// Normal Ender dragon
|
||||
ENDERDRAGON_FLAG(EnderDragon.class, 15, Type.Byte, NewType.Discontinued),
|
||||
ENDERDRAGON_PHASE(EnderDragon.class, 11, Type.Byte, NewType.VarInt);
|
||||
|
||||
private Class<?> clazz;
|
||||
private int newIndex;
|
||||
@@ -144,26 +155,6 @@ public enum MetaIndex {
|
||||
this.newType = newType;
|
||||
}
|
||||
|
||||
public int getNewIndex() {
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
public NewType getNewType() {
|
||||
return newType;
|
||||
}
|
||||
|
||||
public Type getOldType() {
|
||||
return oldType;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public Class<?> getApplicableClass() {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
public static MetaIndex getIndex(EntityType type, int index) {
|
||||
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
|
||||
if (entityClass == null) {
|
||||
@@ -192,5 +183,9 @@ public enum MetaIndex {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class<?> getApplicableClass() {
|
||||
return this.clazz;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import org.bukkit.util.Vector;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.slot.ItemSlotRewriter;
|
||||
import us.myles.ViaVersion.slot.ItemSlotRewriter.ItemStack;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -19,9 +22,7 @@ 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(); //
|
||||
for (Entry entry : list) {
|
||||
MetaIndex metaIndex = entry.index;
|
||||
try {
|
||||
if (metaIndex.getNewType() != NewType.Discontinued) {
|
||||
@@ -34,11 +35,21 @@ public class MetadataRewriter {
|
||||
case Byte:
|
||||
// convert from int, byte
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
output.writeByte(((Byte) value).byteValue());
|
||||
output.writeByte((Byte) value);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
output.writeByte(((Integer) value).byteValue());
|
||||
}
|
||||
// After writing the last one
|
||||
if (metaIndex == MetaIndex.ENTITY_STATUS && type == EntityType.PLAYER) {
|
||||
output.writeByte(MetaIndex.PLAYER_HAND.getNewIndex());
|
||||
output.writeByte(MetaIndex.PLAYER_HAND.getNewType().getTypeID());
|
||||
if ((((Byte) value) & 0x10) == 0x10) { // Player eating/aiming/drinking
|
||||
output.writeByte(1); // Using main hand
|
||||
} else {
|
||||
output.writeByte(0); // Not using any hand to stop animation
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OptUUID:
|
||||
String owner = (String) value;
|
||||
@@ -51,15 +62,15 @@ public class MetadataRewriter {
|
||||
}
|
||||
output.writeBoolean(toWrite != null);
|
||||
if (toWrite != null)
|
||||
PacketUtil.writeUUID((UUID) toWrite, output);
|
||||
PacketUtil.writeUUID(toWrite, output);
|
||||
break;
|
||||
case BlockID:
|
||||
// if we have both sources :))
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
data = ((Byte) value).byteValue();
|
||||
data = (Byte) value;
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Short) {
|
||||
id = ((Short) value).shortValue();
|
||||
id = (Short) value;
|
||||
}
|
||||
if (id != -1 && data != -1) {
|
||||
int combined = id << 4 | data;
|
||||
@@ -77,20 +88,20 @@ public class MetadataRewriter {
|
||||
PacketUtil.writeVarInt(((Short) value).intValue(), output);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
PacketUtil.writeVarInt(((Integer) value).intValue(), output);
|
||||
PacketUtil.writeVarInt((Integer) value, output);
|
||||
}
|
||||
break;
|
||||
case Float:
|
||||
output.writeFloat(((Float) value).floatValue());
|
||||
output.writeFloat((Float) value);
|
||||
break;
|
||||
case String:
|
||||
PacketUtil.writeString((String) value, output);
|
||||
break;
|
||||
case Boolean:
|
||||
if (metaIndex == MetaIndex.AGEABLE_AGE)
|
||||
output.writeBoolean(((Byte) value).byteValue() < 0);
|
||||
output.writeBoolean((Byte) value < 0);
|
||||
else
|
||||
output.writeBoolean(((Byte) value).byteValue() != 0);
|
||||
output.writeBoolean((Byte) value != 0);
|
||||
break;
|
||||
case Slot:
|
||||
ItemStack item = (ItemStack) value;
|
||||
@@ -118,16 +129,18 @@ public class MetadataRewriter {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("INCLUDE THIS IN YOUR ERROR LOG!");
|
||||
if (type != null)
|
||||
System.out.println("An error occurred with entity meta data for " + type + " OldID: " + entry.oldID);
|
||||
else
|
||||
System.out.println("An error occurred with entity meta data for UNKOWN_ENTITY OldID: " + entry.oldID);
|
||||
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());
|
||||
if (!((ViaVersionPlugin) ViaVersion.getInstance()).isSuppressMetadataErrors()) {
|
||||
System.out.println("INCLUDE THIS IN YOUR ERROR LOG!");
|
||||
if (type != null)
|
||||
System.out.println("An error occurred with entity meta data for " + type + " OldID: " + entry.oldID);
|
||||
else
|
||||
System.out.println("An error occurred with entity meta data for UNKNOWN_ENTITY OldID: " + entry.oldID);
|
||||
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();
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
output.writeByte(255);
|
||||
@@ -186,13 +199,15 @@ public class MetadataRewriter {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
@Getter
|
||||
@Setter
|
||||
public static final class Entry {
|
||||
|
||||
private final int oldID;
|
||||
private MetaIndex index;
|
||||
private Object value;
|
||||
|
||||
private Entry(MetaIndex index, Object value, int id) {
|
||||
public Entry(MetaIndex index, Object value, int id) {
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.oldID = id;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum NewType {
|
||||
Byte(0),
|
||||
VarInt(1),
|
||||
@@ -15,13 +20,7 @@ public enum NewType {
|
||||
OptUUID(11),
|
||||
BlockID(12),
|
||||
Discontinued(99);
|
||||
|
||||
private final int typeID;
|
||||
|
||||
NewType(int typeID){
|
||||
this.typeID = typeID;
|
||||
}
|
||||
|
||||
public int getTypeID() {
|
||||
return typeID;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum Type {
|
||||
Byte(0),
|
||||
Short(1),
|
||||
@@ -8,17 +13,10 @@ public enum Type {
|
||||
String(4),
|
||||
Slot(5),
|
||||
Position(6),
|
||||
Rotation(7);
|
||||
Rotation(7),
|
||||
NonExistent(-1);
|
||||
private final int typeID;
|
||||
|
||||
Type(int typeID){
|
||||
this.typeID = typeID;
|
||||
}
|
||||
|
||||
public int getTypeID() {
|
||||
return typeID;
|
||||
}
|
||||
|
||||
public static Type byId(int id) {
|
||||
return values()[id];
|
||||
}
|
||||
|
||||
@@ -1,200 +1,203 @@
|
||||
package us.myles.ViaVersion.packets;
|
||||
|
||||
public enum PacketType {
|
||||
/* Handshake serverbound */
|
||||
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00),
|
||||
/* Login serverbound */
|
||||
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00),
|
||||
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01),
|
||||
/* Login clientbound */
|
||||
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00),
|
||||
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01),
|
||||
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02),
|
||||
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03),
|
||||
/* Status serverbound */
|
||||
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00),
|
||||
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01),
|
||||
/* Status clientbound */
|
||||
STATUS_RESPONSE(State.STATUS, Direction.OUTGOING, 0x00),
|
||||
STATUS_PONG(State.STATUS, Direction.OUTGOING, 0x01),
|
||||
/* Play serverbound */
|
||||
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
|
||||
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
|
||||
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
|
||||
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
|
||||
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
|
||||
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
|
||||
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06),
|
||||
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07),
|
||||
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08),
|
||||
PLAY_PLUGIN_MESSAGE_REQUEST(State.PLAY, Direction.INCOMING, 0x17, 0x09),
|
||||
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A),
|
||||
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B),
|
||||
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C),
|
||||
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D),
|
||||
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E),
|
||||
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F),
|
||||
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10),
|
||||
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11),
|
||||
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12),
|
||||
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13),
|
||||
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14),
|
||||
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15),
|
||||
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16),
|
||||
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17),
|
||||
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18),
|
||||
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19),
|
||||
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A),
|
||||
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B),
|
||||
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C),
|
||||
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D),
|
||||
/* Play clientbound */
|
||||
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00),
|
||||
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01),
|
||||
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02),
|
||||
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
|
||||
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
|
||||
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
|
||||
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
|
||||
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
|
||||
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
|
||||
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09),
|
||||
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A),
|
||||
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B),
|
||||
PLAY_BOSS_BAR(State.PLAY, Direction.OUTGOING, -1, 0x0C),
|
||||
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D),
|
||||
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E),
|
||||
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F),
|
||||
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10),
|
||||
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11),
|
||||
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12),
|
||||
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13),
|
||||
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14),
|
||||
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15),
|
||||
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16),
|
||||
PLAY_SET_COOLDOWN(State.PLAY, Direction.OUTGOING, -1, 0x17),
|
||||
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18),
|
||||
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19),
|
||||
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A),
|
||||
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B),
|
||||
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C),
|
||||
PLAY_UNLOAD_CHUNK(State.PLAY, Direction.OUTGOING, -1, 0x1D),
|
||||
PLAY_CHANGE_GAME_STATE(State.PLAY, Direction.OUTGOING, 0x2B, 0x1E),
|
||||
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F),
|
||||
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20),
|
||||
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21),
|
||||
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22),
|
||||
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23),
|
||||
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24),
|
||||
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25),
|
||||
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26),
|
||||
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27),
|
||||
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28),
|
||||
PLAY_VEHICLE_MOVE(State.PLAY, Direction.OUTGOING, -1, 0x29),
|
||||
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A),
|
||||
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B),
|
||||
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
|
||||
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
|
||||
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
|
||||
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F),
|
||||
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
|
||||
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
|
||||
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
|
||||
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33),
|
||||
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34),
|
||||
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35),
|
||||
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36),
|
||||
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37),
|
||||
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38),
|
||||
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39),
|
||||
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A),
|
||||
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B),
|
||||
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C),
|
||||
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D),
|
||||
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_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),
|
||||
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45),
|
||||
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46),
|
||||
PLAY_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, -1, 0x47),
|
||||
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48),
|
||||
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49),
|
||||
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A),
|
||||
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B),
|
||||
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C),
|
||||
|
||||
PLAY_MAP_CHUNK_BULK(State.PLAY, Direction.OUTGOING, 0x26, -1),
|
||||
PLAY_SET_COMPRESSION(State.PLAY, Direction.OUTGOING, 0x46, -1),
|
||||
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1),;
|
||||
|
||||
private State state;
|
||||
private Direction direction;
|
||||
private int packetID;
|
||||
private int newPacketID = -1;
|
||||
|
||||
PacketType(State state, Direction direction, int packetID) {
|
||||
this.state = state;
|
||||
this.direction = direction;
|
||||
this.packetID = packetID;
|
||||
this.newPacketID = packetID;
|
||||
}
|
||||
|
||||
PacketType(State state, Direction direction, int packetID, int newPacketID) {
|
||||
this.state = state;
|
||||
this.direction = direction;
|
||||
this.packetID = packetID;
|
||||
this.newPacketID = newPacketID;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public int getPacketID() {
|
||||
return packetID;
|
||||
}
|
||||
|
||||
public int getNewPacketID() {
|
||||
return newPacketID;
|
||||
}
|
||||
|
||||
public static PacketType findNewPacket(State state, Direction direction, int id) {
|
||||
if(id == -1)
|
||||
return null;
|
||||
for (PacketType pt : values()) {
|
||||
if (pt.getNewPacketID() == id
|
||||
&& pt.getState() == state
|
||||
&& pt.getDirection() == direction)
|
||||
return pt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PacketType findOldPacket(State state, Direction direction, int id) {
|
||||
if(id == -1)
|
||||
return null;
|
||||
for (PacketType pt : values()) {
|
||||
if (pt.getPacketID() == id
|
||||
&& pt.getState() == state
|
||||
&& pt.getDirection() == direction)
|
||||
return pt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PacketType getIncomingPacket(State state, int id) {
|
||||
return findNewPacket(state, Direction.INCOMING, id);
|
||||
}
|
||||
|
||||
public static PacketType getOutgoingPacket(State state, int id) {
|
||||
return findOldPacket(state, Direction.OUTGOING, id);
|
||||
}
|
||||
}
|
||||
package us.myles.ViaVersion.packets;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
public enum PacketType {
|
||||
/* Handshake serverbound */
|
||||
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00),
|
||||
/* Login serverbound */
|
||||
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00),
|
||||
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01),
|
||||
/* Login clientbound */
|
||||
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00),
|
||||
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01),
|
||||
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02),
|
||||
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03),
|
||||
/* Status serverbound */
|
||||
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00),
|
||||
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01),
|
||||
/* Status clientbound */
|
||||
STATUS_RESPONSE(State.STATUS, Direction.OUTGOING, 0x00),
|
||||
STATUS_PONG(State.STATUS, Direction.OUTGOING, 0x01),
|
||||
/* Play serverbound */
|
||||
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
|
||||
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
|
||||
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
|
||||
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
|
||||
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
|
||||
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
|
||||
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06),
|
||||
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07),
|
||||
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08),
|
||||
PLAY_PLUGIN_MESSAGE_REQUEST(State.PLAY, Direction.INCOMING, 0x17, 0x09),
|
||||
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A),
|
||||
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B),
|
||||
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C),
|
||||
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D),
|
||||
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E),
|
||||
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F),
|
||||
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10),
|
||||
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11),
|
||||
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12),
|
||||
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13),
|
||||
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14),
|
||||
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15),
|
||||
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16),
|
||||
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17),
|
||||
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18),
|
||||
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19),
|
||||
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A),
|
||||
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B),
|
||||
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C),
|
||||
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D),
|
||||
/* Play clientbound */
|
||||
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00),
|
||||
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01),
|
||||
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02),
|
||||
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
|
||||
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
|
||||
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
|
||||
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
|
||||
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
|
||||
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
|
||||
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09),
|
||||
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A),
|
||||
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B),
|
||||
PLAY_BOSS_BAR(State.PLAY, Direction.OUTGOING, -1, 0x0C),
|
||||
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D),
|
||||
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E),
|
||||
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F),
|
||||
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10),
|
||||
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11),
|
||||
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12),
|
||||
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13),
|
||||
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14),
|
||||
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15),
|
||||
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16),
|
||||
PLAY_SET_COOLDOWN(State.PLAY, Direction.OUTGOING, -1, 0x17),
|
||||
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18),
|
||||
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19),
|
||||
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A),
|
||||
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B),
|
||||
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C),
|
||||
PLAY_UNLOAD_CHUNK(State.PLAY, Direction.OUTGOING, -1, 0x1D),
|
||||
PLAY_CHANGE_GAME_STATE(State.PLAY, Direction.OUTGOING, 0x2B, 0x1E),
|
||||
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F),
|
||||
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20),
|
||||
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21),
|
||||
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22),
|
||||
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23),
|
||||
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24),
|
||||
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25),
|
||||
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26),
|
||||
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27),
|
||||
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28),
|
||||
PLAY_VEHICLE_MOVE(State.PLAY, Direction.OUTGOING, -1, 0x29),
|
||||
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A),
|
||||
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B),
|
||||
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
|
||||
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
|
||||
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
|
||||
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F),
|
||||
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
|
||||
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
|
||||
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
|
||||
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33),
|
||||
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34),
|
||||
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35),
|
||||
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36),
|
||||
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37),
|
||||
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38),
|
||||
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39),
|
||||
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A),
|
||||
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B),
|
||||
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C),
|
||||
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D),
|
||||
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_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),
|
||||
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45),
|
||||
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46),
|
||||
PLAY_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, -1, 0x47),
|
||||
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48),
|
||||
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49),
|
||||
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A),
|
||||
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B),
|
||||
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C),
|
||||
|
||||
PLAY_MAP_CHUNK_BULK(State.PLAY, Direction.OUTGOING, 0x26, -1),
|
||||
PLAY_SET_COMPRESSION(State.PLAY, Direction.OUTGOING, 0x46, -1),
|
||||
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1),;
|
||||
|
||||
private State state;
|
||||
private Direction direction;
|
||||
private int packetID;
|
||||
private int newPacketID = -1;
|
||||
|
||||
PacketType(State state, Direction direction, int packetID) {
|
||||
this.state = state;
|
||||
this.direction = direction;
|
||||
this.packetID = packetID;
|
||||
this.newPacketID = packetID;
|
||||
}
|
||||
|
||||
PacketType(State state, Direction direction, int packetID, int newPacketID) {
|
||||
this.state = state;
|
||||
this.direction = direction;
|
||||
this.packetID = packetID;
|
||||
this.newPacketID = newPacketID;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public int getPacketID() {
|
||||
return packetID;
|
||||
}
|
||||
|
||||
public int getNewPacketID() {
|
||||
return newPacketID;
|
||||
}
|
||||
|
||||
public static PacketType findNewPacket(State state, Direction direction, int id) {
|
||||
return newids.get(toShort((short) id, (short)direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType findOldPacket(State state, Direction direction, int id) {
|
||||
return oldids.get(toShort((short) id, (short)direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType getIncomingPacket(State state, int id) {
|
||||
return findNewPacket(state, Direction.INCOMING, id);
|
||||
}
|
||||
|
||||
public static PacketType getOutgoingPacket(State state, int id) {
|
||||
return findOldPacket(state, Direction.OUTGOING, id);
|
||||
}
|
||||
|
||||
private static short toShort(short id, short direction, short state) {
|
||||
return (short) ((id & 0x00FF) | (direction<<8) & 0x0F00 | (state << 12) & 0xF000);
|
||||
}
|
||||
|
||||
private static HashMap<Short, PacketType> oldids = new HashMap<Short, PacketType>();
|
||||
private static HashMap<Short, PacketType> newids = new HashMap<Short, PacketType>();
|
||||
static {
|
||||
for(PacketType pt : PacketType.values()) {
|
||||
oldids.put(toShort((short) pt.getPacketID(), (short)pt.getDirection().ordinal(), (short)pt.getState().ordinal()), pt);
|
||||
newids.put(toShort((short) pt.getNewPacketID(), (short)pt.getDirection().ordinal(), (short)pt.getState().ordinal()), pt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,182 +17,10 @@ import java.util.Map;
|
||||
|
||||
public class ItemSlotRewriter {
|
||||
|
||||
public static void rewrite1_9To1_8(ByteBuf input, ByteBuf output) throws CancelException {
|
||||
try {
|
||||
ItemStack item = readItemStack(input);
|
||||
fixIdsFrom1_9To1_8(item);
|
||||
writeItemStack(item, output);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while rewriting an item slot.");
|
||||
e.printStackTrace();
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rewrite1_8To1_9(ByteBuf input, ByteBuf output) throws CancelException {
|
||||
try {
|
||||
ItemStack item = readItemStack(input);
|
||||
fixIdsFrom1_8To1_9(item);
|
||||
writeItemStack(item, output);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while rewriting an item slot.");
|
||||
e.printStackTrace();
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void fixIdsFrom1_9To1_8(ItemStack item) {
|
||||
if (item != null) {
|
||||
if (item.id == Material.MONSTER_EGG.getId() && item.data == 0) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
if (tag != null && tag.get("EntityTag") instanceof CompoundTag) {
|
||||
CompoundTag entityTag = tag.get("EntityTag");
|
||||
if (entityTag.get("id") instanceof StringTag) {
|
||||
StringTag id = entityTag.get("id");
|
||||
if (ENTTIY_NAME_TO_ID.containsKey(id.getValue()))
|
||||
data = ENTTIY_NAME_TO_ID.get(id.getValue());
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
if (item.id == Material.POTION.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
if (tag != null && tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName);
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
if (item.id == 438) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
item.id = (short) Material.POTION.getId();
|
||||
if (tag != null && tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName) + 8192;
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void fixIdsFrom1_8To1_9(ItemStack item) {
|
||||
if (item != null) {
|
||||
if (item.id == Material.MONSTER_EGG.getId() && item.data != 0) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
CompoundTag entityTag = new CompoundTag("EntityTag");
|
||||
if (ENTTIY_ID_TO_NAME.containsKey(Integer.valueOf(item.data))) {
|
||||
StringTag id = new StringTag("id", ENTTIY_ID_TO_NAME.get(Integer.valueOf(item.data)));
|
||||
entityTag.put(id);
|
||||
tag.put(entityTag);
|
||||
}
|
||||
item.tag = tag;
|
||||
item.data = 0;
|
||||
}
|
||||
if (item.id == Material.POTION.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
if (item.data >= 16384) {
|
||||
item.id = 438; // splash id
|
||||
item.data = (short) (item.data - 8192);
|
||||
}
|
||||
if (POTION_ID_TO_NAME.containsKey(Integer.valueOf(item.data))) {
|
||||
String name = POTION_ID_TO_NAME.get(Integer.valueOf(item.data));
|
||||
StringTag potion = new StringTag("Potion", "minecraft:" + name);
|
||||
tag.put(potion);
|
||||
}
|
||||
item.tag = tag;
|
||||
item.data = 0;
|
||||
}
|
||||
if (item.id == Material.WRITTEN_BOOK.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
ListTag pages = tag.get("pages");
|
||||
if (pages == null) {
|
||||
pages = new ListTag("pages", Collections.<Tag>singletonList(new StringTag(OutgoingTransformer.fixJson(""))));
|
||||
tag.put(pages);
|
||||
item.tag = tag;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
if (!(pages.get(i) instanceof StringTag))
|
||||
continue;
|
||||
StringTag page = pages.get(i);
|
||||
page.setValue(OutgoingTransformer.fixJson(page.getValue()));
|
||||
}
|
||||
item.tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack readItemStack(ByteBuf input) throws IOException {
|
||||
short id = input.readShort();
|
||||
if (id < 0) {
|
||||
|
||||
return null;
|
||||
} else {
|
||||
ItemStack item = new ItemStack();
|
||||
item.id = id;
|
||||
item.amount = input.readByte();
|
||||
item.data = input.readShort();
|
||||
item.tag = PacketUtil.readNBT(input);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeItemStack(ItemStack item, ByteBuf output) throws IOException {
|
||||
if (item == null) {
|
||||
output.writeShort(-1);
|
||||
} else {
|
||||
output.writeShort(item.id);
|
||||
output.writeByte(item.amount);
|
||||
output.writeShort(item.data);
|
||||
PacketUtil.writeNBT(output, item.tag);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ItemStack {
|
||||
|
||||
public short id;
|
||||
public byte amount;
|
||||
public short data;
|
||||
public CompoundTag tag;
|
||||
|
||||
public static ItemStack fromBukkit(org.bukkit.inventory.ItemStack stack) {
|
||||
if(stack == null) return null;
|
||||
ItemStack item = new ItemStack();
|
||||
item.id = (short) stack.getTypeId();
|
||||
item.amount = (byte) stack.getAmount();
|
||||
item.data = stack.getData().getData();
|
||||
// TODO: nbt
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Integer> ENTTIY_NAME_TO_ID = new HashMap<>();
|
||||
private static Map<Integer, String> ENTTIY_ID_TO_NAME = new HashMap<>();
|
||||
|
||||
private static Map<String, Integer> POTION_NAME_TO_ID = new HashMap<>();
|
||||
private static Map<Integer, String> POTION_ID_TO_NAME = new HashMap<>();
|
||||
private static final Map<String, Integer> ENTTIY_NAME_TO_ID = new HashMap<>();
|
||||
private static final Map<Integer, String> ENTTIY_ID_TO_NAME = new HashMap<>();
|
||||
private static final Map<String, Integer> POTION_NAME_TO_ID = new HashMap<>();
|
||||
private static final Map<Integer, String> POTION_ID_TO_NAME = new HashMap<>();
|
||||
|
||||
static {
|
||||
/* Entities */
|
||||
@@ -311,6 +139,159 @@ public class ItemSlotRewriter {
|
||||
|
||||
}
|
||||
|
||||
public static void rewrite1_9To1_8(ByteBuf input, ByteBuf output) throws CancelException {
|
||||
try {
|
||||
ItemStack item = readItemStack(input);
|
||||
fixIdsFrom1_9To1_8(item);
|
||||
writeItemStack(item, output);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while rewriting an item slot.");
|
||||
e.printStackTrace();
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rewrite1_8To1_9(ByteBuf input, ByteBuf output) throws CancelException {
|
||||
try {
|
||||
ItemStack item = readItemStack(input);
|
||||
fixIdsFrom1_8To1_9(item);
|
||||
writeItemStack(item, output);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while rewriting an item slot.");
|
||||
e.printStackTrace();
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void fixIdsFrom1_9To1_8(ItemStack item) {
|
||||
if (item != null) {
|
||||
if (item.id == Material.MONSTER_EGG.getId() && item.data == 0) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
if (tag != null && tag.get("EntityTag") instanceof CompoundTag) {
|
||||
CompoundTag entityTag = tag.get("EntityTag");
|
||||
if (entityTag.get("id") instanceof StringTag) {
|
||||
StringTag id = entityTag.get("id");
|
||||
if (ENTTIY_NAME_TO_ID.containsKey(id.getValue()))
|
||||
data = ENTTIY_NAME_TO_ID.get(id.getValue());
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
if (item.id == Material.POTION.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
if (tag != null && tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName);
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
if (item.id == 438) {
|
||||
CompoundTag tag = item.tag;
|
||||
int data = 0;
|
||||
item.id = (short) Material.POTION.getId();
|
||||
if (tag != null && tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName) + 8192;
|
||||
}
|
||||
}
|
||||
item.tag = null;
|
||||
item.data = (short) data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void fixIdsFrom1_8To1_9(ItemStack item) {
|
||||
if (item != null) {
|
||||
if (item.id == Material.MONSTER_EGG.getId() && item.data != 0) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
CompoundTag entityTag = new CompoundTag("EntityTag");
|
||||
if (ENTTIY_ID_TO_NAME.containsKey((int) item.data)) {
|
||||
StringTag id = new StringTag("id", ENTTIY_ID_TO_NAME.get((int) item.data));
|
||||
entityTag.put(id);
|
||||
tag.put(entityTag);
|
||||
}
|
||||
item.tag = tag;
|
||||
item.data = 0;
|
||||
}
|
||||
if (item.id == Material.POTION.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
if (item.data >= 16384) {
|
||||
item.id = 438; // splash id
|
||||
item.data = (short) (item.data - 8192);
|
||||
}
|
||||
if (POTION_ID_TO_NAME.containsKey((int) item.data)) {
|
||||
String name = POTION_ID_TO_NAME.get((int) item.data);
|
||||
StringTag potion = new StringTag("Potion", "minecraft:" + name);
|
||||
tag.put(potion);
|
||||
}
|
||||
item.tag = tag;
|
||||
item.data = 0;
|
||||
}
|
||||
if (item.id == Material.WRITTEN_BOOK.getId()) {
|
||||
CompoundTag tag = item.tag;
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
ListTag pages = tag.get("pages");
|
||||
if (pages == null) {
|
||||
pages = new ListTag("pages", Collections.<Tag>singletonList(new StringTag(OutgoingTransformer.fixJson(""))));
|
||||
tag.put(pages);
|
||||
item.tag = tag;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
if (!(pages.get(i) instanceof StringTag))
|
||||
continue;
|
||||
StringTag page = pages.get(i);
|
||||
page.setValue(OutgoingTransformer.fixJson(page.getValue()));
|
||||
}
|
||||
item.tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack readItemStack(ByteBuf input) throws IOException {
|
||||
short id = input.readShort();
|
||||
if (id < 0) {
|
||||
|
||||
return null;
|
||||
} else {
|
||||
ItemStack item = new ItemStack();
|
||||
item.id = id;
|
||||
item.amount = input.readByte();
|
||||
item.data = input.readShort();
|
||||
item.tag = PacketUtil.readNBT(input);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeItemStack(ItemStack item, ByteBuf output) throws IOException {
|
||||
if (item == null) {
|
||||
output.writeShort(-1);
|
||||
} else {
|
||||
output.writeShort(item.id);
|
||||
output.writeByte(item.amount);
|
||||
output.writeShort(item.data);
|
||||
PacketUtil.writeNBT(output, item.tag);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerEntity(Integer id, String name) {
|
||||
ENTTIY_ID_TO_NAME.put(id, name);
|
||||
ENTTIY_NAME_TO_ID.put(name, id);
|
||||
@@ -320,4 +301,22 @@ public class ItemSlotRewriter {
|
||||
POTION_ID_TO_NAME.put(id, name);
|
||||
POTION_NAME_TO_ID.put(name, id);
|
||||
}
|
||||
|
||||
public static class ItemStack {
|
||||
|
||||
public short id;
|
||||
public byte amount;
|
||||
public short data;
|
||||
public CompoundTag tag;
|
||||
|
||||
public static ItemStack fromBukkit(org.bukkit.inventory.ItemStack stack) {
|
||||
if(stack == null) return null;
|
||||
ItemStack item = new ItemStack();
|
||||
item.id = (short) stack.getTypeId();
|
||||
item.amount = (byte) stack.getAmount();
|
||||
item.data = stack.getData().getData();
|
||||
// TODO: nbt
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package us.myles.ViaVersion.sounds;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum SoundCategory {
|
||||
|
||||
|
||||
@@ -14,20 +19,7 @@ public enum SoundCategory {
|
||||
AMBIENT("ambient", 8),
|
||||
VOICE("voice", 9);
|
||||
|
||||
private String name;
|
||||
private int id;
|
||||
|
||||
SoundCategory(String name, int id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
private final String name;
|
||||
private final int id;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package us.myles.ViaVersion.sounds;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum SoundEffect {
|
||||
|
||||
MOB_HORSE_ZOMBIE_IDLE("mob.horse.zombie.idle", "entity.zombie_horse.ambient", SoundCategory.NEUTRAL),
|
||||
@@ -249,21 +252,21 @@ public enum SoundEffect {
|
||||
MUSIC_GAME_END_CREDITS("music.game.end.credits", "music.credits", SoundCategory.MUSIC),
|
||||
MUSIC_GAME_CREATIVE("music.game.creative", "music.creative", SoundCategory.MUSIC);
|
||||
|
||||
private String name;
|
||||
private String newname;
|
||||
private SoundCategory cat;
|
||||
private boolean breaksound;
|
||||
private final String name;
|
||||
private final String newName;
|
||||
private final SoundCategory category;
|
||||
private final boolean breaksound;
|
||||
|
||||
SoundEffect(String name, String newname, SoundCategory cat) {
|
||||
this.cat = cat;
|
||||
this.newname = newname;
|
||||
this.category = cat;
|
||||
this.newName = newname;
|
||||
this.name = name;
|
||||
this.breaksound = name.startsWith("dig.");
|
||||
}
|
||||
|
||||
SoundEffect(String name, String newname, SoundCategory cat, boolean shouldIgnore) {
|
||||
this.cat = cat;
|
||||
this.newname = newname;
|
||||
this.category = cat;
|
||||
this.newName = newname;
|
||||
this.name = name;
|
||||
this.breaksound = name.startsWith("dig.") || shouldIgnore;
|
||||
}
|
||||
@@ -277,20 +280,4 @@ public enum SoundEffect {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getNewName() {
|
||||
return newname;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SoundCategory getCategory() {
|
||||
return cat;
|
||||
}
|
||||
|
||||
public boolean isBreakPlaceSound() {
|
||||
return breaksound;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,9 @@ import us.myles.ViaVersion.util.PacketUtil;
|
||||
import java.io.IOException;
|
||||
|
||||
public class IncomingTransformer {
|
||||
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
private final ConnectionInfo info;
|
||||
private boolean startedBlocking = false;
|
||||
|
||||
public IncomingTransformer(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
@@ -33,7 +35,7 @@ public class IncomingTransformer {
|
||||
if (packet.getPacketID() != -1) {
|
||||
packetID = packet.getPacketID();
|
||||
}
|
||||
if (ViaVersion.getInstance().isDebug()) {
|
||||
if (plugin.isDebug()) {
|
||||
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("Direction " + packet.getDirection().name() + " Packet Type: " + packet + " New ID: " + packetID + " Original: " + original + " Size: " + input.readableBytes());
|
||||
}
|
||||
@@ -41,6 +43,11 @@ public class IncomingTransformer {
|
||||
if (packet == PacketType.PLAY_TP_CONFIRM || packet == PacketType.PLAY_VEHICLE_MOVE_REQUEST) { //TODO handle client-sided horse riding
|
||||
throw new CancelException();
|
||||
}
|
||||
// Handle movement increment
|
||||
// Update idle status (player, position, look, positionandlook)
|
||||
if(packet == PacketType.PLAY_PLAYER || packet == PacketType.PLAY_PLAYER_POSITION_REQUEST || packet == PacketType.PLAY_PLAYER_LOOK_REQUEST || packet == PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST) {
|
||||
info.incrementIdlePacket();
|
||||
}
|
||||
PacketUtil.writeVarInt(packetID, output);
|
||||
if (packet == PacketType.HANDSHAKE) {
|
||||
int protVer = PacketUtil.readVarInt(input);
|
||||
@@ -87,14 +94,30 @@ public class IncomingTransformer {
|
||||
}
|
||||
if (packet == PacketType.PLAY_PLAYER_DIGGING) {
|
||||
int status = input.readByte() & 0xFF; // unsign
|
||||
if (status == 5 && startedBlocking) {
|
||||
// stopped blocking
|
||||
startedBlocking = false;
|
||||
sendSecondHandItem(null);
|
||||
}
|
||||
if (status == 6) { // item swap
|
||||
throw new CancelException();
|
||||
}
|
||||
output.writeByte(status);
|
||||
// write remaining bytes
|
||||
output.writeBytes(input);
|
||||
Long position = input.readLong();
|
||||
output.writeLong(position);
|
||||
|
||||
int face = input.readUnsignedByte();
|
||||
output.writeByte(face);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_HELD_ITEM_CHANGE_REQUEST) {
|
||||
if (startedBlocking) {
|
||||
// stopped blocking
|
||||
startedBlocking = false;
|
||||
sendSecondHandItem(null);
|
||||
}
|
||||
}
|
||||
if (packet == PacketType.PLAY_CLICK_WINDOW) {
|
||||
// if placed in new slot, reject :)
|
||||
int windowID = input.readUnsignedByte();
|
||||
@@ -163,6 +186,19 @@ public class IncomingTransformer {
|
||||
PacketUtil.readVarInt(input);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_ENTITY_ACTION) {
|
||||
int playerId = PacketUtil.readVarInt(input);
|
||||
int action = PacketUtil.readVarInt(input);
|
||||
int jump = PacketUtil.readVarInt(input);
|
||||
if (action == 6 || action == 8) //Ignore stop jumping / start elytra flying
|
||||
throw new CancelException();
|
||||
if (action == 7) //Change open horse inventory to the 1.8 value
|
||||
action = 6;
|
||||
PacketUtil.writeVarInt(playerId, output);
|
||||
PacketUtil.writeVarInt(action, output);
|
||||
PacketUtil.writeVarInt(jump, output);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_USE_ENTITY) {
|
||||
int target = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(target, output);
|
||||
@@ -188,40 +224,45 @@ public class IncomingTransformer {
|
||||
byte[] b = new byte[input.readableBytes()];
|
||||
input.readBytes(b);
|
||||
// patch books
|
||||
if (name.equals("MC|BSign")) {
|
||||
ByteBuf in = Unpooled.wrappedBuffer(b);
|
||||
try {
|
||||
ItemSlotRewriter.ItemStack stack = ItemSlotRewriter.readItemStack(in);
|
||||
stack.id = (short) Material.WRITTEN_BOOK.getId();
|
||||
// write
|
||||
ItemSlotRewriter.writeItemStack(stack, output);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
switch (name) {
|
||||
case "MC|BSign": {
|
||||
ByteBuf in = Unpooled.wrappedBuffer(b);
|
||||
try {
|
||||
ItemSlotRewriter.ItemStack stack = ItemSlotRewriter.readItemStack(in);
|
||||
if (stack != null)
|
||||
stack.id = (short) Material.WRITTEN_BOOK.getId();
|
||||
// write
|
||||
ItemSlotRewriter.writeItemStack(stack, output);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else if (name.equals("MC|AutoCmd")) {
|
||||
ByteBuf in = Unpooled.wrappedBuffer(b);
|
||||
int x = in.readInt();
|
||||
int y = in.readInt();
|
||||
int z = in.readInt();
|
||||
String command = PacketUtil.readString(in);
|
||||
boolean flag = in.readBoolean();
|
||||
case "MC|AutoCmd": {
|
||||
ByteBuf in = Unpooled.wrappedBuffer(b);
|
||||
int x = in.readInt();
|
||||
int y = in.readInt();
|
||||
int z = in.readInt();
|
||||
String command = PacketUtil.readString(in);
|
||||
boolean flag = in.readBoolean();
|
||||
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_PLUGIN_MESSAGE_REQUEST.getPacketID(), output);
|
||||
PacketUtil.writeString("MC|AdvCdm", output);
|
||||
output.writeByte(0);
|
||||
output.writeInt(x);
|
||||
output.writeInt(y);
|
||||
output.writeInt(z);
|
||||
PacketUtil.writeString(command, output);
|
||||
output.writeBoolean(flag);
|
||||
return;
|
||||
} else if (name.equals("MC|AdvCmd")) {
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_PLUGIN_MESSAGE_REQUEST.getPacketID(), output);
|
||||
PacketUtil.writeString("MC|AdvCdm", output);
|
||||
output.writeBytes(b);
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_PLUGIN_MESSAGE_REQUEST.getPacketID(), output);
|
||||
PacketUtil.writeString("MC|AdvCdm", output);
|
||||
output.writeByte(0);
|
||||
output.writeInt(x);
|
||||
output.writeInt(y);
|
||||
output.writeInt(z);
|
||||
PacketUtil.writeString(command, output);
|
||||
output.writeBoolean(flag);
|
||||
return;
|
||||
}
|
||||
case "MC|AdvCmd":
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_PLUGIN_MESSAGE_REQUEST.getPacketID(), output);
|
||||
PacketUtil.writeString("MC|AdvCdm", output);
|
||||
output.writeBytes(b);
|
||||
break;
|
||||
}
|
||||
output.writeBytes(b);
|
||||
}
|
||||
@@ -230,9 +271,9 @@ public class IncomingTransformer {
|
||||
output.writeLong(position);
|
||||
int face = PacketUtil.readVarInt(input);
|
||||
output.writeByte(face);
|
||||
int hand = PacketUtil.readVarInt(input);
|
||||
PacketUtil.readVarInt(input);
|
||||
|
||||
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
|
||||
ItemStack inHand = plugin.getHandItem(info);
|
||||
try {
|
||||
ItemSlotRewriter.ItemStack item = ItemSlotRewriter.ItemStack.fromBukkit(inHand);
|
||||
ItemSlotRewriter.fixIdsFrom1_9To1_8(item);
|
||||
@@ -240,23 +281,39 @@ public class IncomingTransformer {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
short curX = input.readUnsignedByte();
|
||||
output.writeByte(curX);
|
||||
short curY = input.readUnsignedByte();
|
||||
output.writeByte(curY);
|
||||
short curZ = input.readUnsignedByte();
|
||||
output.writeByte(curZ);
|
||||
return;
|
||||
short curX = input.readUnsignedByte();
|
||||
output.writeByte(curX);
|
||||
short curY = input.readUnsignedByte();
|
||||
output.writeByte(curY);
|
||||
short curZ = input.readUnsignedByte();
|
||||
output.writeByte(curZ);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_USE_ITEM) {
|
||||
int hand = PacketUtil.readVarInt(input);
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output);
|
||||
// Simulate using item :)
|
||||
output.writeLong(-1L);
|
||||
output.writeByte(255);
|
||||
// write item in hand
|
||||
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
|
||||
ItemStack inHand = plugin.getHandItem(info);
|
||||
if (inHand != null && plugin.isShieldBlocking()) {
|
||||
if (inHand.getType().name().endsWith("SWORD")) {
|
||||
// blocking?
|
||||
if (hand == 0) {
|
||||
if (!startedBlocking) {
|
||||
startedBlocking = true;
|
||||
ItemSlotRewriter.ItemStack shield = new ItemSlotRewriter.ItemStack();
|
||||
shield.id = 442;
|
||||
shield.amount = 1;
|
||||
shield.data = 0;
|
||||
sendSecondHandItem(shield);
|
||||
}
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
ItemSlotRewriter.ItemStack item = ItemSlotRewriter.ItemStack.fromBukkit(inHand);
|
||||
ItemSlotRewriter.fixIdsFrom1_9To1_8(item);
|
||||
@@ -265,9 +322,9 @@ public class IncomingTransformer {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
output.writeByte(-1);
|
||||
output.writeByte(-1);
|
||||
output.writeByte(-1);
|
||||
output.writeByte(0); //Is zero in 1.8, not -1
|
||||
output.writeByte(0);
|
||||
output.writeByte(0);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_CREATIVE_INVENTORY_ACTION) {
|
||||
@@ -278,4 +335,18 @@ public class IncomingTransformer {
|
||||
}
|
||||
output.writeBytes(input);
|
||||
}
|
||||
|
||||
private void sendSecondHandItem(ItemSlotRewriter.ItemStack o) {
|
||||
ByteBuf buf = info.getChannel().alloc().buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_EQUIPMENT.getNewPacketID(), buf);
|
||||
PacketUtil.writeVarInt(info.getEntityID(), buf);
|
||||
PacketUtil.writeVarInt(1, buf); // slot
|
||||
// write shield
|
||||
try {
|
||||
ItemSlotRewriter.writeItemStack(o, buf);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
info.sendRawPacket(buf);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
package us.myles.ViaVersion.transformers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||
import org.spacehq.mc.protocol.util.NetUtil;
|
||||
import org.spacehq.opennbt.NBTIO;
|
||||
import org.spacehq.opennbt.tag.builtin.ByteTag;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import org.spacehq.opennbt.tag.builtin.StringTag;
|
||||
@@ -17,7 +15,14 @@ import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.chunks.Chunk;
|
||||
import us.myles.ViaVersion.chunks.ChunkManager;
|
||||
import us.myles.ViaVersion.metadata.MetaIndex;
|
||||
import us.myles.ViaVersion.metadata.MetadataRewriter;
|
||||
import us.myles.ViaVersion.metadata.MetadataRewriter.Entry;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
import us.myles.ViaVersion.slot.ItemSlotRewriter;
|
||||
@@ -26,31 +31,53 @@ import us.myles.ViaVersion.util.EntityUtil;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static us.myles.ViaVersion.util.PacketUtil.*;
|
||||
|
||||
public class OutgoingTransformer {
|
||||
private final ConnectionInfo info;
|
||||
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
private boolean cancel = false;
|
||||
|
||||
private Map<Integer, UUID> uuidMap = new HashMap<Integer, UUID>();
|
||||
private Map<Integer, EntityType> clientEntityTypes = new HashMap<Integer, EntityType>();
|
||||
private Map<Integer, Integer> vehicleMap = new HashMap<>();
|
||||
public class OutgoingTransformer {
|
||||
|
||||
private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
private final ConnectionInfo info;
|
||||
private final Map<Integer, UUID> uuidMap = new HashMap<>();
|
||||
private final Map<Integer, EntityType> clientEntityTypes = new HashMap<>();
|
||||
private final Map<Integer, Integer> vehicleMap = new HashMap<>();
|
||||
private final Set<Integer> validBlocking = new HashSet<>();
|
||||
private final Set<Integer> knownHolograms = new HashSet<>();
|
||||
private final Map<Integer, BossBar> bossBarMap = new HashMap<>();
|
||||
private boolean autoTeam = false;
|
||||
private boolean teamExists = false;
|
||||
|
||||
public OutgoingTransformer(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||
if (cancel) {
|
||||
throw new CancelException();
|
||||
public static String fixJson(String line) {
|
||||
if (line == null || line.equalsIgnoreCase("null")) {
|
||||
line = "{\"text\":\"\"}";
|
||||
} else {
|
||||
if ((!line.startsWith("\"") || !line.endsWith("\"")) && (!line.startsWith("{") || !line.endsWith("}"))) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("text", line);
|
||||
return obj.toJSONString();
|
||||
}
|
||||
if (line.startsWith("\"") && line.endsWith("\"")) {
|
||||
line = "{\"text\":" + line + "}";
|
||||
}
|
||||
}
|
||||
try {
|
||||
new JSONParser().parse(line);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github: " + e.getMessage());
|
||||
return "{\"text\":\"\"}";
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||
PacketType packet = PacketType.getOutgoingPacket(info.getState(), packetID);
|
||||
int original = packetID;
|
||||
if (packet == null) {
|
||||
@@ -74,7 +101,7 @@ public class OutgoingTransformer {
|
||||
int catid = 0;
|
||||
String newname = name;
|
||||
if (effect != null) {
|
||||
if (effect.isBreakPlaceSound()) {
|
||||
if (effect.isBreaksound()) {
|
||||
throw new CancelException();
|
||||
}
|
||||
catid = effect.getCategory().getId();
|
||||
@@ -86,8 +113,10 @@ public class OutgoingTransformer {
|
||||
}
|
||||
if (packet == PacketType.PLAY_EFFECT) {
|
||||
int effectid = input.readInt();
|
||||
if (effectid >= 1000 && effectid < 2000) //Sound effect
|
||||
if (effectid >= 1000 && effectid < 2000 && effectid != 1005) //Sound effect
|
||||
throw new CancelException();
|
||||
if (effectid == 1005) //Fix jukebox
|
||||
effectid = 1010;
|
||||
output.writeInt(effectid);
|
||||
}
|
||||
if (packet == PacketType.PLAY_ATTACH_ENTITY) {
|
||||
@@ -197,13 +226,15 @@ public class OutgoingTransformer {
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_ENTITY_TELEPORT) {
|
||||
// Port this so that it relative moves :P
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
PacketUtil.writeVarInt(id, output);
|
||||
|
||||
int x = input.readInt();
|
||||
output.writeDouble(x / 32D);
|
||||
int y = input.readInt();
|
||||
if (plugin.isHologramPatch() & knownHolograms.contains(id)) {
|
||||
y = (int) ((y) + (plugin.getHologramYOffset() * 32D));
|
||||
}
|
||||
output.writeDouble(y / 32D);
|
||||
int z = input.readInt();
|
||||
output.writeDouble(z / 32D);
|
||||
@@ -224,6 +255,9 @@ public class OutgoingTransformer {
|
||||
int x = input.readByte();
|
||||
output.writeShort(x * 128);
|
||||
int y = input.readByte();
|
||||
if (plugin.isHologramPatch() & knownHolograms.contains(id)) {
|
||||
y = (int) ((y) + plugin.getHologramYOffset());
|
||||
}
|
||||
output.writeShort(y * 128);
|
||||
int z = input.readByte();
|
||||
output.writeShort(z * 128);
|
||||
@@ -244,12 +278,16 @@ public class OutgoingTransformer {
|
||||
short x = (short) (input.readByte());
|
||||
output.writeShort(x * 128);
|
||||
short y = (short) (input.readByte());
|
||||
if (plugin.isHologramPatch() & knownHolograms.contains(id)) {
|
||||
y = (short) (y - 1);
|
||||
}
|
||||
output.writeShort(y * 128);
|
||||
short z = (short) (input.readByte());
|
||||
output.writeShort(z * 128);
|
||||
|
||||
boolean onGround = input.readBoolean();
|
||||
output.writeBoolean(onGround);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -300,7 +338,24 @@ public class OutgoingTransformer {
|
||||
slot += 1; // add 1 so it's now 2-5
|
||||
}
|
||||
PacketUtil.writeVarInt(slot, output);
|
||||
|
||||
if (slot == 0) {
|
||||
// Read aheat for sword
|
||||
input.markReaderIndex();
|
||||
short itemID = input.readShort();
|
||||
if (itemID != -1) {
|
||||
Material m = Material.getMaterial(itemID);
|
||||
if (m != null) {
|
||||
if (m.name().endsWith("SWORD")) {
|
||||
validBlocking.add(id);
|
||||
} else {
|
||||
validBlocking.remove(id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validBlocking.remove(id);
|
||||
}
|
||||
input.resetReaderIndex();
|
||||
}
|
||||
ItemSlotRewriter.rewrite1_8To1_9(input, output);
|
||||
return;
|
||||
}
|
||||
@@ -336,7 +391,14 @@ public class OutgoingTransformer {
|
||||
int[] toDestroy = PacketUtil.readVarInts(count, input);
|
||||
for (int entityID : toDestroy) {
|
||||
clientEntityTypes.remove(entityID);
|
||||
knownHolograms.remove(entityID);
|
||||
PacketUtil.writeVarInt(entityID, output);
|
||||
|
||||
// Remvoe boss bar
|
||||
BossBar bar = bossBarMap.remove(entityID);
|
||||
if (bar != null) {
|
||||
bar.hide();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -546,13 +608,33 @@ public class OutgoingTransformer {
|
||||
if (packet == PacketType.PLAY_JOIN_GAME) {
|
||||
int id = input.readInt();
|
||||
clientEntityTypes.put(id, EntityType.PLAYER);
|
||||
info.setEntityID(id);
|
||||
output.writeInt(id);
|
||||
output.writeBytes(input);
|
||||
// send fake team
|
||||
|
||||
int gamemode = input.readUnsignedByte();
|
||||
output.writeByte(gamemode);
|
||||
int dimension = input.readByte();
|
||||
output.writeByte(dimension);
|
||||
int difficulty = input.readUnsignedByte();
|
||||
if (info.getProtocol() >= 108) {
|
||||
// 1.8.1 Pre 2
|
||||
output.writeInt(difficulty);
|
||||
} else {
|
||||
output.writeByte(difficulty);
|
||||
}
|
||||
int maxPlayers = input.readUnsignedByte();
|
||||
output.writeByte(maxPlayers);
|
||||
String level = PacketUtil.readString(input);
|
||||
PacketUtil.writeString(level, output);
|
||||
boolean reducedDebug = input.readBoolean();
|
||||
output.writeBoolean(reducedDebug);
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_SERVER_DIFFICULTY) {
|
||||
if (plugin.isAutoTeam()) {
|
||||
autoTeam = true;
|
||||
sendTeamPacket(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (packet == PacketType.PLAY_SPAWN_PLAYER) {
|
||||
int id = PacketUtil.readVarInt(input);
|
||||
@@ -627,7 +709,7 @@ public class OutgoingTransformer {
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = PacketUtil.readString(input);
|
||||
if (plugin.isAutoTeam() && name.equalsIgnoreCase(info.getUsername())) {
|
||||
if (autoTeam && name.equalsIgnoreCase(info.getUsername())) {
|
||||
if (mode == 4) {
|
||||
// since removing add to auto team
|
||||
plugin.run(new Runnable() {
|
||||
@@ -655,21 +737,18 @@ public class OutgoingTransformer {
|
||||
if (action == 1) { // update spawner
|
||||
try {
|
||||
int index = input.readerIndex();
|
||||
DataInputStream stream = new DataInputStream(new ByteBufInputStream(input));
|
||||
CompoundTag tag = (CompoundTag) NBTIO.readTag(stream);
|
||||
CompoundTag tag = PacketUtil.readNBT(input);
|
||||
if (tag != null && tag.contains("EntityId")) {
|
||||
String entity = (String) tag.get("EntityId").getValue();
|
||||
CompoundTag spawn = new CompoundTag("SpawnData");
|
||||
spawn.put(new StringTag("id", entity));
|
||||
tag.put(spawn);
|
||||
DataOutputStream out = new DataOutputStream(new ByteBufOutputStream(output));
|
||||
NBTIO.writeTag(out, tag);
|
||||
PacketUtil.writeNBT(output, tag);
|
||||
} else if (tag != null) { // EntityID does not exist
|
||||
CompoundTag spawn = new CompoundTag("SpawnData");
|
||||
spawn.put(new StringTag("id", "AreaEffectCloud")); //Make spawners show up as empty when no EntityId is given.
|
||||
tag.put(spawn);
|
||||
DataOutputStream out = new DataOutputStream(new ByteBufOutputStream(output));
|
||||
NBTIO.writeTag(out, tag);
|
||||
PacketUtil.writeNBT(output, spawn);
|
||||
} else { //There doesn't exist any NBT tag
|
||||
input.readerIndex(index);
|
||||
output.writeBytes(input, input.readableBytes());
|
||||
@@ -699,114 +778,46 @@ public class OutgoingTransformer {
|
||||
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)
|
||||
int chunkX = input.readInt();
|
||||
int chunkZ = input.readInt();
|
||||
output.writeInt(chunkX);
|
||||
output.writeInt(chunkZ);
|
||||
|
||||
|
||||
boolean groundUp = input.readBoolean();
|
||||
output.writeBoolean(groundUp);
|
||||
|
||||
int bitMask = input.readUnsignedShort();
|
||||
int size = PacketUtil.readVarInt(input);
|
||||
byte[] data = new byte[size];
|
||||
input.readBytes(data);
|
||||
// if (bitMask == 0 && groundUp) {
|
||||
// // if 256
|
||||
// output.clear();
|
||||
// PacketUtil.writeVarInt(PacketType.PLAY_UNLOAD_CHUNK.getNewPacketID(), output);
|
||||
// output.writeInt(chunkX);
|
||||
// output.writeInt(chunkZ);
|
||||
// System.out.println("Sending unload chunk " + chunkX + " " + chunkZ + " - " + size + " bulk: " + bulk);
|
||||
// return;
|
||||
// }
|
||||
boolean sk = false;
|
||||
if (info.getLastPacket().getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) {
|
||||
try {
|
||||
sk = ReflectionUtil.get(info.getLastPacket(), "d", boolean.class);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Column read = NetUtil.readOldChunkData(chunkX, chunkZ, groundUp, bitMask, data, true, sk);
|
||||
// Write chunk section array :((
|
||||
ByteBuf temp = output.alloc().buffer();
|
||||
try {
|
||||
int bitmask = NetUtil.writeNewColumn(temp, read, groundUp, sk);
|
||||
PacketUtil.writeVarInt(bitmask, output);
|
||||
PacketUtil.writeVarInt(temp.readableBytes(), output);
|
||||
output.writeBytes(temp);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// Read chunk
|
||||
ChunkManager chunkManager = info.getChunkManager();
|
||||
Chunk chunk = chunkManager.readChunk(input);
|
||||
if(chunk == null) {
|
||||
throw new CancelException();
|
||||
}
|
||||
|
||||
// Write chunk
|
||||
chunkManager.writeChunk(chunk, output);
|
||||
return;
|
||||
}
|
||||
output.writeBytes(input);
|
||||
}
|
||||
private void sendCreateTeam() {
|
||||
ByteBuf buf = info.getChannel().alloc().buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_TEAM.getNewPacketID(), buf);
|
||||
PacketUtil.writeString("viaversion", buf);
|
||||
buf.writeByte(0); // make team
|
||||
PacketUtil.writeString("viaversion", buf);
|
||||
PacketUtil.writeString("", buf); // prefix
|
||||
PacketUtil.writeString("", buf); // suffix
|
||||
buf.writeByte(0); // friendly fire
|
||||
PacketUtil.writeString("", buf); // nametags
|
||||
PacketUtil.writeString("never", buf); // collision rule :)
|
||||
buf.writeByte(0); // color
|
||||
PacketUtil.writeVarInt(0, buf); // player count
|
||||
info.sendRawPacket(buf);
|
||||
}
|
||||
|
||||
private void sendTeamPacket(boolean b) {
|
||||
ByteBuf buf = info.getChannel().alloc().buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_TEAM.getNewPacketID(), buf);
|
||||
PacketUtil.writeString("viaversion", buf); // Use viaversion as name
|
||||
if (b) {
|
||||
// add
|
||||
buf.writeByte(0); // make team
|
||||
PacketUtil.writeString("viaversion", buf);
|
||||
PacketUtil.writeString("", buf); // prefix
|
||||
PacketUtil.writeString("", buf); // suffix
|
||||
buf.writeByte(0); // friendly fire
|
||||
PacketUtil.writeString("", buf); // nametags
|
||||
PacketUtil.writeString("never", buf); // collision rule :)
|
||||
buf.writeByte(0); // color
|
||||
if (!teamExists) {
|
||||
buf.writeByte(0); // make team
|
||||
PacketUtil.writeString("viaversion", buf);
|
||||
PacketUtil.writeString("", buf); // prefix
|
||||
PacketUtil.writeString("", buf); // suffix
|
||||
buf.writeByte(0); // friendly fire
|
||||
PacketUtil.writeString("", buf); // nametags
|
||||
PacketUtil.writeString("never", buf); // collision rule :)
|
||||
buf.writeByte(0); // color
|
||||
} else
|
||||
buf.writeByte(3);
|
||||
PacketUtil.writeVarInt(1, buf); // player count
|
||||
PacketUtil.writeString(info.getUsername(), buf); // us
|
||||
} else {
|
||||
buf.writeByte(1); // remove team
|
||||
}
|
||||
teamExists = b;
|
||||
info.sendRawPacket(buf);
|
||||
}
|
||||
|
||||
public static String fixJson(String line) {
|
||||
if (line == null || line.equalsIgnoreCase("null")) {
|
||||
line = "{\"text\":\"\"}";
|
||||
} else {
|
||||
if ((!line.startsWith("\"") || !line.endsWith("\"")) && (!line.startsWith("{") || !line.endsWith("}"))) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("text", line);
|
||||
return obj.toJSONString();
|
||||
}
|
||||
if (line.startsWith("\"") && line.endsWith("\"")) {
|
||||
line = "{\"text\":" + line + "}";
|
||||
}
|
||||
}
|
||||
try {
|
||||
new JSONParser().parse(line);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github: " + e.getMessage());
|
||||
return "{\"text\":\"\"}";
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private void transformMetadata(int entityID, ByteBuf input, ByteBuf output) throws CancelException {
|
||||
EntityType type = clientEntityTypes.get(entityID);
|
||||
if (type == null) {
|
||||
@@ -815,9 +826,113 @@ public class OutgoingTransformer {
|
||||
return;
|
||||
}
|
||||
List<MetadataRewriter.Entry> list = MetadataRewriter.readMetadata1_8(type, input);
|
||||
for (MetadataRewriter.Entry entry : list) {
|
||||
handleMetadata(entityID, entry, type);
|
||||
}
|
||||
// Fix: wither (crash fix)
|
||||
if (type == EntityType.WITHER) {
|
||||
// Remove custom value if already exist
|
||||
Iterator<Entry> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry e = it.next();
|
||||
if (e.getOldID() == 10) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
list.add(new Entry(MetaIndex.WITHER_PROPERTIES, (byte) 0, 10));
|
||||
}
|
||||
// Fix: Dragon (crash fix)
|
||||
if (type == EntityType.ENDER_DRAGON) {
|
||||
// Remove custom value if already exist
|
||||
Iterator<Entry> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry e = it.next();
|
||||
if (e.getOldID() == 11) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
list.add(new Entry(MetaIndex.ENDERDRAGON_PHASE, (byte) 0, 11));
|
||||
}
|
||||
MetadataRewriter.writeMetadata1_9(type, list, output);
|
||||
}
|
||||
|
||||
private void handleMetadata(int entityID, MetadataRewriter.Entry entry, EntityType type) {
|
||||
// This handles old IDs
|
||||
if (type == EntityType.PLAYER) {
|
||||
if (entry.getOldID() == 0) {
|
||||
// Byte
|
||||
byte data = (byte) entry.getValue();
|
||||
if (entityID != info.getEntityID() && plugin.isShieldBlocking()) {
|
||||
if ((data & 0x10) == 0x10) {
|
||||
if (validBlocking.contains(entityID)) {
|
||||
ItemSlotRewriter.ItemStack shield = new ItemSlotRewriter.ItemStack();
|
||||
shield.id = 442;
|
||||
shield.amount = 1;
|
||||
shield.data = 0;
|
||||
sendSecondHandItem(entityID, shield);
|
||||
}
|
||||
} else {
|
||||
sendSecondHandItem(entityID, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == EntityType.ARMOR_STAND && plugin.isHologramPatch()) {
|
||||
if (entry.getOldID() == 0) {
|
||||
byte data = (byte) entry.getValue();
|
||||
if ((data & 0x20) == 0x20) {
|
||||
if (!knownHolograms.contains(entityID)) {
|
||||
knownHolograms.add(entityID);
|
||||
// Send movement
|
||||
ByteBuf buf = info.getChannel().alloc().buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_RELATIVE_MOVE.getNewPacketID(), buf);
|
||||
PacketUtil.writeVarInt(entityID, buf);
|
||||
buf.writeShort(0);
|
||||
buf.writeShort((short) (128D * (plugin.getHologramYOffset() * 32D)));
|
||||
buf.writeShort(0);
|
||||
buf.writeBoolean(true);
|
||||
info.sendRawPacket(buf, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Boss bar
|
||||
if (plugin.isBossbarPatch()) {
|
||||
if (type == EntityType.ENDER_DRAGON || type == EntityType.WITHER) {
|
||||
if (entry.getOldID() == 2) {
|
||||
BossBar bar = bossBarMap.get(entityID);
|
||||
String title = (String) entry.getValue();
|
||||
title = title.isEmpty() ? (type == EntityType.ENDER_DRAGON ? "Ender Dragon" : "Wither") : title;
|
||||
if (bar == null) {
|
||||
bar = ViaVersion.getInstance().createBossBar(title, BossColor.PINK, BossStyle.SOLID);
|
||||
bossBarMap.put(entityID, bar);
|
||||
bar.addPlayer(info.getPlayer());
|
||||
bar.show();
|
||||
} else {
|
||||
bar.setTitle(title);
|
||||
}
|
||||
} else if (entry.getOldID() == 6 && !plugin.isBossbarAntiflicker()) { // If anti flicker is enabled, don't update health
|
||||
BossBar bar = bossBarMap.get(entityID);
|
||||
// Make health range between 0 and 1
|
||||
float maxHealth = type == EntityType.ENDER_DRAGON ? 200.0f : 300.0f;
|
||||
float health = Math.max(0.0f, Math.min(((float) entry.getValue()) / maxHealth, 1.0f));
|
||||
if (bar == null) {
|
||||
String title = type == EntityType.ENDER_DRAGON ? "Ender Dragon" : "Wither";
|
||||
bar = ViaVersion.getInstance().createBossBar(title, health, BossColor.PINK, BossStyle.SOLID);
|
||||
bossBarMap.put(entityID, bar);
|
||||
bar.addPlayer(info.getPlayer());
|
||||
bar.show();
|
||||
} else {
|
||||
bar.setHealth(health);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private UUID getUUID(int id) {
|
||||
if (uuidMap.containsKey(id)) {
|
||||
@@ -829,4 +944,18 @@ public class OutgoingTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private void sendSecondHandItem(int entityID, ItemSlotRewriter.ItemStack o) {
|
||||
|
||||
ByteBuf buf = info.getChannel().alloc().buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_EQUIPMENT.getNewPacketID(), buf);
|
||||
PacketUtil.writeVarInt(entityID, buf);
|
||||
PacketUtil.writeVarInt(1, buf); // slot
|
||||
// write shield
|
||||
try {
|
||||
ItemSlotRewriter.writeItemStack(o, buf);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
info.sendRawPacket(buf, true);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
package us.myles.ViaVersion.update;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateListener implements Listener {
|
||||
|
||||
private Plugin plugin;
|
||||
|
||||
public UpdateListener(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
|
||||
@@ -98,7 +98,7 @@ public class UpdateUtil {
|
||||
URL url = new URL(URL + PLUGIN + "?" + System.currentTimeMillis());
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setUseCaches(true);
|
||||
connection.addRequestProperty("User-Agent", "Mozilla/4.76");
|
||||
connection.addRequestProperty("User-Agent", "ViaVersion " + ViaVersion.getInstance().getVersion());
|
||||
connection.setDoOutput(true);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String input;
|
||||
|
||||
@@ -9,7 +9,7 @@ public class Version implements Comparable<Version> {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("Version can not be null");
|
||||
|
||||
if (value.matches("^[0-9]+(\\.[0-9]+)*$") == false)
|
||||
if (!value.matches("^[0-9]+(\\.[0-9]+)*$"))
|
||||
throw new IllegalArgumentException("Invalid version format");
|
||||
|
||||
String[] split = value.split("\\.");
|
||||
@@ -19,16 +19,6 @@ public class Version implements Comparable<Version> {
|
||||
parts[i] = Integer.parseInt(split[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String[] split = new String[parts.length];
|
||||
|
||||
for (int i = 0; i < parts.length; i += 1)
|
||||
split[i] = String.valueOf(parts[i]);
|
||||
|
||||
return StringUtils.join(split, ".");
|
||||
}
|
||||
|
||||
public static int compare(Version verA, Version verB) {
|
||||
if (verA == verB) return 0;
|
||||
if (verA == null) return -1;
|
||||
@@ -46,18 +36,25 @@ public class Version implements Comparable<Version> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean equals(Version verA, Version verB) {
|
||||
return verA == verB || verA != null && verB != null && compare(verA, verB) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String[] split = new String[parts.length];
|
||||
|
||||
for (int i = 0; i < parts.length; i += 1)
|
||||
split[i] = String.valueOf(parts[i]);
|
||||
|
||||
return StringUtils.join(split, ".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Version that) {
|
||||
return compare(this, that);
|
||||
}
|
||||
|
||||
public static boolean equals(Version verA, Version verB) {
|
||||
if (verA == verB) return true;
|
||||
if (verA == null) return false;
|
||||
if (verB == null) return false;
|
||||
return compare(verA, verB) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
return that instanceof Version && equals(this, (Version) that);
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
package us.myles.ViaVersion.util;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Configuration extends YamlConfiguration {
|
||||
private List<String> mainHeader = Lists.newArrayList();
|
||||
private final Map<String, List<String>> headers = Maps.newConcurrentMap();
|
||||
private final File file;
|
||||
private boolean loadHeaders;
|
||||
|
||||
public Configuration(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the main header displayed at top of config.
|
||||
*
|
||||
* @param header header
|
||||
*/
|
||||
public void mainHeader(String... header) {
|
||||
mainHeader = Arrays.asList(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main header displayed at top of config.
|
||||
*
|
||||
* @return header
|
||||
*/
|
||||
public List<String> mainHeader() {
|
||||
return mainHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set option header.
|
||||
*
|
||||
* @param key of option (or section)
|
||||
* @param header of option (or section)
|
||||
*/
|
||||
public void header(String key, String... header) {
|
||||
// String value = Joiner.on('\n').join(header);
|
||||
headers.put(key, Arrays.asList(header));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header of option
|
||||
*
|
||||
* @param key of option (or section)
|
||||
* @return Header
|
||||
*/
|
||||
public List<String> header(String key) {
|
||||
return headers.get(key);
|
||||
}
|
||||
|
||||
public <T> T get(String key, Class<T> type) {
|
||||
return type.cast(get(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload config from file.
|
||||
*/
|
||||
public void reload() {
|
||||
reload(headers.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload config from file.
|
||||
*
|
||||
* @param loadHeaders Whether or not to load headers.
|
||||
*/
|
||||
public void reload(boolean loadHeaders) {
|
||||
this.loadHeaders = loadHeaders;
|
||||
try {
|
||||
load(file);
|
||||
} catch(Exception e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "failed to reload file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromString(String contents) throws InvalidConfigurationException {
|
||||
if(!loadHeaders) {
|
||||
super.loadFromString(contents);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder memoryData = new StringBuilder();
|
||||
|
||||
// Parse headers
|
||||
final int indentLength = options().indent();
|
||||
final String pathSeparator = Character.toString(options().pathSeparator());
|
||||
int currentIndents = 0;
|
||||
String key = "";
|
||||
List<String> headers = Lists.newArrayList();
|
||||
for(String line : contents.split("\n")) {
|
||||
if(line.isEmpty()) continue; // Skip empty lines
|
||||
int indent = getSuccessiveCharCount(line, ' ');
|
||||
String subline = indent > 0 ? line.substring(indent) : line;
|
||||
if(subline.startsWith("#")) {
|
||||
if(subline.startsWith("#>")) {
|
||||
String txt = subline.startsWith("#> ") ? subline.substring(3) : subline.substring(2);
|
||||
mainHeader.add(txt);
|
||||
continue; // Main header, handled by bukkit
|
||||
}
|
||||
|
||||
// Add header to list
|
||||
String txt = subline.startsWith("# ") ? subline.substring(2) : subline.substring(1);
|
||||
headers.add(txt);
|
||||
continue;
|
||||
}
|
||||
|
||||
int indents = indent / indentLength;
|
||||
if(indents <= currentIndents) {
|
||||
// Remove last section of key
|
||||
String[] array = key.split(Pattern.quote(pathSeparator));
|
||||
int backspace = currentIndents - indents + 1;
|
||||
key = join(array, options().pathSeparator(), 0, array.length - backspace);
|
||||
}
|
||||
|
||||
// Add new section to key
|
||||
String separator = key.length() > 0 ? pathSeparator : "";
|
||||
String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line;
|
||||
key += separator + lineKey.substring(indent);
|
||||
|
||||
currentIndents = indents;
|
||||
|
||||
memoryData.append(line).append('\n');
|
||||
if(!headers.isEmpty()) {
|
||||
this.headers.put(key, headers);
|
||||
headers = Lists.newArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse remaining text
|
||||
super.loadFromString(memoryData.toString());
|
||||
|
||||
// Clear bukkit header
|
||||
options().header(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save config to file
|
||||
*/
|
||||
public void save() {
|
||||
if(headers.isEmpty() && mainHeader.isEmpty()) {
|
||||
try {
|
||||
super.save(file);
|
||||
} catch(IOException e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to save file", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom save
|
||||
final int indentLength = options().indent();
|
||||
final String pathSeparator = Character.toString(options().pathSeparator());
|
||||
String content = saveToString();
|
||||
StringBuilder fileData = new StringBuilder(buildHeader());
|
||||
int currentIndents = 0;
|
||||
String key = "";
|
||||
for(String h : mainHeader) {
|
||||
// Append main header to top of file
|
||||
fileData.append("#> ").append(h).append('\n');
|
||||
}
|
||||
|
||||
for(String line : content.split("\n")) {
|
||||
if(line.isEmpty()) continue; // Skip empty lines
|
||||
int indent = getSuccessiveCharCount(line, ' ');
|
||||
int indents = indent / indentLength;
|
||||
String indentText = indent > 0 ? line.substring(0, indent) : "";
|
||||
if(indents <= currentIndents) {
|
||||
// Remove last section of key
|
||||
String[] array = key.split(Pattern.quote(pathSeparator));
|
||||
int backspace = currentIndents - indents + 1;
|
||||
key = join(array, options().pathSeparator(), 0, array.length - backspace);
|
||||
}
|
||||
|
||||
// Add new section to key
|
||||
String separator = key.length() > 0 ? pathSeparator : "";
|
||||
String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line;
|
||||
key += separator + lineKey.substring(indent);
|
||||
|
||||
currentIndents = indents;
|
||||
|
||||
List<String> header = headers.get(key);
|
||||
String headerText = header != null ? addHeaderTags(header, indentText) : "";
|
||||
fileData.append(headerText).append(line).append('\n');
|
||||
}
|
||||
|
||||
// Write data to file
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(file);
|
||||
writer.write(fileData.toString());
|
||||
writer.flush();
|
||||
} catch(IOException e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to save file", e);
|
||||
} finally {
|
||||
if(writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch(IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String addHeaderTags(List<String> header, String indent) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(String line : header) {
|
||||
builder.append(indent).append("# ").append(line).append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String join(String[] array, char joinChar, int start, int length) {
|
||||
String[] copy = new String[length - start];
|
||||
System.arraycopy(array, start, copy, 0, length - start);
|
||||
return Joiner.on(joinChar).join(copy);
|
||||
}
|
||||
|
||||
private int getSuccessiveCharCount(String text, char key) {
|
||||
int count = 0;
|
||||
for(int i = 0; i < text.length(); i++) {
|
||||
if(text.charAt(i) == key) {
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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);
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package us.myles.ViaVersion.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public abstract class ListWrapper implements List {
|
||||
private final List list;
|
||||
|
||||
public ListWrapper(List inputList) {
|
||||
this.list = inputList;
|
||||
}
|
||||
|
||||
public abstract void handleAdd(Object o);
|
||||
|
||||
@Override
|
||||
public synchronized int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return this.list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(Object o) {
|
||||
return this.list.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator iterator() {
|
||||
return this.list.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object[] toArray() {
|
||||
return this.list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean add(Object o) {
|
||||
handleAdd(o);
|
||||
return this.list.add(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean remove(Object o) {
|
||||
return this.list.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection c) {
|
||||
for (Object o : c) {
|
||||
handleAdd(o);
|
||||
}
|
||||
return this.list.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(int index, Collection c) {
|
||||
for (Object o : c) {
|
||||
handleAdd(o);
|
||||
}
|
||||
return this.list.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
this.list.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(int index) {
|
||||
return this.list.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object set(int index, Object element) {
|
||||
return this.list.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void add(int index, Object element) {
|
||||
this.list.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object remove(int index) {
|
||||
return this.list.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int indexOf(Object o) {
|
||||
return this.list.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int lastIndexOf(Object o) {
|
||||
return this.list.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator listIterator() {
|
||||
return this.list.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator listIterator(int index) {
|
||||
return this.list.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List subList(int fromIndex, int toIndex) {
|
||||
return this.list.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean retainAll(Collection c) {
|
||||
return this.list.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeAll(Collection c) {
|
||||
return this.list.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean containsAll(Collection c) {
|
||||
return this.list.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object[] toArray(Object[] a) {
|
||||
return this.list.toArray(a);
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,13 @@ import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import org.spacehq.opennbt.NBTIO;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import us.myles.ViaVersion.chunks.MagicBitSet;
|
||||
import us.myles.ViaVersion.chunks.PacketChunk;
|
||||
import us.myles.ViaVersion.chunks.PacketChunkData;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -50,13 +42,22 @@ public class PacketUtil {
|
||||
}
|
||||
|
||||
public static CompoundTag readNBT(ByteBuf input) throws IOException {
|
||||
// Default client is limited to 2097152 bytes. (2.09mb)
|
||||
Preconditions.checkArgument(input.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", input.readableBytes());
|
||||
|
||||
int readerIndex = input.readerIndex();
|
||||
byte b = input.readByte();
|
||||
if (b == 0) {
|
||||
return null;
|
||||
} else {
|
||||
input.readerIndex(readerIndex);
|
||||
return (CompoundTag) NBTIO.readTag(new DataInputStream(new ByteBufInputStream(input)));
|
||||
ByteBufInputStream bytebufStream = new ByteBufInputStream(input);
|
||||
DataInputStream dataInputStream = new DataInputStream(bytebufStream);
|
||||
try {
|
||||
return (CompoundTag) NBTIO.readTag(dataInputStream);
|
||||
} finally {
|
||||
dataInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,17 +65,20 @@ public class PacketUtil {
|
||||
if (tag == null) {
|
||||
output.writeByte(0);
|
||||
} else {
|
||||
NBTIO.writeTag(new DataOutputStream(new ByteBufOutputStream(output)), tag);
|
||||
ByteBufOutputStream bytebufStream = new ByteBufOutputStream(output);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(bytebufStream);
|
||||
|
||||
NBTIO.writeTag(dataOutputStream, tag);
|
||||
|
||||
dataOutputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Object> callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) {
|
||||
List<Object> output = new ArrayList<Object>();
|
||||
List<Object> output = new ArrayList<>();
|
||||
try {
|
||||
PacketUtil.DECODE_METHOD.invoke(decoder, ctx, input, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return output;
|
||||
@@ -83,9 +87,7 @@ public class PacketUtil {
|
||||
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) {
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -169,7 +171,7 @@ public class PacketUtil {
|
||||
|
||||
public static List<String> readStringArray(ByteBuf buf) {
|
||||
int len = readVarInt(buf);
|
||||
List<String> ret = new ArrayList<String>(len);
|
||||
List<String> ret = new ArrayList<>(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
ret.add(readString(buf));
|
||||
}
|
||||
@@ -258,8 +260,8 @@ public class PacketUtil {
|
||||
}
|
||||
|
||||
public static void writeLongs(long[] data, ByteBuf output) {
|
||||
for (int index = 0; index < data.length; index++) {
|
||||
output.writeLong(data[index]);
|
||||
for (long aData : data) {
|
||||
output.writeLong(aData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,114 +274,6 @@ public class PacketUtil {
|
||||
return data;
|
||||
}
|
||||
|
||||
// This method is based on one from here
|
||||
// https://github.com/Steveice10/MCProtocolLib/blob/master/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java
|
||||
public static PacketChunk readChunkData(boolean isFullChunk, int bitmask, byte[] input) {
|
||||
PacketChunkData[] chunks = new PacketChunkData[16];
|
||||
boolean sky = false;
|
||||
int expected = 0;
|
||||
int position = 0;
|
||||
ShortBuffer blockData = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
expected += (4096 * 2) + 2048;
|
||||
}
|
||||
}
|
||||
// If there is more data, then there must be skylights :D
|
||||
if (input.length > expected) {
|
||||
sky = true;
|
||||
}
|
||||
|
||||
// Read block data
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
chunks[i] = new PacketChunkData(sky);
|
||||
blockData.position(position / 2);
|
||||
blockData.get(chunks[i].getBlocks(), 0, 4096);
|
||||
position = position + (4096 * 2);
|
||||
} else {
|
||||
chunks[i] = new PacketChunkData(sky);
|
||||
}
|
||||
}
|
||||
// Read blocklight data
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
System.arraycopy(input, position, chunks[i].getBlockLight(), 0, 2048);
|
||||
position = position + 2048;
|
||||
}
|
||||
}
|
||||
// Read skylight data
|
||||
if (sky) {
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
System.arraycopy(input, position, chunks[i].getSkyLight(), 0, 2048);
|
||||
position = position + 2048;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] biomeData = null;
|
||||
|
||||
if (isFullChunk) {
|
||||
// yay im a full chunk meaning i know my biomes, mommy get the camera!
|
||||
biomeData = new byte[256];
|
||||
System.arraycopy(input, position, biomeData, 0, 256);
|
||||
}
|
||||
return new PacketChunk(chunks, biomeData);
|
||||
}
|
||||
|
||||
public static void writeNewChunk(ByteBuf buffer, PacketChunkData chunk) {
|
||||
// Bits Per Block (We use 0, cause we're not gonna write a palette ;) )
|
||||
buffer.writeByte(0);
|
||||
// No Palette nor length :D
|
||||
|
||||
// Data Array Length
|
||||
byte[] blockData = convertBlockArray(chunk.getBlocks());
|
||||
writeVarInt(blockData.length / 8, buffer); // Notchian is divide by 8
|
||||
|
||||
buffer.writeBytes(blockData);
|
||||
// Block Light
|
||||
buffer.writeBytes(chunk.getBlockLight());
|
||||
// If has skylight, write it
|
||||
if (chunk.getSkyLight() != null) {
|
||||
buffer.writeBytes(chunk.getSkyLight());
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] convertBlockArray(short[] blocks) {
|
||||
// block ID for the first 9 bits, and the block damage value for the last 4 bits
|
||||
byte[] output = new byte[6664]; // (16 * 16 * 16 * 13) / 8 :) (plus some for padding ...)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
for (short block : blocks) {
|
||||
int blockID = block >> 4; // (Needs to be to 9 bits)
|
||||
int data = block & 0xF; // 8 bits
|
||||
|
||||
}
|
||||
return null; // todo: finish
|
||||
}
|
||||
|
||||
private static BitSet append(BitSet base, int index, MagicBitSet toAdd) {
|
||||
int length = index;
|
||||
for (int i = 0; i < toAdd.getTrueLength(); i++) {
|
||||
base.set(length + i, toAdd.get(i));
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private static MagicBitSet getPaddedBitSet(int value, int bitSize) {
|
||||
MagicBitSet output = new MagicBitSet(bitSize);
|
||||
BitSet temp = BitSet.valueOf(new long[]{value});
|
||||
for (int i = 0; i < bitSize; i++) {
|
||||
output.set(i, false);
|
||||
}
|
||||
int toShift = bitSize - temp.length();
|
||||
for (int i = 0; i < temp.length(); i++) {
|
||||
output.set(toShift + i, temp.get(i));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static long[] readBlockPosition(ByteBuf buf) {
|
||||
long val = buf.readLong();
|
||||
long x = (val >> 38); // signed
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package us.myles.ViaVersion.util;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReflectionUtil {
|
||||
private static String BASE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
@@ -50,4 +54,74 @@ public class ReflectionUtil {
|
||||
field.setAccessible(true);
|
||||
field.set(o, value);
|
||||
}
|
||||
|
||||
public static final class ClassReflection {
|
||||
private final Class<?> handle;
|
||||
private final Map<String, Field> fields = Maps.newConcurrentMap();
|
||||
private final Map<String, Method> methods = Maps.newConcurrentMap();
|
||||
|
||||
public ClassReflection(Class<?> handle) {
|
||||
this(handle, true);
|
||||
}
|
||||
|
||||
public ClassReflection(Class<?> handle, boolean recursive) {
|
||||
this.handle = handle;
|
||||
scanFields(handle, recursive);
|
||||
scanMethods(handle, recursive);
|
||||
}
|
||||
|
||||
private void scanFields(Class<?> host, boolean recursive) {
|
||||
if(host.getSuperclass() != null && recursive) {
|
||||
scanFields(host.getSuperclass(), true);
|
||||
}
|
||||
|
||||
for(Field field : host.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
|
||||
private void scanMethods(Class<?> host, boolean recursive) {
|
||||
if(host.getSuperclass() != null && recursive) {
|
||||
scanMethods(host.getSuperclass(), true);
|
||||
}
|
||||
|
||||
for(Method method : host.getDeclaredMethods()) {
|
||||
method.setAccessible(true);
|
||||
methods.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
|
||||
public Object newInstance() throws IllegalAccessException, InstantiationException {
|
||||
return handle.newInstance();
|
||||
}
|
||||
|
||||
public Field getField(String name) {
|
||||
return fields.get(name);
|
||||
}
|
||||
|
||||
public void setFieldValue(String fieldName, Object instance, Object value) throws IllegalAccessException {
|
||||
getField(fieldName).set(instance, value);
|
||||
}
|
||||
|
||||
public <T> T getFieldValue(String fieldName, Object instance, Class<T> type) throws IllegalAccessException {
|
||||
return type.cast(getField(fieldName).get(instance));
|
||||
}
|
||||
|
||||
public <T> T invokeMethod(Class<T> type, String methodName, Object instance, Object... args) throws InvocationTargetException, IllegalAccessException {
|
||||
return type.cast(getMethod(methodName).invoke(instance, args));
|
||||
}
|
||||
|
||||
public Method getMethod(String name) {
|
||||
return methods.get(name);
|
||||
}
|
||||
|
||||
public Collection<Field> getFields() {
|
||||
return Collections.unmodifiableCollection(fields.values());
|
||||
}
|
||||
|
||||
public Collection<Method> getMethods() {
|
||||
return Collections.unmodifiableCollection(methods.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,22 @@
|
||||
# Should ViaVersion check for updates?
|
||||
checkforupdates: true
|
||||
# Should we send any bulk chunks, in sync (may be slower but fixes timings)
|
||||
# Currently broken due to sign updates, possibly will return
|
||||
sync-chunks: true
|
||||
# No collide options, these allow you to configure how collision works.
|
||||
# Do you want us to prevent collision?
|
||||
prevent-collision: true
|
||||
# If the above is true, should we automatically team players until you do?
|
||||
auto-team: true
|
||||
# When enabled if certain metadata can't be read we won't tell you about it
|
||||
suppress-metadata-errors: false
|
||||
# When enabled 1.9 will be able to block by using shields
|
||||
shield-blocking: true
|
||||
# Should we enable our hologram patch?
|
||||
# If they're in the wrong place enable this
|
||||
hologram-patch: false
|
||||
# This is the offset, should work as default when enabled.
|
||||
hologram-y: -1
|
||||
# Enable player tick simulation, this fixes eating, drinking, nether portals.
|
||||
simulate-pt: true
|
||||
# Should we patch boss bars so they work? (Default: true, disable if you're having issues)
|
||||
bossbar-patch: true
|
||||
# If your boss bar flickers on 1.9, set this to 'true'. It will keep all boss bars on 100% (not recommended)
|
||||
bossbar-anti-flicker: false
|
||||
Reference in new issue
Block a user