Add ViaManager and abstraction / Add todo list

This commit is contained in:
Myles 2016-09-25 14:39:37 +01:00
parent e7ab232b28
commit 05930ad791
30 changed files with 662 additions and 425 deletions

14
TODOLIST Normal file
View File

@ -0,0 +1,14 @@
Migrate EntityUtil to be cool
Fix 1.9to1.8
Fix 1.9.3to1.9.1/2
Fix 1.9.1/2to1.9.3/4
Fix snapshot to 1.10
Fix BaseProtocol
Fix BossBar to use Generics
Register Listeners Properly
Fix commands
Handle injector errors
Add new info to dump for platform
Migrate Bukkit types to our own :D
Important: Create builder for ViaManager which allows supplying on the command exectutor, injector, platform :D

View File

@ -1,71 +1,55 @@
package us.myles.ViaVersion;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaAPI;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionAPI;
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.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.platform.ViaPlatform;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.boss.ViaBossBar;
import us.myles.ViaVersion.bukkit.BukkitCommandHandler;
import us.myles.ViaVersion.bukkit.BukkitViaAPI;
import us.myles.ViaVersion.bukkit.BukkitViaInjector;
import us.myles.ViaVersion.classgenerator.ClassGenerator;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.listeners.UpdateListener;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.update.UpdateUtil;
import us.myles.ViaVersion.util.ConcurrentList;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ProtocolSupportUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPlatform {
public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform {
private final Map<UUID, UserConnection> portedPlayers = new ConcurrentHashMap<>();
private List<ChannelFuture> injectedFutures = new ArrayList<>();
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
private BukkitCommandHandler commandHandler;
private boolean debug = false;
private boolean compatSpigotBuild = false;
private boolean spigot = true;
private boolean lateBind = false;
private boolean protocolSupport = false;
@Getter
private ViaConfig conf;
@Getter
private ViaAPI<Player> api = new BukkitViaAPI(this);
public ViaVersionPlugin() {
// Config magic
conf = new ViaConfig(this);
// Init platform
Via.init(this);
// For compatibility
ViaVersion.setInstance(this);
// Check if we're using protocol support too
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
if (protocolSupport) {
getLogger().info("Hooking into ProtocolSupport, to prevent issues!");
try {
patchLists();
BukkitViaInjector.patchLists();
} catch (Exception e) {
e.printStackTrace();
}
@ -74,27 +58,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPl
@Override
public void onLoad() {
// Config magic
conf = new ViaConfig(this);
// Init platform
Via.init(this);
// For compatibility
ViaVersion.setInstance(this);
// Handle reloads
if (System.getProperty("ViaVersion") != null) {
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
getLogger().severe("ViaVersion is already loaded, we're going to kick all the players... because otherwise we'll crash because of ProtocolLib.");
for (Player player : Bukkit.getOnlinePlayers()) {
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', getConf().getReloadDisconnectMsg()));
}
} else {
getLogger().severe("ViaVersion is already loaded, this should work fine. If you get any console errors, try rebooting.");
}
}
// Spigot detector
try {
Class.forName("org.spigotmc.SpigotConfig");
@ -111,35 +74,19 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPl
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !isBinded();
lateBind = !BukkitViaInjector.isBinded();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
if (!lateBind)
injectPacketHandler();
if (!lateBind) {
Via.getManager().init();
}
}
@Override
public void onEnable() {
if (lateBind)
injectPacketHandler();
if (conf.isCheckForUpdates())
UpdateUtil.sendUpdateMessage();
// Gather version :)
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override
public void run() {
gatherProtocolVersion();
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
getLogger().info("ViaVersion detected server version: " + ProtocolVersion.getProtocol(ProtocolRegistry.SERVER_PROTOCOL));
if (!ProtocolRegistry.isWorkingPipe()) {
getLogger().warning("ViaVersion does not have any compatible versions for this server version, please read our resource page carefully.");
if (lateBind) {
Via.getManager().init();
}
}
ProtocolRegistry.refreshVersions();
}
});
Bukkit.getPluginManager().registerEvents(new UpdateListener(), this);
@ -157,325 +104,18 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPl
@Override
public void onDisable() {
getLogger().info("ViaVersion is disabling, if this is a reload and you experience issues consider rebooting.");
uninject();
// TODO: Call ViaManager.destroy()
}
public void gatherProtocolVersion() {
try {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
Object ping = null;
// Search for ping method
for (Field f : serverClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().equals("ServerPing")) {
f.setAccessible(true);
ping = f.get(server);
}
}
}
if (ping != null) {
Object serverData = null;
for (Field f : pingClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().endsWith("ServerData")) {
f.setAccessible(true);
serverData = f.get(ping);
}
}
}
if (serverData != null) {
int protocolVersion = -1;
for (Field f : serverData.getClass().getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType() == int.class) {
f.setAccessible(true);
protocolVersion = (int) f.get(serverData);
}
}
}
if (protocolVersion != -1) {
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
return;
}
}
}
} catch (Exception e) {
e.printStackTrace();
// We couldn't work it out... We'll just use ping and hope for the best...
}
}
public Object getServerConnection() throws Exception {
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);
}
}
}
}
return connection;
}
public void injectPacketHandler() {
try {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
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);
}
}
}
};
injectedLists.add(new Pair<>(field, connection));
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 ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
e.printStackTrace();
}
}
public void patchLists() throws Exception {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
if (!(value instanceof ConcurrentList)) {
ConcurrentList list = new ConcurrentList();
list.addAll((List) value);
field.set(connection, list);
}
}
}
}
public boolean isBinded() {
try {
Object connection = getServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return 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);
injectedFutures.add(future);
} catch (NoSuchFieldException e) {
// let's find who to blame!
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
} else {
throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
} catch (Exception e) {
getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?");
e.printStackTrace();
}
}
private void uninject() {
// TODO: Uninject from players currently online to prevent protocol lib issues.
for (ChannelFuture future : injectedFutures) {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (oldInit instanceof ViaVersionInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
System.out.println("Failed to remove injection handler, reload won't work with connections, please reboot!");
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Object o = pair.getKey().get(pair.getValue());
if (o instanceof ListWrapper) {
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
System.out.println("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
injectedLists.clear();
}
@Override
public boolean isPorted(Player player) {
return isPorted(player.getUniqueId());
}
@Override
public int getPlayerVersion(@NonNull Player player) {
if (!isPorted(player))
return getExternalVersion(player);
return portedPlayers.get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
}
@Override
public int getPlayerVersion(@NonNull UUID uuid) {
if (!isPorted(uuid))
return getExternalVersion(Bukkit.getPlayer(uuid));
return portedPlayers.get(uuid).get(ProtocolInfo.class).getProtocolVersion();
}
private int getExternalVersion(Player player) {
if (!isProtocolSupport()) {
return ProtocolRegistry.SERVER_PROTOCOL;
} else {
return ProtocolSupportUtil.getProtocolVersion(player);
}
}
@Override
public boolean isPorted(UUID playerUUID) {
return portedPlayers.containsKey(playerUUID);
}
@Override
public String getVersion() {
return getDescription().getVersion();
}
public UserConnection getConnection(UUID playerUUID) {
return portedPlayers.get(playerUUID);
}
public UserConnection getConnection(Player player) {
return portedPlayers.get(player.getUniqueId());
}
public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException {
sendRawPacket(player.getUniqueId(), packet);
}
@Override
public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException {
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!");
UserConnection ci = portedPlayers.get(uuid);
ci.sendRawPacket(packet);
}
@Override
public BossBar createBossBar(String title, BossColor color, BossStyle style) {
return new ViaBossBar(title, 1F, color, style);
}
@Override
public BossBar createBossBar(String title, float health, BossColor color, BossStyle style) {
return new ViaBossBar(title, health, color, style);
}
@Override
public boolean isDebug() {
return this.debug;
}
public void setDebug(boolean value) {
this.debug = value;
}
@Override
public ViaVersionCommand getCommandHandler() {
return commandHandler;
}
@Override
public boolean isCompatSpigotBuild() {
return compatSpigotBuild;
}
@Override
public SortedSet<Integer> getSupportedVersions() {
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
outputSet.removeAll(getConf().getBlockedProtocols());
return outputSet;
}
@Override
public boolean isSpigot() {
return this.spigot;
}
public void addPortedClient(UserConnection info) {
portedPlayers.put(info.get(ProtocolInfo.class).getUuid(), info);
}
public void removePortedClient(UUID clientID) {
portedPlayers.remove(clientID);
}
public void run(final Runnable runnable, boolean wait) {
try {
Future f = Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<Boolean>() {
@ -499,10 +139,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPl
return protocolSupport;
}
public Map<UUID, UserConnection> getPortedPlayers() {
return portedPlayers;
}
public boolean handlePPS(UserConnection info) {
// Max PPS Checker
if (conf.getMaxPPS() > 0) {
@ -576,4 +212,17 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaPl
public boolean isPluginEnabled() {
return Bukkit.getPluginManager().getPlugin("ViaVersion").isEnabled();
}
@Override
public void onReload() {
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
getLogger().severe("ViaVersion is already loaded, we're going to kick all the players... because otherwise we'll crash because of ProtocolLib.");
for (Player player : Bukkit.getOnlinePlayers()) {
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', getConf().getReloadDisconnectMsg()));
}
} else {
getLogger().severe("ViaVersion is already loaded, this should work fine. If you get any console errors, try rebooting.");
}
}
}

View File

@ -7,13 +7,12 @@ import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.boss.ViaBossBar;
import java.util.SortedSet;
import java.util.UUID;
@Deprecated
public interface ViaVersionAPI {
public interface ViaVersionAPI extends ViaAPI<Player> {
/**
* Is the player connection modified by ViaVersion?
*

View File

@ -8,6 +8,7 @@ import us.myles.ViaVersion.api.boss.BossStyle;
@Getter
public class ViaBossBar extends CommonBoss {
// TODO: Fix to use generics
public ViaBossBar(String title, float health, BossColor color, BossStyle style) {
super(title, health, color, style);

View File

@ -0,0 +1,127 @@
package us.myles.ViaVersion.bukkit;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaAPI;
import us.myles.ViaVersion.api.ViaVersionAPI;
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.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.boss.ViaBossBar;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.ProtocolSupportUtil;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
@AllArgsConstructor
public class BukkitViaAPI implements ViaAPI<Player>, ViaVersionAPI {
public ViaVersionPlugin plugin;
@Override
public int getPlayerVersion(@NonNull Player player) {
if (!isPorted(player))
return getExternalVersion(player);
return getPortedPlayers().get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
}
@Override
public int getPlayerVersion(@NonNull UUID uuid) {
if (!isPorted(uuid))
return getExternalVersion(Bukkit.getPlayer(uuid));
return getPortedPlayers().get(uuid).get(ProtocolInfo.class).getProtocolVersion();
}
private int getExternalVersion(Player player) {
if (!isProtocolSupport()) {
return ProtocolRegistry.SERVER_PROTOCOL;
} else {
return ProtocolSupportUtil.getProtocolVersion(player);
}
}
@Override
public boolean isPorted(Player player) {
return isPorted(player.getUniqueId());
}
@Override
public boolean isPorted(UUID playerUUID) {
return getPortedPlayers().containsKey(playerUUID);
}
@Override
public String getVersion() {
return plugin.getDescription().getVersion();
}
@Override
public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException {
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!");
UserConnection ci = getPortedPlayers().get(uuid);
ci.sendRawPacket(packet);
}
@Override
public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException {
sendRawPacket(player.getUniqueId(), packet);
}
@Override
public BossBar createBossBar(String title, BossColor color, BossStyle style) {
return new ViaBossBar(title, 1F, color, style);
}
@Override
public BossBar createBossBar(String title, float health, BossColor color, BossStyle style) {
return new ViaBossBar(title, health, color, style);
}
@Override
public boolean isDebug() {
return Via.getManager().isDebug();
}
@Override
public ViaVersionCommand getCommandHandler() {
return Via.getManager().getCommandHandler();
}
@Override
public SortedSet<Integer> getSupportedVersions() {
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
outputSet.removeAll(Via.getPlatform().getConf().getBlockedProtocols());
return outputSet;
}
@Override
public boolean isCompatSpigotBuild() {
return plugin.isCompatSpigotBuild();
}
@Override
public boolean isSpigot() {
return plugin.isSpigot();
}
@Override
public boolean isProtocolSupport() {
return plugin.isProtocolSupport();
}
public Map<UUID, UserConnection> getPortedPlayers() {
return Via.getManager().getPortedPlayers();
}
}

View File

@ -0,0 +1,233 @@
package us.myles.ViaVersion.bukkit;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.bukkit.plugin.PluginDescriptionFile;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.platform.ViaInjector;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.util.ConcurrentList;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class BukkitViaInjector implements ViaInjector {
private List<ChannelFuture> injectedFutures = new ArrayList<>();
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
@Override
public void inject() {
try {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
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) {
injectChannelFuture((ChannelFuture) o);
}
}
}
};
injectedLists.add(new Pair<>(field, connection));
field.set(connection, wrapper);
// Iterate through current list
synchronized (wrapper) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
injectChannelFuture((ChannelFuture) o);
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
e.printStackTrace();
}
}
private void injectChannelFuture(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);
injectedFutures.add(future);
} catch (NoSuchFieldException e) {
// let's find who to blame!
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
} else {
throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
} catch (Exception e) {
getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?");
e.printStackTrace();
}
}
@Override
public void uninject() {
// TODO: Uninject from players currently online to prevent protocol lib issues.
for (ChannelFuture future : injectedFutures) {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (oldInit instanceof ViaVersionInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
System.out.println("Failed to remove injection handler, reload won't work with connections, please reboot!");
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Object o = pair.getKey().get(pair.getValue());
if (o instanceof ListWrapper) {
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
System.out.println("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
injectedLists.clear();
}
@Override
public int getServerProtocolVersion() {
try {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
Object ping = null;
// Search for ping method
for (Field f : serverClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().equals("ServerPing")) {
f.setAccessible(true);
ping = f.get(server);
}
}
}
if (ping != null) {
Object serverData = null;
for (Field f : pingClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().endsWith("ServerData")) {
f.setAccessible(true);
serverData = f.get(ping);
}
}
}
if (serverData != null) {
int protocolVersion = -1;
for (Field f : serverData.getClass().getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType() == int.class) {
f.setAccessible(true);
protocolVersion = (int) f.get(serverData);
}
}
}
if (protocolVersion != -1) {
return protocolVersion;
}
}
}
} catch (Exception e) {
e.printStackTrace();
// We couldn't work it out... We'll just use ping and hope for the best...
}
}
public static Object getServerConnection() throws Exception {
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);
}
}
}
}
return connection;
}
public static void patchLists() throws Exception {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
if (!(value instanceof ConcurrentList)) {
ConcurrentList list = new ConcurrentList();
list.addAll((List) value);
field.set(connection, list);
}
}
}
}
public static boolean isBinded() {
try {
Object connection = getServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return false;
}
}

View File

@ -20,7 +20,6 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
private final ByteToMessageDecoder minecraftDecoder;
private final UserConnection info;
public static int PASSTHROUGH_ID = 1000;
public ViaDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
this.info = info;

View File

@ -15,5 +15,4 @@ public class UpdateListener implements Listener {
UpdateUtil.sendUpdateMessage(e.getPlayer().getUniqueId());
}
}
}

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import org.bukkit.Bukkit;
import org.bukkit.World;

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

View File

@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
package us.myles.ViaVersion.listeners.protocol1_9to1_8;
import org.bukkit.Location;
import org.bukkit.Material;

View File

@ -7,6 +7,7 @@
<groupId>us.myles</groupId>
<version>1.0.0-ALPHA-16w38a</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>viaversion-common</artifactId>

View File

@ -0,0 +1,84 @@
package us.myles.ViaVersion;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.platform.ViaInjector;
import us.myles.ViaVersion.api.platform.ViaPlatform;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.update.UpdateUtil;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Getter
public class ViaManager {
private ViaPlatform platform;
private final Map<UUID, UserConnection> portedPlayers = new ConcurrentHashMap<>();
@Setter
private boolean debug = false;
// Internals
private ViaInjector injector;
private ViaVersionCommand commandHandler;
public ViaManager(ViaPlatform platform) {
this.platform = platform;
}
public void init() {
if (System.getProperty("ViaVersion") != null) {
// Reload?
platform.onReload();
}
// Check for updates
if (platform.getConf().isCheckForUpdates())
UpdateUtil.sendUpdateMessage();
// Inject
// TODO: Get errors
injector.inject();
// Mark as injected
System.setProperty("ViaVersion", getPlatform().getPluginVersion());
// If successful
// TODO: This method might run in onLoad, ensure sync tasks can still run if plugin not enabled.
platform.runSync(new Runnable() {
@Override
public void run() {
ProtocolRegistry.SERVER_PROTOCOL = injector.getServerProtocolVersion();
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
getPlatform().getLogger().info("ViaVersion detected server version: " + ProtocolVersion.getProtocol(ProtocolRegistry.SERVER_PROTOCOL));
if (!ProtocolRegistry.isWorkingPipe()) {
getPlatform().getLogger().warning("ViaVersion does not have any compatible versions for this server version, please read our resource page carefully.");
}
}
ProtocolRegistry.refreshVersions();
}
});
}
public void destroy() {
// Uninject
getPlatform().getLogger().info("ViaVersion is disabling, if this is a reload and you experience issues consider rebooting.");
injector.uninject();
}
public void addPortedClient(UserConnection info) {
portedPlayers.put(info.get(ProtocolInfo.class).getUuid(), info);
}
public void removePortedClient(UUID clientID) {
portedPlayers.remove(clientID);
}
public UserConnection getConnection(UUID playerUUID) {
return portedPlayers.get(playerUUID);
}
}

View File

@ -12,7 +12,6 @@ import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
import us.myles.ViaVersion.exception.InformativeException;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
@ -25,6 +24,8 @@ import java.util.LinkedList;
import java.util.List;
public class PacketWrapper {
public static int PASSTHROUGH_ID = 1000;
private final ByteBuf inputBuffer;
private final UserConnection userConnection;
private boolean send = true;
@ -461,7 +462,7 @@ public class PacketWrapper {
public void sendToServer() throws Exception {
if (!isCancelled()) {
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
Type.VAR_INT.write(output, ViaDecodeHandler.PASSTHROUGH_ID); // Pass through
Type.VAR_INT.write(output, PacketWrapper.PASSTHROUGH_ID); // Pass through
writeToBuffer(output);

View File

@ -2,14 +2,23 @@ package us.myles.ViaVersion.api;
import lombok.Getter;
import org.apache.commons.lang.Validate;
import us.myles.ViaVersion.ViaManager;
import us.myles.ViaVersion.api.platform.ViaPlatform;
public class Via {
@Getter
private static ViaPlatform platform;
@Getter
private static ViaManager manager;
public static void init(ViaPlatform platform) {
Validate.isTrue(platform == null, "Platform is already set");
Via.platform = platform;
Via.manager = new ViaManager(platform);
}
public static ViaAPI getAPI() {
Validate.isTrue(platform != null, "ViaVersion has not loaded the Platform");
return Via.platform.getApi();
}
}

View File

@ -0,0 +1,100 @@
package us.myles.ViaVersion.api;
import io.netty.buffer.ByteBuf;
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.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import java.util.SortedSet;
import java.util.UUID;
/**
* Represents the ViaAPI
*
* @param <T> The player type for the specific platform, for bukkit it's {@code ViaAPI<Player>}
*/
public interface ViaAPI<T> {
/**
* Get protocol number from a player
* Will also retrieve version from ProtocolSupport if it's being used.
*
* @param player Platform player object, eg. Bukkit this is Player
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
*/
int getPlayerVersion(T player);
/**
* Get protocol number from a player
*
* @param uuid UUID of a player
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
*/
int getPlayerVersion(UUID uuid);
/**
* Is player using 1.9?
*
* @param playerUUID UUID of a player
* @return True if the client is on 1.9
* @deprecated As of 0.9.9, because all players are ported use {@link #getPlayerVersion(UUID)}
*/
@Deprecated
boolean isPorted(UUID playerUUID);
/**
* Get the version of the plugin
*
* @return Plugin version
*/
String getVersion();
/**
* Send a raw packet to the player (Use new IDs)
*
* @param player Platform player object, eg. Bukkit this is Player
* @param packet The packet, you need a VarInt ID then the packet contents.
* @throws IllegalArgumentException If not on 1.9 throws IllegalArg
*/
void sendRawPacket(T player, ByteBuf packet) throws IllegalArgumentException;
/**
* Send a raw packet to the player (Use new IDs)
*
* @param uuid The uuid from the player to send packet
* @param packet The packet, you need a VarInt ID then the packet contents.
* @throws IllegalArgumentException If not on 1.9 throws IllegalArg
*/
void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException;
/**
* Create a new bossbar instance
*
* @param title The title
* @param color The color
* @param style The style
* @return BossBar instance
*/
BossBar createBossBar(String title, BossColor color, BossStyle style);
/**
* Create a new bossbar instance
*
* @param title The title
* @param health Number between 0 and 1
* @param color The color
* @param style The style
* @return BossBar instance
*/
BossBar createBossBar(String title, float health, BossColor color, BossStyle style);
/**
* Get the supported protocol versions
* This method removes any blocked protocol versions.
*
* @return a list of protocol versions
* @see ProtocolRegistry#getSupportedVersions() for full list.
*/
SortedSet<Integer> getSupportedVersions();
}

View File

@ -7,7 +7,6 @@ import io.netty.channel.socket.SocketChannel;
import lombok.Data;
import net.md_5.bungee.api.ChatColor;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.Map;

View File

@ -0,0 +1,9 @@
package us.myles.ViaVersion.api.platform;
public interface ViaInjector {
public void inject();
public void uninject();
public int getServerProtocolVersion();
}

View File

@ -1,9 +1,17 @@
package us.myles.ViaVersion.api.platform;
import us.myles.ViaVersion.api.ViaAPI;
import us.myles.ViaVersion.api.ViaVersionConfig;
import java.util.UUID;
import java.util.logging.Logger;
public interface ViaPlatform {
/**
* ViaPlatform represents a platform ViaVersion runs on
*
* @param <T> - The player type for the platform, used for API related methods
*/
public interface ViaPlatform<T> {
public Logger getLogger();
public String getPlatformName();
@ -19,4 +27,10 @@ public interface ViaPlatform {
public boolean kickPlayer(UUID uuid, String message);
public boolean isPluginEnabled();
public ViaAPI<T> getApi();
public ViaVersionConfig getConf();
public void onReload();
}

View File

@ -1,7 +1,7 @@
package us.myles.ViaVersion.api.protocol;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.platform.ViaPlatform;
import us.myles.ViaVersion.packets.Direction;
@ -74,7 +74,7 @@ public class ProtocolPipeline extends Protocol {
super.transform(direction, state, packetWrapper);
if (ViaVersion.getInstance().isDebug()) {
if (Via.getManager().isDebug()) {
// Debug packet
String packet = "UNKNOWN";
@ -109,7 +109,7 @@ public class ProtocolPipeline extends Protocol {
}
}
String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]";
ViaPlatform platform = ViaVersion.getPlatform();
ViaPlatform platform = Via.getPlatform();
String actualUsername = packetWrapper.user().get(ProtocolInfo.class).getUsername();
String username = actualUsername != null ? actualUsername + " " : "";

View File

@ -3,7 +3,7 @@ package us.myles.ViaVersion.api.protocol;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.protocols.base.BaseProtocol;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2TO1_9_3_4;
@ -61,7 +61,7 @@ public class ProtocolRegistry {
registryMap.get(version).put(output, protocol);
}
if (ViaVersion.getPlatform().isPluginEnabled()) {
if (Via.getPlatform().isPluginEnabled()) {
protocol.registerListeners();
refreshVersions();
} else {

View File

@ -15,8 +15,8 @@ import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.version.Metadata1_8Type;
import us.myles.ViaVersion.api.type.types.version.MetadataList1_8Type;
import us.myles.ViaVersion.listeners.protocol1_9to1_8.*;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.packets.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.*;

View File

@ -3,7 +3,7 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Getter;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@ -24,6 +24,7 @@ public class ClientChunks extends StoredObject {
static {
try {
// TODO: Abstract this ?
mapChunkBulkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk"));
mapChunkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk"));
if (ViaVersion.getInstance().isSpigot()) {
@ -31,7 +32,7 @@ public class ClientChunks extends StoredObject {
worldRef = ReflectionUtil.nms("World");
}
} catch (Exception e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to initialise chunks reflection", e);
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to initialise chunks reflection", e);
}
}
@ -83,7 +84,7 @@ public class ClientChunks extends StoredObject {
list.add(chunkPacket);
}
} catch (Exception e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to transform chunks bulk", e);
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to transform chunks bulk", e);
}
return list;
}

View File

@ -2,7 +2,7 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.type.PartialType;
@ -134,7 +134,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// Check remaining bytes
if (bytesLeft > 0) {
Bukkit.getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunks! (" + groundUp + ")");
Via.getPlatform().getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunks! (" + groundUp + ")");
}
// Return chunks

View File

@ -1,6 +1,5 @@
package us.myles.ViaVersion.protocols.protocolsnapshotto1_10;
import org.bukkit.Material;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import org.spacehq.opennbt.tag.builtin.StringTag;
import us.myles.ViaVersion.api.minecraft.item.Item;
@ -31,7 +30,7 @@ public class ItemRewriter {
}
private static boolean hasEntityTag(Item item) {
if (item != null && item.getId() == Material.MONSTER_EGG.getId()) {
if (item != null && item.getId() == 383) { // Monster Egg
CompoundTag tag = item.getTag();
if (tag != null && tag.contains("EntityTag") && tag.get("EntityTag") instanceof CompoundTag) {
if (((CompoundTag) tag.get("EntityTag")).get("id") instanceof StringTag) {

View File

@ -5,7 +5,7 @@ import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.md_5.bungee.api.ChatColor;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.Via;
import java.io.BufferedReader;
import java.io.IOException;
@ -24,16 +24,16 @@ public class UpdateUtil {
private final static Gson gson = new GsonBuilder().create();
public static void sendUpdateMessage(final UUID uuid) {
ViaVersion.getPlatform().runAsync(new Runnable() {
Via.getPlatform().runAsync(new Runnable() {
@Override
public void run() {
final String message = getUpdateMessage(false);
if (message != null) {
ViaVersion.getPlatform().runSync(
Via.getPlatform().runSync(
new Runnable() {
@Override
public void run() {
ViaVersion.getPlatform().sendMessage(uuid, PREFIX + message);
Via.getPlatform().sendMessage(uuid, PREFIX + message);
}
}
);
@ -43,16 +43,16 @@ public class UpdateUtil {
}
public static void sendUpdateMessage() {
ViaVersion.getPlatform().runAsync(new Runnable() {
Via.getPlatform().runAsync(new Runnable() {
@Override
public void run() {
final String message = getUpdateMessage(true);
if (message != null) {
ViaVersion.getPlatform().runSync(
Via.getPlatform().runSync(
new Runnable() {
@Override
public void run() {
ViaVersion.getPlatform().getLogger().warning(message);
Via.getPlatform().getLogger().warning(message);
}
}
);
@ -62,7 +62,7 @@ public class UpdateUtil {
}
private static String getUpdateMessage(boolean console) {
if (ViaVersion.getInstance().getVersion().equals("${project.version}")) {
if (Via.getPlatform().getPluginVersion().equals("${project.version}")) {
return "You are using a debug/custom version, consider updating.";
}
String newestString = getNewestVersion();
@ -75,7 +75,7 @@ public class UpdateUtil {
}
Version current;
try {
current = new Version(ViaVersion.getInstance().getVersion());
current = new Version(Via.getPlatform().getPluginVersion());
} catch (IllegalArgumentException e) {
return "You are using a custom version, consider updating.";
}
@ -97,7 +97,7 @@ public class UpdateUtil {
URL url = new URL(URL + PLUGIN + LATEST_VERSION + "?" + System.currentTimeMillis());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(true);
connection.addRequestProperty("User-Agent", "ViaVersion " + ViaVersion.getInstance().getVersion());
connection.addRequestProperty("User-Agent", "ViaVersion " + Via.getPlatform().getPluginVersion());
connection.setDoOutput(true);
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String input;

View File

@ -6,7 +6,6 @@ import java.util.List;
import java.util.ListIterator;
public abstract class ListWrapper implements List {
public final Object lock = new Object();
private final List list;
public ListWrapper(List inputList) {