Compare commits

...
24 Commits
Author SHA1 Message Date
Myles 5df8a1e8e7 Bump version 2016-03-10 16:47:52 +00:00
Myles a3d124b552 Merge pull request #190 from lenis0012/master
Add config wrapper to update config
2016-03-10 16:44:20 +00:00
Lennart ten Wolde 6d95af3963 Add config wrapper to update config
Configuration reads old config and moves it's values over to a new
config copied from the resources directory.

normally, when you save it would delete the comments, but they are
written back in to the new config by this wrapper

now people their config files will actually update
2016-03-10 17:40:40 +01:00
Myles c08a38ff21 Merge pull request #189 from lenis0012/master
fix metadata type mismatch
2016-03-10 16:21:02 +00:00
Lennart ten Wolde 4a804bae7c fix metadata type mismatch & add anti-flicker 2016-03-10 16:52:31 +01:00
Myles fd42c7eba6 Merge pull request #188 from lenis0012/master
fixed default boss title
2016-03-10 14:15:30 +00:00
Lennart ten Wolde 1af86d1354 fixed default boss title 2016-03-10 15:14:27 +01:00
Myles 1b77e1c89f Fix BarAPI, (adds new metaindex, and change colour to be right and remove debug) 2016-03-10 14:07:27 +00:00
Myles c42b9351b8 Remove sync-chunks 2016-03-10 13:25:49 +00:00
Myles ab8c8cd7c8 Merge pull request #187 from lenis0012/chunks
Add chunk handler to netty pipeline. Fixes #48
2016-03-10 13:22:48 +00:00
Myles 83518b92fe Make bossbar patch a configurable option 2016-03-10 13:22:32 +00:00
Myles 1bf1e8981c Merge pull request #186 from lenis0012/master
Add support for 1.8 boss bars. Fixes #41
2016-03-10 13:13:55 +00:00
Lennart ten Wolde b1f076936d Add chunk handler to netty pipeline. FIxes #48 2016-03-10 12:47:09 +01:00
Lennart ten Wolde 319e793cd6 Add support for 1.8 boss bars
Automatically converts legacy boss bars to 1.9 ones using ViaVersions's
boss API.

I suggest you add a config option
Note: also fixed crash on wither/dragon spawn
2016-03-10 03:14:30 +01:00
Myles c9e1360951 Version bump 2016-03-09 21:40:11 +00:00
Myles c11ad7930f Tiny cleanup allow it to be disabled via config and add lenis0012 to contributors <3 2016-03-09 21:30:43 +00:00
Myles 7b36a9de51 Merge pull request #185 from lenis0012/master
Manually perform idle updates. Fixed #160 #99
2016-03-09 21:21:59 +00:00
Lennart ten Wolde d5e9170109 Manually perform idle updates. Fixed #160 #99
Manually performs idle updates by keeping track of the player's
movement.
It compensates for the missing idle packets attempting to get a ratio of
20 movement related packets per second.
2016-03-09 22:17:17 +01:00
Myles 47b447f195 Add the option to use a hologram patch to fix invisible holograms being in wrong place, by default disabled. 2016-03-09 21:08:50 +00:00
Myles e2d27df1a8 Add another build server 2016-03-09 14:37:21 +00:00
Myles 9a781d940f Bump version 2016-03-09 14:29:30 +00:00
Myles fed36c9487 synchronize all the things! 2016-03-09 14:12:50 +00:00
Myles 463327c2a7 Bump version to 0.6.1 snapshot 2016-03-09 12:09:24 +00:00
Myles b0050d4a8d Add config option for blocking and fix crash due to synchronization 2016-03-09 12:08:21 +00:00
16 changed files with 597 additions and 103 deletions

No files matched your search

