Compare commits

...
43 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
Myles a743895c3f Bump version to 0.6.0-SNAPSHOT 2016-03-09 01:13:24 +00:00
Myles f65666b7a6 Better support for ServerConnection detection, and also adding injectors. (Basic late-bind support is now available, but does not work with ProtocolLib due to it injecting first.) 2016-03-09 00:51:50 +00:00
Myles 3a217503fc Merge pull request #179 from HugoDaBosss/master
Stop error fetching hand item spamming console.
2016-03-08 21:24:47 +00:00
Myles c71b75e13b Oops 2016-03-08 21:23:57 +00:00
HugoDaBosss 0c890c3a30 Print errors while in debug mode 2016-03-08 22:23:36 +01:00
HugoDaBosss f38bb0ca22 Stop error fetching hand item spamming console. 2016-03-08 22:20:52 +01:00
Myles 2144fa1837 Handle any exception so it doesn't implode. (Still prints to the console.) 2016-03-08 21:16:34 +00:00
Myles fc4a617da7 Don't send chunk packets with no chunks :) 2016-03-08 21:13:15 +00:00
Myles 126ee6e03f Formatting 2016-03-08 20:54:03 +00:00
Myles 498867032f Merge pull request #178 from HugoDaBosss/master
Fix animation for bows
2016-03-08 20:51:02 +00:00
HugoDaBosss aef8eca790 Fix animation for bows 2016-03-08 21:48:21 +01:00
Myles d8bbb51721 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-03-08 19:27:24 +00:00
Myles 6e4fd5a63b Only show blocking if it knows that we're blocking #176 2016-03-08 19:27:04 +00:00
Myles e7bd6245c2 Merge pull request #175 from Matsv/master
Reimplement bossbar methods
2016-03-08 14:02:18 +00:00
Mats 6242cb0b47 Merge remote-tracking branch 'origin/master' 2016-03-08 13:48:20 +01:00
Mats bf5072e1cf Add removed bossbar methods 2016-03-08 13:48:03 +01:00
Mats c24b673550 Add removed bossbar methods 2016-03-08 13:46:30 +01:00
Myles dfc1e1cf51 Merge pull request #174 from Matsv/master
Add /viaversion debug to the commandlist
2016-03-08 12:42:16 +00:00
Mats c600ab6dd1 Add /viaversion debug to the commandlist 2016-03-08 13:30:53 +01:00
21 changed files with 862 additions and 136 deletions

No files matched your search