+6 -1
View File
@@ -1,4 +1,4 @@
# ViaVersion 0.5.9
# ViaVersion 0.6.2
[![Build Status](https://travis-ci.org/MylesIsCool/ViaVersion.svg?branch=master)](https://travis-ci.org/MylesIsCool/ViaVersion)
**Allows the connection of 1.9 clients to 1.8**
@@ -40,6 +40,8 @@ Contributors:
**fillefilip8**
**lenis0012**
Releases / Dev Builds:
@@ -55,6 +57,9 @@ 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:
--------
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.6.0-SNAPSHOT</version>
<version>0.6.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ViaVersion</name>
@@ -12,6 +12,9 @@ import us.myles.ViaVersion.packets.State;
@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 Object lastPacket;
private java.util.UUID UUID;
@@ -22,6 +25,7 @@ public class ConnectionInfo {
private int entityID;
private boolean active = true;
private String username;
private long nextIdlePacket = 0L;
public ConnectionInfo(SocketChannel socketChannel) {
this.channel = socketChannel;
@@ -33,6 +37,7 @@ public class ConnectionInfo {
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) {
@@ -56,4 +61,10 @@ public class ConnectionInfo {
public void closeWindow() {
this.openWindow = null;
}
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();
}
}
}
}
}
@@ -24,9 +24,12 @@ 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.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
@@ -36,6 +39,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
@@ -64,7 +68,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
@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.");
@@ -73,6 +77,8 @@ 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)");
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);
@@ -91,6 +97,28 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
}
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();
}
}
public void injectPacketHandler() {
try {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
@@ -116,23 +144,28 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
if (connection != null) {
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(connection);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
field.set(connection, new ListWrapper((List) value) {
List wrapper = new ListWrapper((List) value) {
@Override
public void handleAdd(Object o) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
public synchronized void handleAdd(Object o) {
synchronized (this) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
}
}
}
});
};
field.set(connection, wrapper);
// Iterate through current list
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
} else {
break; // not the right list.
synchronized (wrapper) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
} else {
break; // not the right list.
}
}
}
}
@@ -208,11 +241,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
this.debug = value;
}
@Override
public boolean isSyncedChunks() {
return getConfig().getBoolean("sync-chunks", true);
}
public boolean isPreventCollision() {
return getConfig().getBoolean("prevent-collision", true);
}
@@ -221,6 +249,26 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return getConfig().getBoolean("suppress-metadata-errors", false);
}
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;
@@ -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();
}
@@ -0,0 +1,53 @@
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()) {
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);
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);
list.add(packet);
} catch(InstantiationException | InvocationTargetException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
}
return;
}
}
list.add(o);
}
}
@@ -28,38 +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 | InvocationTargetException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
};
chunks.run();
bytebuf.clear();
throw new CancelException();
}
// call minecraft encoder
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
}
@@ -32,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);
}
}
@@ -91,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
@@ -123,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;
@@ -207,7 +207,7 @@ public class MetadataRewriter {
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;
@@ -16,6 +16,7 @@ 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;
@@ -34,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());
}
@@ -42,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);
@@ -267,7 +273,7 @@ public class IncomingTransformer {
output.writeByte(face);
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);
@@ -291,8 +297,8 @@ public class IncomingTransformer {
output.writeLong(-1L);
output.writeByte(255);
// write item in hand
ItemStack inHand = ViaVersionPlugin.getHandItem(info);
if (inHand != null) {
ItemStack inHand = plugin.getHandItem(info);
if (inHand != null && plugin.isShieldBlocking()) {
if (inHand.getType().name().endsWith("SWORD")) {
// blocking?
if (hand == 0) {
@@ -15,7 +15,12 @@ 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.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;
@@ -39,6 +44,8 @@ public class OutgoingTransformer {
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;
public OutgoingTransformer(ConnectionInfo info) {
@@ -216,13 +223,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);
@@ -243,6 +252,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);
@@ -263,12 +275,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;
}
@@ -372,7 +388,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;
}
@@ -821,6 +844,32 @@ public class OutgoingTransformer {
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);
}
@@ -830,17 +879,70 @@ public class OutgoingTransformer {
if (entry.getOldID() == 0) {
// Byte
byte data = (byte) entry.getValue();
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);
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);
}
} else {
sendSecondHandItem(entityID, null);
}
}
}
@@ -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;
}
}
@@ -15,43 +15,43 @@ public abstract class ListWrapper implements List {
public abstract void handleAdd(Object o);
@Override
public int size() {
public synchronized int size() {
return this.list.size();
}
@Override
public boolean isEmpty() {
public synchronized boolean isEmpty() {
return this.list.isEmpty();
}
@Override
public boolean contains(Object o) {
public synchronized boolean contains(Object o) {
return this.list.contains(o);
}
@Override
public Iterator iterator() {
public synchronized Iterator iterator() {
return this.list.iterator();
}
@Override
public Object[] toArray() {
public synchronized Object[] toArray() {
return this.list.toArray();
}
@Override
public boolean add(Object o) {
public synchronized boolean add(Object o) {
handleAdd(o);
return this.list.add(o);
}
@Override
public boolean remove(Object o) {
public synchronized boolean remove(Object o) {
return this.list.remove(o);
}
@Override
public boolean addAll(Collection c) {
public synchronized boolean addAll(Collection c) {
for (Object o : c) {
handleAdd(o);
}
@@ -59,7 +59,7 @@ public abstract class ListWrapper implements List {
}
@Override
public boolean addAll(int index, Collection c) {
public synchronized boolean addAll(int index, Collection c) {
for (Object o : c) {
handleAdd(o);
}
@@ -67,72 +67,72 @@ public abstract class ListWrapper implements List {
}
@Override
public void clear() {
public synchronized void clear() {
this.list.clear();
}
@Override
public Object get(int index) {
public synchronized Object get(int index) {
return this.list.get(index);
}
@Override
public Object set(int index, Object element) {
public synchronized Object set(int index, Object element) {
return this.list.set(index, element);
}
@Override
public void add(int index, Object element) {
public synchronized void add(int index, Object element) {
this.list.add(index, element);
}
@Override
public Object remove(int index) {
public synchronized Object remove(int index) {
return this.list.remove(index);
}
@Override
public int indexOf(Object o) {
public synchronized int indexOf(Object o) {
return this.list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
public synchronized int lastIndexOf(Object o) {
return this.list.lastIndexOf(o);
}
@Override
public ListIterator listIterator() {
public synchronized ListIterator listIterator() {
return this.list.listIterator();
}
@Override
public ListIterator listIterator(int index) {
return this.listIterator(index);
public synchronized ListIterator listIterator(int index) {
return this.list.listIterator(index);
}
@Override
public List subList(int fromIndex, int toIndex) {
public synchronized List subList(int fromIndex, int toIndex) {
return this.list.subList(fromIndex, toIndex);
}
@Override
public boolean retainAll(Collection c) {
public synchronized boolean retainAll(Collection c) {
return this.list.retainAll(c);
}
@Override
public boolean removeAll(Collection c) {
public synchronized boolean removeAll(Collection c) {
return this.list.removeAll(c);
}
@Override
public boolean containsAll(Collection c) {
public synchronized boolean containsAll(Collection c) {
return this.list.containsAll(c);
}
@Override
public Object[] toArray(Object[] a) {
public synchronized Object[] toArray(Object[] a) {
return this.list.toArray(a);
}
}
+14 -4
View File
@@ -1,12 +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
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