+6 -1
View File
@@ -1,4 +1,4 @@
# ViaVersion 0.5.8
# 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.5.9-SNAPSHOT</version>
<version>0.6.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ViaVersion</name>
@@ -47,18 +47,21 @@ public class NetUtil {
// 2 = Get block light.
// 3 = Get sky light.
Chunk[] chunks = new Chunk[16];
int chunkCount = 0;
for (int pass = 0; pass < 4; pass++) {
if(pass == 1){
if(chunkCount == 0) return null;
}
for (int ind = 0; ind < 16; ind++) {
if ((bitmask & 1 << ind) != 0) {
if (pass == 0) {
chunkCount++;
// Block length + Blocklight length
expected += (4096 * 2) + 2048;
}
if (pass == 1) {
chunks[ind] = new Chunk(sky || hasSkyLight);
buf.position(pos / 2);
int buffPos = buf.position();
// convert short array to new one
@@ -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();
}
}
}
}
}
@@ -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,9 +24,14 @@ 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;
import java.util.Map;
import java.util.UUID;
@@ -35,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 {
@@ -53,8 +58,9 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}
}).get(10, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Error fetching hand item ");
e.printStackTrace();
System.out.println("Error fetching hand item: " + e.getClass().getName());
if (ViaVersion.getInstance().isDebug())
e.printStackTrace();
return null;
}
}
@@ -62,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.");
@@ -70,13 +76,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);
@@ -94,39 +97,102 @@ 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 = serverClazz.getDeclaredMethod("getServerConnection").invoke(server);
if (connection == null) {
System.out.println("connection is null!!");
//try others
for (Method m : serverClazz.getDeclaredMethods()) {
if (m.getReturnType() != null && !m.getName().equals("getServerConnection")) {
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
if (m.getParameterTypes().length == 0) {
connection = m.invoke(server);
}
}
}
}
if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :(");
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.
}
}
}
injected = true;
} 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();
}
}
@@ -175,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);
}
@@ -188,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;
@@ -215,8 +296,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();
}
}
}
@@ -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();
}
@@ -13,6 +13,13 @@ public interface BossBar {
*/
void setTitle(String title);
/**
* Get the current title
*
* @return the title
*/
String getTitle();
/**
* Change the health
*
@@ -21,11 +28,11 @@ public interface BossBar {
void setHealth(float health);
/**
* Get the bossbar color
* Get the health
*
* @return
* @return float between 0F - 1F
*/
BossColor getColor();
float getHealth();
/**
* Yay colors!
@@ -34,6 +41,13 @@ public interface BossBar {
*/
void setColor(BossColor color);
/**
* Get the bossbar color
*
* @return
*/
BossColor getColor();
/**
* Change the bosbar style
*
@@ -41,6 +55,13 @@ public interface BossBar {
*/
void setStyle(BossStyle style);
/**
* Get the bosbar style
*
* @return BossStyle
*/
BossStyle getStyle();
/**
* Show the bossbar to a player.
*
@@ -20,16 +20,11 @@ public class ViaVersionCommand implements CommandExecutor {
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 autoteam &7- &6Toggle automatically teaming to prevent colliding."));
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<>();
@@ -59,11 +54,7 @@ public class ViaVersionCommand implements CommandExecutor {
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 {
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 autoteam &7- &6Toggle automatically teaming to prevent colliding."));
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
sendHelp(sender);
}
}
@@ -71,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,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);
}
}
@@ -34,7 +34,7 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
incomingTransformer.transform(id, bytebuf, newPacket);
bytebuf.clear();
bytebuf = newPacket;
} catch (CancelException e) {
} catch (Exception e) {
bytebuf.clear();
throw e;
}
@@ -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);
}
@@ -73,7 +41,7 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
bytebuf.clear();
try {
outgoingTransformer.transform(id, oldPacket, bytebuf);
} catch (CancelException e) {
} catch (Exception e) {
bytebuf.clear();
throw e;
} finally {
@@ -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);
}
}
@@ -33,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),
@@ -90,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
@@ -122,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;
@@ -40,6 +40,16 @@ public class MetadataRewriter {
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;
@@ -197,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;
@@ -13,7 +13,8 @@ public enum Type {
String(4),
Slot(5),
Position(6),
Rotation(7);
Rotation(7),
NonExistent(-1);
private final int typeID;
public static Type byId(int 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);
@@ -275,14 +281,13 @@ 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);
@@ -292,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) {
@@ -317,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) {
@@ -1,6 +1,7 @@
package us.myles.ViaVersion.transformers;
import io.netty.buffer.ByteBuf;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@@ -14,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;
@@ -37,6 +43,9 @@ public class OutgoingTransformer {
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;
public OutgoingTransformer(ConnectionInfo info) {
@@ -214,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);
@@ -241,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);
@@ -261,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;
}
@@ -317,7 +335,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;
}
@@ -353,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;
}
@@ -751,6 +793,9 @@ public class OutgoingTransformer {
}
}
Column read = NetUtil.readOldChunkData(chunkX, chunkZ, groundUp, bitMask, data, true, sk);
if (read == null) {
throw new CancelException();
}
// Write chunk section array :((
ByteBuf temp = output.alloc().buffer();
try {
@@ -799,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);
}
@@ -808,14 +879,70 @@ public class OutgoingTransformer {
if (entry.getOldID() == 0) {
// Byte
byte data = (byte) entry.getValue();
if ((data & 0x10) == 0x10) {
ItemSlotRewriter.ItemStack shield = new ItemSlotRewriter.ItemStack();
shield.id = 442;
shield.amount = 1;
shield.data = 0;
sendSecondHandItem(entityID, shield);
} else {
sendSecondHandItem(entityID, null);
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);
}
}
}
}
@@ -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;
}
}
@@ -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);
}
}
+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