Nothing like a change of coding style to neaten things up.

This commit is contained in:
md_5 2012-10-16 19:50:56 +11:00
parent e0b738e188
commit 6c66e18262
46 changed files with 807 additions and 377 deletions

View File

@ -26,7 +26,8 @@ import net.md_5.bungee.plugin.JavaPluginManager;
/** /**
* Main BungeeCord proxy class. * Main BungeeCord proxy class.
*/ */
public class BungeeCord { public class BungeeCord
{
/** /**
* Current software instance. * Current software instance.
@ -69,6 +70,7 @@ public class BungeeCord {
*/ */
public final JavaPluginManager pluginManager = new JavaPluginManager(); public final JavaPluginManager pluginManager = new JavaPluginManager();
{ {
commandMap.put("end", new CommandEnd()); commandMap.put("end", new CommandEnd());
commandMap.put("glist", new CommandList()); commandMap.put("glist", new CommandList());
@ -83,17 +85,21 @@ public class BungeeCord {
* @param args command line arguments, currently none are used * @param args command line arguments, currently none are used
* @throws IOException when the server cannot be started * @throws IOException when the server cannot be started
*/ */
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException
{
instance = new BungeeCord(); instance = new BungeeCord();
$().info("Enabled BungeeCord version " + instance.version); $().info("Enabled BungeeCord version " + instance.version);
instance.start(); instance.start();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (instance.isRunning) { while (instance.isRunning)
{
String line = br.readLine(); String line = br.readLine();
if (line != null) { if (line != null)
{
boolean handled = instance.dispatchCommand(line, ConsoleCommandSender.instance); boolean handled = instance.dispatchCommand(line, ConsoleCommandSender.instance);
if (!handled) { if (!handled)
{
System.err.println("Command not found"); System.err.println("Command not found");
} }
} }
@ -107,15 +113,19 @@ public class BungeeCord {
* @param sender which executed the command * @param sender which executed the command
* @return whether the command was handled or not. * @return whether the command was handled or not.
*/ */
public boolean dispatchCommand(String commandLine, CommandSender sender) { public boolean dispatchCommand(String commandLine, CommandSender sender)
{
String[] split = commandLine.trim().split(" "); String[] split = commandLine.trim().split(" ");
String commandName = split[0].toLowerCase(); String commandName = split[0].toLowerCase();
Command command = commandMap.get(commandName); Command command = commandMap.get(commandName);
if (command != null && !config.disabledCommands.contains(commandName)) { if (command != null && !config.disabledCommands.contains(commandName))
{
String[] args = Arrays.copyOfRange(split, 1, split.length); String[] args = Arrays.copyOfRange(split, 1, split.length);
try { try
{
command.execute(sender, args); command.execute(sender, args);
} catch (Exception ex) { } catch (Exception ex)
{
sender.sendMessage(ChatColor.RED + "An error occurred while executing this command!"); sender.sendMessage(ChatColor.RED + "An error occurred while executing this command!");
$().severe("----------------------- [Start of command error] -----------------------"); $().severe("----------------------- [Start of command error] -----------------------");
$().log(Level.SEVERE, "", ex); $().log(Level.SEVERE, "", ex);
@ -132,7 +142,8 @@ public class BungeeCord {
* *
* @throws IOException * @throws IOException
*/ */
public void start() throws IOException { public void start() throws IOException
{
config.load(); config.load();
isRunning = true; isRunning = true;
@ -152,16 +163,19 @@ public class BungeeCord {
* Destroy this proxy instance cleanly by kicking all users, saving the * Destroy this proxy instance cleanly by kicking all users, saving the
* configuration and closing all sockets. * configuration and closing all sockets.
*/ */
public void stop() { public void stop()
{
this.isRunning = false; this.isRunning = false;
$().info("Disabling plugin"); $().info("Disabling plugin");
pluginManager.onDisable(); pluginManager.onDisable();
$().info("Closing listen thread"); $().info("Closing listen thread");
try { try
{
listener.socket.close(); listener.socket.close();
listener.join(); listener.join();
} catch (InterruptedException | IOException ex) { } catch (InterruptedException | IOException ex)
{
$().severe("Could not close listen thread"); $().severe("Could not close listen thread");
} }
@ -169,15 +183,18 @@ public class BungeeCord {
threadPool.shutdown(); threadPool.shutdown();
$().info("Disconnecting " + connections.size() + " connections"); $().info("Disconnecting " + connections.size() + " connections");
for (UserConnection user : connections.values()) { for (UserConnection user : connections.values())
{
user.disconnect("Proxy restarting, brb."); user.disconnect("Proxy restarting, brb.");
} }
$().info("Saving reconnect locations"); $().info("Saving reconnect locations");
saveThread.interrupt(); saveThread.interrupt();
try { try
{
saveThread.join(); saveThread.join();
} catch (InterruptedException ex) { } catch (InterruptedException ex)
{
} }
$().info("Thank you and goodbye"); $().info("Thank you and goodbye");
@ -190,7 +207,8 @@ public class BungeeCord {
* @param socket to set the options on * @param socket to set the options on
* @throws IOException when the underlying set methods thrown an exception * @throws IOException when the underlying set methods thrown an exception
*/ */
public void setSocketOptions(Socket socket) throws IOException { public void setSocketOptions(Socket socket) throws IOException
{
socket.setSoTimeout(config.timeout); socket.setSoTimeout(config.timeout);
socket.setTrafficClass(0x18); socket.setTrafficClass(0x18);
socket.setTcpNoDelay(true); socket.setTcpNoDelay(true);

View File

@ -5,7 +5,8 @@ import java.util.regex.Pattern;
/** /**
* Simplistic enumeration of all supported color values for chat. * Simplistic enumeration of all supported color values for chat.
*/ */
public enum ChatColor { public enum ChatColor
{
/** /**
* Represents black. * Represents black.
@ -109,12 +110,17 @@ public enum ChatColor {
*/ */
private final String toString; private final String toString;
private ChatColor(char code) { private ChatColor(char code)
this.toString = new String(new char[]{COLOR_CHAR, code}); {
this.toString = new String(new char[]
{
COLOR_CHAR, code
});
} }
@Override @Override
public String toString() { public String toString()
{
return toString; return toString;
} }
@ -124,8 +130,10 @@ public enum ChatColor {
* @param input String to strip of color * @param input String to strip of color
* @return A copy of the input string, without any coloring * @return A copy of the input string, without any coloring
*/ */
public static String stripColor(final String input) { public static String stripColor(final String input)
if (input == null) { {
if (input == null)
{
return null; return null;
} }

View File

@ -23,7 +23,8 @@ import org.yaml.snakeyaml.Yaml;
/** /**
* Core configuration for the proxy. * Core configuration for the proxy.
*/ */
public class Configuration { public class Configuration
{
/** /**
* Reconnect locations file. * Reconnect locations file.
@ -68,7 +69,9 @@ public class Configuration {
/** /**
* All servers. * All servers.
*/ */
public Map<String, String> servers = new HashMap<String, String>() { public Map<String, String> servers = new HashMap<String, String>()
{
{ {
put(defaultServerName, "127.0.0.1:1338"); put(defaultServerName, "127.0.0.1:1338");
put("pvp", "127.0.0.1:1337"); put("pvp", "127.0.0.1:1337");
@ -77,7 +80,9 @@ public class Configuration {
/** /**
* Forced servers. * Forced servers.
*/ */
public Map<String, String> forcedServers = new HashMap<String, String>() { public Map<String, String> forcedServers = new HashMap<String, String>()
{
{ {
put("pvp.md-5.net", "pvp"); put("pvp.md-5.net", "pvp");
} }
@ -85,7 +90,9 @@ public class Configuration {
/** /**
* Proxy admins. * Proxy admins.
*/ */
public List<String> admins = new ArrayList<String>() { public List<String> admins = new ArrayList<String>()
{
{ {
add("Insert Admins Here"); add("Insert Admins Here");
} }
@ -93,7 +100,9 @@ public class Configuration {
/** /**
* Proxy moderators. * Proxy moderators.
*/ */
public List<String> moderators = new ArrayList<String>() { public List<String> moderators = new ArrayList<String>()
{
{ {
add("Insert Moderators Here"); add("Insert Moderators Here");
} }
@ -101,7 +110,9 @@ public class Configuration {
/** /**
* Commands which will be blocked completely. * Commands which will be blocked completely.
*/ */
public List<String> disabledCommands = new ArrayList<String>() { public List<String> disabledCommands = new ArrayList<String>()
{
{ {
add("glist"); add("glist");
} }
@ -118,76 +129,96 @@ public class Configuration {
/** /**
* Load the configuration and save default values. * Load the configuration and save default values.
*/ */
public void load() { public void load()
try { {
try
{
file.createNewFile(); file.createNewFile();
DumperOptions options = new DumperOptions(); DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yaml = new Yaml(options); yaml = new Yaml(options);
try (InputStream is = new FileInputStream(file)) { try (InputStream is = new FileInputStream(file))
{
config = (Map) yaml.load(is); config = (Map) yaml.load(is);
} }
if (config == null) { if (config == null)
{
config = new LinkedHashMap<>(); config = new LinkedHashMap<>();
} }
$().info("-------------- Loading configuration ----------------"); $().info("-------------- Loading configuration ----------------");
for (Field field : getClass().getDeclaredFields()) { for (Field field : getClass().getDeclaredFields())
if (!Modifier.isTransient(field.getModifiers())) { {
if (!Modifier.isTransient(field.getModifiers()))
{
String name = Util.normalize(field.getName()); String name = Util.normalize(field.getName());
try { try
{
Object def = field.get(this); Object def = field.get(this);
Object value = get(name, def); Object value = get(name, def);
field.set(this, value); field.set(this, value);
$().info(name + ": " + value); $().info(name + ": " + value);
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex)
{
$().severe("Could not get config node: " + name); $().severe("Could not get config node: " + name);
} }
} }
} }
$().info("-----------------------------------------------------"); $().info("-----------------------------------------------------");
if (servers.get(defaultServerName) == null) { if (servers.get(defaultServerName) == null)
{
throw new IllegalArgumentException("Server '" + defaultServerName + "' not defined"); throw new IllegalArgumentException("Server '" + defaultServerName + "' not defined");
} }
for (String server : forcedServers.values()) { for (String server : forcedServers.values())
if (!servers.containsKey(server)) { {
if (!servers.containsKey(server))
{
throw new IllegalArgumentException("Forced server " + server + " is not defined in servers"); throw new IllegalArgumentException("Forced server " + server + " is not defined in servers");
} }
} }
reconnect.createNewFile(); reconnect.createNewFile();
try (FileInputStream recon = new FileInputStream(reconnect)) { try (FileInputStream recon = new FileInputStream(reconnect))
{
reconnectLocations = (Map) yaml.load(recon); reconnectLocations = (Map) yaml.load(recon);
} }
if (reconnectLocations == null) { if (reconnectLocations == null)
{
reconnectLocations = new LinkedHashMap<>(); reconnectLocations = new LinkedHashMap<>();
} }
} catch (IOException ex) { } catch (IOException ex)
{
$().severe("Could not load config!"); $().severe("Could not load config!");
ex.printStackTrace(); ex.printStackTrace();
} }
} }
private <T> T get(String path, T def) { private <T> T get(String path, T def)
if (!config.containsKey(path)) { {
if (!config.containsKey(path))
{
config.put(path, def); config.put(path, def);
save(file, config); save(file, config);
} }
return (T) config.get(path); return (T) config.get(path);
} }
private void save(File fileToSave, Map toSave) { private void save(File fileToSave, Map toSave)
try { {
try (FileWriter wr = new FileWriter(fileToSave)) { try
{
try (FileWriter wr = new FileWriter(fileToSave))
{
yaml.dump(toSave, wr); yaml.dump(toSave, wr);
} }
} catch (IOException ex) { } catch (IOException ex)
{
$().severe("Could not save config file " + fileToSave); $().severe("Could not save config file " + fileToSave);
ex.printStackTrace(); ex.printStackTrace();
} }
@ -201,12 +232,15 @@ public class Configuration {
* @param requestedHost the host which they connected to * @param requestedHost the host which they connected to
* @return the name of the server which they should be connected to. * @return the name of the server which they should be connected to.
*/ */
public String getServer(String user, String requestedHost) { public String getServer(String user, String requestedHost)
{
String server = forcedServers.get(requestedHost); String server = forcedServers.get(requestedHost);
if (server == null) { if (server == null)
{
server = reconnectLocations.get(user); server = reconnectLocations.get(user);
} }
if (server == null) { if (server == null)
{
server = servers.get(defaultServerName); server = servers.get(defaultServerName);
} }
return server; return server;
@ -218,7 +252,8 @@ public class Configuration {
* @param user the name of the user * @param user the name of the user
* @param server which they were last on * @param server which they were last on
*/ */
public void setServer(UserConnection user, String server) { public void setServer(UserConnection user, String server)
{
reconnectLocations.put(user.username, server); reconnectLocations.put(user.username, server);
} }
@ -228,7 +263,8 @@ public class Configuration {
* @param name the friendly name of a server * @param name the friendly name of a server
* @return the usable {@link InetSocketAddress} mapped to this server * @return the usable {@link InetSocketAddress} mapped to this server
*/ */
public InetSocketAddress getServer(String name) { public InetSocketAddress getServer(String name)
{
String server = servers.get((name == null) ? defaultServerName : name); String server = servers.get((name == null) ? defaultServerName : name);
return (server != null) ? Util.getAddr(server) : getServer(null); return (server != null) ? Util.getAddr(server) : getServer(null);
} }
@ -236,7 +272,8 @@ public class Configuration {
/** /**
* Save the current mappings of users to servers. * Save the current mappings of users to servers.
*/ */
public void saveHosts() { public void saveHosts()
{
save(reconnect, reconnectLocations); save(reconnect, reconnectLocations);
$().info("Saved reconnect locations to " + reconnect); $().info("Saved reconnect locations to " + reconnect);
} }
@ -247,11 +284,14 @@ public class Configuration {
* @param sender to get permissions of * @param sender to get permissions of
* @return their permission * @return their permission
*/ */
public Permission getPermission(CommandSender sender) { public Permission getPermission(CommandSender sender)
{
Permission permission = Permission.DEFAULT; Permission permission = Permission.DEFAULT;
if (admins.contains(sender.getName()) || sender instanceof ConsoleCommandSender) { if (admins.contains(sender.getName()) || sender instanceof ConsoleCommandSender)
{
permission = Permission.ADMIN; permission = Permission.ADMIN;
} else if (moderators.contains(sender.getName())) { } else if (moderators.contains(sender.getName()))
{
permission = Permission.MODERATOR; permission = Permission.MODERATOR;
} }
return permission; return permission;

View File

@ -38,18 +38,22 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
/** /**
* Class containing all encryption related methods for the proxy. * Class containing all encryption related methods for the proxy.
*/ */
public class EncryptionUtil { public class EncryptionUtil
{
private static final Random secure = new SecureRandom(); private static final Random secure = new SecureRandom();
private static final Random random = new Random(); private static final Random random = new Random();
private static KeyPair keys; private static KeyPair keys;
static { static
{
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
} }
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException { public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
if (keys == null) { {
if (keys == null)
{
keys = KeyPairGenerator.getInstance("RSA").generateKeyPair(); keys = KeyPairGenerator.getInstance("RSA").generateKeyPair();
} }
@ -60,12 +64,14 @@ public class EncryptionUtil {
return new PacketFDEncryptionRequest(hash, pubKey, verify); return new PacketFDEncryptionRequest(hash, pubKey, verify);
} }
public static SecretKey getSecret(PacketFCEncryptionResponse resp, PacketFDEncryptionRequest request) throws BadPaddingException, IllegalBlockSizeException, IllegalStateException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { public static SecretKey getSecret(PacketFCEncryptionResponse resp, PacketFDEncryptionRequest request) throws BadPaddingException, IllegalBlockSizeException, IllegalStateException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher cipher = Cipher.getInstance("RSA"); Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, keys.getPrivate()); cipher.init(Cipher.DECRYPT_MODE, keys.getPrivate());
byte[] decrypted = cipher.doFinal(resp.verifyToken); byte[] decrypted = cipher.doFinal(resp.verifyToken);
if (!Arrays.equals(request.verifyToken, decrypted)) { if (!Arrays.equals(request.verifyToken, decrypted))
{
throw new IllegalStateException("Key pairs do not match!"); throw new IllegalStateException("Key pairs do not match!");
} }
@ -76,47 +82,58 @@ public class EncryptionUtil {
return new SecretKeySpec(secret, "AES"); return new SecretKeySpec(secret, "AES");
} }
public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException { public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException
{
String encName = URLEncoder.encode(username, "UTF-8"); String encName = URLEncoder.encode(username, "UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1"); MessageDigest sha = MessageDigest.getInstance("SHA-1");
for (byte[] bit : new byte[][]{connectionHash.getBytes("ISO_8859_1"), shared.getEncoded(), keys.getPublic().getEncoded()}) { for (byte[] bit : new byte[][]
{
connectionHash.getBytes("ISO_8859_1"), shared.getEncoded(), keys.getPublic().getEncoded()
})
{
sha.update(bit); sha.update(bit);
} }
String encodedHash = URLEncoder.encode(new BigInteger(sha.digest()).toString(16), "UTF-8"); String encodedHash = URLEncoder.encode(new BigInteger(sha.digest()).toString(16), "UTF-8");
String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash; String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash;
String reply; String reply;
try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL(authURL).openStream()))) { try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL(authURL).openStream())))
{
reply = in.readLine(); reply = in.readLine();
} }
return "YES".equals(reply); return "YES".equals(reply);
} }
public static BufferedBlockCipher getCipher(boolean forEncryption, Key shared) { public static BufferedBlockCipher getCipher(boolean forEncryption, Key shared)
{
BufferedBlockCipher cip = new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 8)); BufferedBlockCipher cip = new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 8));
cip.init(forEncryption, new ParametersWithIV(new KeyParameter(shared.getEncoded()), shared.getEncoded(), 0, 16)); cip.init(forEncryption, new ParametersWithIV(new KeyParameter(shared.getEncoded()), shared.getEncoded(), 0, 16));
return cip; return cip;
} }
public static SecretKey getSecret() { public static SecretKey getSecret()
{
byte[] rand = new byte[32]; byte[] rand = new byte[32];
secure.nextBytes(rand); secure.nextBytes(rand);
return new SecretKeySpec(rand, "AES"); return new SecretKeySpec(rand, "AES");
} }
public static PublicKey getPubkey(PacketFDEncryptionRequest request) throws InvalidKeySpecException, NoSuchAlgorithmException { public static PublicKey getPubkey(PacketFDEncryptionRequest request) throws InvalidKeySpecException, NoSuchAlgorithmException
{
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(request.publicKey)); return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(request.publicKey));
} }
public static byte[] encrypt(Key key, byte[] b) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { public static byte[] encrypt(Key key, byte[] b) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher hasher = Cipher.getInstance("RSA"); Cipher hasher = Cipher.getInstance("RSA");
hasher.init(Cipher.ENCRYPT_MODE, key); hasher.init(Cipher.ENCRYPT_MODE, key);
return hasher.doFinal(b); return hasher.doFinal(b);
} }
public static byte[] getShared(SecretKey key, PublicKey pubkey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { public static byte[] getShared(SecretKey key, PublicKey pubkey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher cipher = Cipher.getInstance("RSA"); Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubkey); cipher.init(Cipher.ENCRYPT_MODE, pubkey);
return cipher.doFinal(key.getEncoded()); return cipher.doFinal(key.getEncoded());

View File

@ -3,47 +3,120 @@ package net.md_5.bungee;
/** /**
* Class to rewrite integers within packets. * Class to rewrite integers within packets.
*/ */
public class EntityMap { public class EntityMap
{
public final static int[][] entityIds = new int[256][]; public final static int[][] entityIds = new int[256][];
static { static
entityIds[0x05] = new int[]{1}; {
entityIds[0x07] = new int[]{1, 5}; entityIds[0x05] = new int[]
entityIds[0x11] = new int[]{1}; {
entityIds[0x12] = new int[]{1}; 1
entityIds[0x13] = new int[]{1}; };
entityIds[0x14] = new int[]{1}; entityIds[0x07] = new int[]
entityIds[0x15] = new int[]{1}; {
entityIds[0x16] = new int[]{1, 5}; 1, 5
entityIds[0x17] = new int[]{1}; };
entityIds[0x18] = new int[]{1}; entityIds[0x11] = new int[]
entityIds[0x19] = new int[]{1}; {
entityIds[0x1C] = new int[]{1}; 1
entityIds[0x1E] = new int[]{1}; };
entityIds[0x1F] = new int[]{1}; entityIds[0x12] = new int[]
entityIds[0x20] = new int[]{1}; {
entityIds[0x21] = new int[]{1}; 1
entityIds[0x22] = new int[]{1}; };
entityIds[0x26] = new int[]{1}; entityIds[0x13] = new int[]
entityIds[0x27] = new int[]{1, 5}; {
entityIds[0x28] = new int[]{1}; 1
entityIds[0x47] = new int[]{1}; };
entityIds[0x14] = new int[]
{
1
};
entityIds[0x15] = new int[]
{
1
};
entityIds[0x16] = new int[]
{
1, 5
};
entityIds[0x17] = new int[]
{
1
};
entityIds[0x18] = new int[]
{
1
};
entityIds[0x19] = new int[]
{
1
};
entityIds[0x1C] = new int[]
{
1
};
entityIds[0x1E] = new int[]
{
1
};
entityIds[0x1F] = new int[]
{
1
};
entityIds[0x20] = new int[]
{
1
};
entityIds[0x21] = new int[]
{
1
};
entityIds[0x22] = new int[]
{
1
};
entityIds[0x26] = new int[]
{
1
};
entityIds[0x27] = new int[]
{
1, 5
};
entityIds[0x28] = new int[]
{
1
};
entityIds[0x47] = new int[]
{
1
};
} }
public static void rewrite(byte[] packet, int oldId, int newId) { public static void rewrite(byte[] packet, int oldId, int newId)
{
int packetId = Util.getId(packet); int packetId = Util.getId(packet);
if (packetId == 0x1D) { // bulk entity if (packetId == 0x1D)
for (int pos = 2; pos < packet.length; pos += 4) { { // bulk entity
if (oldId == readInt(packet, pos)) { for (int pos = 2; pos < packet.length; pos += 4)
{
if (oldId == readInt(packet, pos))
{
setInt(packet, pos, newId); setInt(packet, pos, newId);
} }
} }
} else { } else
{
int[] idArray = entityIds[packetId]; int[] idArray = entityIds[packetId];
if (idArray != null) { if (idArray != null)
for (int pos : idArray) { {
if (oldId == readInt(packet, pos)) { for (int pos : idArray)
{
if (oldId == readInt(packet, pos))
{
setInt(packet, pos, newId); setInt(packet, pos, newId);
} }
} }
@ -51,14 +124,16 @@ public class EntityMap {
} }
} }
private static void setInt(byte[] buf, int pos, int i) { private static void setInt(byte[] buf, int pos, int i)
{
buf[pos] = (byte) (i >> 24); buf[pos] = (byte) (i >> 24);
buf[pos + 1] = (byte) (i >> 16); buf[pos + 1] = (byte) (i >> 16);
buf[pos + 2] = (byte) (i >> 8); buf[pos + 2] = (byte) (i >> 8);
buf[pos + 3] = (byte) i; buf[pos + 3] = (byte) i;
} }
private static int readInt(byte[] buf, int pos) { private static int readInt(byte[] buf, int pos)
{
return (((buf[pos] & 0xFF) << 24) | ((buf[pos + 1] & 0xFF) << 16) | ((buf[pos + 2] & 0xFF) << 8) | buf[pos + 3] & 0xFF); return (((buf[pos] & 0xFF) << 24) | ((buf[pos + 1] & 0xFF) << 16) | ((buf[pos + 2] & 0xFF) << 8) | buf[pos + 3] & 0xFF);
} }
} }

View File

@ -14,7 +14,8 @@ import net.md_5.bungee.packet.PacketInputStream;
*/ */
@EqualsAndHashCode @EqualsAndHashCode
@RequiredArgsConstructor @RequiredArgsConstructor
public class GenericConnection { public class GenericConnection
{
protected final Socket socket; protected final Socket socket;
protected final PacketInputStream in; protected final PacketInputStream in;
@ -26,24 +27,32 @@ public class GenericConnection {
* *
* @param reason to disconnect * @param reason to disconnect
*/ */
public void disconnect(String reason) { public void disconnect(String reason)
if (socket.isClosed()) { {
if (socket.isClosed())
{
return; return;
} }
log("disconnected with " + reason); log("disconnected with " + reason);
try { try
{
out.write(new PacketFFKick("[Proxy] " + reason).getPacket()); out.write(new PacketFFKick("[Proxy] " + reason).getPacket());
} catch (IOException ex) { } catch (IOException ex)
} finally { {
try { } finally
{
try
{
out.flush(); out.flush();
socket.close(); socket.close();
} catch (IOException ioe) { } catch (IOException ioe)
{
} }
} }
} }
public void log(String message) { public void log(String message)
{
$().info(socket.getInetAddress() + ((username == null) ? " " : " [" + username + "] ") + message); $().info(socket.getInetAddress() + ((username == null) ? " " : " [" + username + "] ") + message);
} }
} }

View File

@ -13,30 +13,36 @@ import net.md_5.bungee.plugin.HandshakeEvent;
import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream; import org.bouncycastle.crypto.io.CipherOutputStream;
public class InitialHandler implements Runnable { public class InitialHandler implements Runnable
{
private final Socket socket; private final Socket socket;
private PacketInputStream in; private PacketInputStream in;
private OutputStream out; private OutputStream out;
public InitialHandler(Socket socket) throws IOException { public InitialHandler(Socket socket) throws IOException
{
this.socket = socket; this.socket = socket;
in = new PacketInputStream(socket.getInputStream()); in = new PacketInputStream(socket.getInputStream());
out = socket.getOutputStream(); out = socket.getOutputStream();
} }
@Override @Override
public void run() { public void run()
try { {
try
{
byte[] packet = in.readPacket(); byte[] packet = in.readPacket();
int id = Util.getId(packet); int id = Util.getId(packet);
switch (id) { switch (id)
{
case 0x02: case 0x02:
Packet2Handshake handshake = new Packet2Handshake(packet); Packet2Handshake handshake = new Packet2Handshake(packet);
// fire connect event // fire connect event
HandshakeEvent event = new HandshakeEvent(handshake.username, socket.getInetAddress()); HandshakeEvent event = new HandshakeEvent(handshake.username, socket.getInetAddress());
BungeeCord.instance.pluginManager.onHandshake(event); BungeeCord.instance.pluginManager.onHandshake(event);
if (event.isCancelled()) { if (event.isCancelled())
{
throw new KickException(event.getCancelReason()); throw new KickException(event.getCancelReason());
} }
@ -45,7 +51,8 @@ public class InitialHandler implements Runnable {
PacketFCEncryptionResponse response = new PacketFCEncryptionResponse(in.readPacket()); PacketFCEncryptionResponse response = new PacketFCEncryptionResponse(in.readPacket());
SecretKey shared = EncryptionUtil.getSecret(response, request); SecretKey shared = EncryptionUtil.getSecret(response, request);
if (!EncryptionUtil.isAuthenticated(handshake.username, request.serverId, shared)) { if (!EncryptionUtil.isAuthenticated(handshake.username, request.serverId, shared))
{
throw new KickException("Not authenticated with minecraft.net"); throw new KickException("Not authenticated with minecraft.net");
} }
@ -54,7 +61,8 @@ public class InitialHandler implements Runnable {
out = new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared)); out = new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared));
int ciphId = Util.getId(in.readPacket()); int ciphId = Util.getId(in.readPacket());
if (ciphId != 0xCD) { if (ciphId != 0xCD)
{
throw new KickException("Unable to receive encrypted client status"); throw new KickException("Unable to receive encrypted client status");
} }
@ -66,22 +74,30 @@ public class InitialHandler implements Runnable {
default: default:
throw new IllegalArgumentException("Wasn't ready for packet id " + Util.hex(id)); throw new IllegalArgumentException("Wasn't ready for packet id " + Util.hex(id));
} }
} catch (KickException ex) { } catch (KickException ex)
{
kick(ex.getMessage()); kick(ex.getMessage());
} catch (Exception ex) { } catch (Exception ex)
{
kick("[Proxy Error] " + Util.exception(ex)); kick("[Proxy Error] " + Util.exception(ex));
} }
} }
private void kick(String message) { private void kick(String message)
try { {
try
{
out.write(new PacketFFKick(message).getPacket()); out.write(new PacketFFKick(message).getPacket());
} catch (IOException ioe) { } catch (IOException ioe)
} finally { {
try { } finally
{
try
{
out.flush(); out.flush();
socket.close(); socket.close();
} catch (IOException ioe2) { } catch (IOException ioe2)
{
} }
} }
} }

View File

@ -4,9 +4,11 @@ package net.md_5.bungee;
* Exception, which when thrown will disconnect the player from the proxy with * Exception, which when thrown will disconnect the player from the proxy with
* the specified message. * the specified message.
*/ */
public class KickException extends RuntimeException { public class KickException extends RuntimeException
{
public KickException(String message) { public KickException(String message)
{
super(message); super(message);
} }
} }

View File

@ -10,27 +10,34 @@ import static net.md_5.bungee.Logger.$;
/** /**
* Thread to listen and dispatch incoming connections to the proxy. * Thread to listen and dispatch incoming connections to the proxy.
*/ */
public class ListenThread extends Thread { public class ListenThread extends Thread
{
public final ServerSocket socket; public final ServerSocket socket;
public ListenThread(InetSocketAddress addr) throws IOException { public ListenThread(InetSocketAddress addr) throws IOException
{
super("Listen Thread"); super("Listen Thread");
socket = new ServerSocket(); socket = new ServerSocket();
socket.bind(addr); socket.bind(addr);
} }
@Override @Override
public void run() { public void run()
while (BungeeCord.instance.isRunning) { {
try { while (BungeeCord.instance.isRunning)
{
try
{
Socket client = socket.accept(); Socket client = socket.accept();
BungeeCord.instance.setSocketOptions(client); BungeeCord.instance.setSocketOptions(client);
$().info(client.getInetAddress() + " has connected"); $().info(client.getInetAddress() + " has connected");
InitialHandler handler = new InitialHandler(client); InitialHandler handler = new InitialHandler(client);
BungeeCord.instance.threadPool.submit(handler); BungeeCord.instance.threadPool.submit(handler);
} catch (SocketException ex) { } catch (SocketException ex)
} catch (IOException ex) { {
} catch (IOException ex)
{
ex.printStackTrace(); // TODO ex.printStackTrace(); // TODO
} }
} }

View File

@ -12,30 +12,37 @@ import java.util.logging.LogRecord;
/** /**
* Logger to handle formatting and storage of the proxy's logger. * Logger to handle formatting and storage of the proxy's logger.
*/ */
public class Logger extends java.util.logging.Logger { public class Logger extends java.util.logging.Logger
{
private static final Formatter formatter = new ConsoleLogFormatter(); private static final Formatter formatter = new ConsoleLogFormatter();
private static final Logger instance = new Logger(); private static final Logger instance = new Logger();
public Logger() { public Logger()
{
super("RubberBand", null); super("RubberBand", null);
try { try
{
FileHandler handler = new FileHandler("proxy.log", BungeeCord.instance.config.logNumLines, 1, true); FileHandler handler = new FileHandler("proxy.log", BungeeCord.instance.config.logNumLines, 1, true);
handler.setFormatter(formatter); handler.setFormatter(formatter);
addHandler(handler); addHandler(handler);
} catch (IOException ex) { } catch (IOException ex)
{
System.err.println("Could not register logger!"); System.err.println("Could not register logger!");
ex.printStackTrace(); ex.printStackTrace();
} }
} }
@Override @Override
public void log(LogRecord record) { public void log(LogRecord record)
{
super.log(record); super.log(record);
String message = formatter.format(record); String message = formatter.format(record);
if (record.getLevel() == Level.SEVERE || record.getLevel() == Level.WARNING) { if (record.getLevel() == Level.SEVERE || record.getLevel() == Level.WARNING)
{
System.err.print(message); System.err.print(message);
} else { } else
{
System.out.print(message); System.out.print(message);
} }
} }
@ -45,32 +52,41 @@ public class Logger extends java.util.logging.Logger {
* *
* @return the current logger instance * @return the current logger instance
*/ */
public static Logger $() { public static Logger $()
{
return instance; return instance;
} }
public static class ConsoleLogFormatter extends Formatter { public static class ConsoleLogFormatter extends Formatter
{
private SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); private SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
@Override @Override
public String format(LogRecord logrecord) { public String format(LogRecord logrecord)
{
StringBuilder formatted = new StringBuilder(); StringBuilder formatted = new StringBuilder();
formatted.append(formatter.format(logrecord.getMillis())); formatted.append(formatter.format(logrecord.getMillis()));
Level level = logrecord.getLevel(); Level level = logrecord.getLevel();
if (level == Level.FINEST) { if (level == Level.FINEST)
{
formatted.append(" [FINEST] "); formatted.append(" [FINEST] ");
} else if (level == Level.FINER) { } else if (level == Level.FINER)
{
formatted.append(" [FINER] "); formatted.append(" [FINER] ");
} else if (level == Level.FINE) { } else if (level == Level.FINE)
{
formatted.append(" [FINE] "); formatted.append(" [FINE] ");
} else if (level == Level.INFO) { } else if (level == Level.INFO)
{
formatted.append(" [INFO] "); formatted.append(" [INFO] ");
} else if (level == Level.WARNING) { } else if (level == Level.WARNING)
{
formatted.append(" [WARNING] "); formatted.append(" [WARNING] ");
} else if (level == Level.SEVERE) { } else if (level == Level.SEVERE)
{
formatted.append(" [SEVERE] "); formatted.append(" [SEVERE] ");
} }
@ -78,7 +94,8 @@ public class Logger extends java.util.logging.Logger {
formatted.append('\n'); formatted.append('\n');
Throwable throwable = logrecord.getThrown(); Throwable throwable = logrecord.getThrown();
if (throwable != null) { if (throwable != null)
{
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer)); throwable.printStackTrace(new PrintWriter(writer));

View File

@ -10,7 +10,8 @@ import java.net.URLConnection;
import java.net.URLEncoder; import java.net.URLEncoder;
import static net.md_5.bungee.Logger.$; import static net.md_5.bungee.Logger.$;
public class Metrics extends Thread { public class Metrics extends Thread
{
/** /**
* The current revision number * The current revision number
@ -29,16 +30,20 @@ public class Metrics extends Thread {
*/ */
private final static int PING_INTERVAL = 10; private final static int PING_INTERVAL = 10;
public Metrics() { public Metrics()
{
super("Metrics Gathering Thread"); super("Metrics Gathering Thread");
setDaemon(true); setDaemon(true);
} }
@Override @Override
public void run() { public void run()
{
boolean firstPost = true; boolean firstPost = true;
while (true) { while (true)
try { {
try
{
// We use the inverse of firstPost because if it is the first time we are posting, // We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE // it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING! // Each time thereafter it will evaluate to TRUE, i.e PING!
@ -47,12 +52,15 @@ public class Metrics extends Thread {
// After the first post we set firstPost to false // After the first post we set firstPost to false
// Each post thereafter will be a ping // Each post thereafter will be a ping
firstPost = false; firstPost = false;
} catch (IOException ex) { } catch (IOException ex)
{
$().info("[Metrics] " + ex.getMessage()); $().info("[Metrics] " + ex.getMessage());
} }
try { try
{
sleep(PING_INTERVAL * 1000 * 60); sleep(PING_INTERVAL * 1000 * 60);
} catch (InterruptedException ex) { } catch (InterruptedException ex)
{
break; break;
} }
} }
@ -61,7 +69,8 @@ public class Metrics extends Thread {
/** /**
* Generic method that posts a plugin to the metrics website * Generic method that posts a plugin to the metrics website
*/ */
private void postPlugin(boolean isPing) throws IOException { private void postPlugin(boolean isPing) throws IOException
{
// Construct the post data // Construct the post data
final StringBuilder data = new StringBuilder(); final StringBuilder data = new StringBuilder();
data.append(encode("guid")).append('=').append(encode(BungeeCord.instance.config.statsUuid)); data.append(encode("guid")).append('=').append(encode(BungeeCord.instance.config.statsUuid));
@ -71,7 +80,8 @@ public class Metrics extends Thread {
encodeDataPair(data, "revision", String.valueOf(REVISION)); encodeDataPair(data, "revision", String.valueOf(REVISION));
// If we're pinging, append it // If we're pinging, append it
if (isPing) { if (isPing)
{
encodeDataPair(data, "ping", "true"); encodeDataPair(data, "ping", "true");
} }
@ -86,7 +96,8 @@ public class Metrics extends Thread {
connection.setDoOutput(true); connection.setDoOutput(true);
final BufferedReader reader; final BufferedReader reader;
final String response; final String response;
try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) { try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()))
{
writer.write(data.toString()); writer.write(data.toString());
writer.flush(); writer.flush();
reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
@ -94,7 +105,8 @@ public class Metrics extends Thread {
} }
reader.close(); reader.close();
if (response == null || response.startsWith("ERR")) { if (response == null || response.startsWith("ERR"))
{
throw new IOException(response); //Throw the exception throw new IOException(response); //Throw the exception
} }
} }
@ -113,7 +125,8 @@ public class Metrics extends Thread {
* @param key the key value * @param key the key value
* @param value the value * @param value the value
*/ */
private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException
{
buffer.append('&').append(encode(key)).append('=').append(encode(value)); buffer.append('&').append(encode(key)).append('=').append(encode(value));
} }
@ -123,7 +136,8 @@ public class Metrics extends Thread {
* @param text the text to encode * @param text the text to encode
* @return the encoded text, as UTF-8 * @return the encoded text, as UTF-8
*/ */
private static String encode(final String text) throws UnsupportedEncodingException { private static String encode(final String text) throws UnsupportedEncodingException
{
return URLEncoder.encode(text, "UTF-8"); return URLEncoder.encode(text, "UTF-8");
} }
} }

View File

@ -1,6 +1,7 @@
package net.md_5.bungee; package net.md_5.bungee;
public enum Permission { public enum Permission
{
/** /**
* Can access all commands. * Can access all commands.

View File

@ -4,19 +4,25 @@ package net.md_5.bungee;
* Class to call the {@link Configuration#saveHosts() } method at 5 minute * Class to call the {@link Configuration#saveHosts() } method at 5 minute
* intervals. * intervals.
*/ */
public class ReconnectSaveThread extends Thread { public class ReconnectSaveThread extends Thread
{
public ReconnectSaveThread() { public ReconnectSaveThread()
{
super("Location Save Thread"); super("Location Save Thread");
setPriority(Thread.MIN_PRIORITY); setPriority(Thread.MIN_PRIORITY);
} }
@Override @Override
public void run() { public void run()
while (BungeeCord.instance.isRunning) { {
try { while (BungeeCord.instance.isRunning)
{
try
{
Thread.sleep(5 * 1000 * 60); // 5 minutes Thread.sleep(5 * 1000 * 60); // 5 minutes
} catch (InterruptedException ex) { } catch (InterruptedException ex)
{
} }
BungeeCord.instance.config.saveHosts(); BungeeCord.instance.config.saveHosts();
} }

View File

@ -19,19 +19,23 @@ import org.bouncycastle.crypto.io.CipherOutputStream;
/** /**
* Class representing a connection from the proxy to the server; ie upstream. * Class representing a connection from the proxy to the server; ie upstream.
*/ */
public class ServerConnection extends GenericConnection { public class ServerConnection extends GenericConnection
{
public final String name; public final String name;
public final Packet1Login loginPacket; public final Packet1Login loginPacket;
public ServerConnection(String name, Socket socket, PacketInputStream in, OutputStream out, Packet1Login loginPacket) { public ServerConnection(String name, Socket socket, PacketInputStream in, OutputStream out, Packet1Login loginPacket)
{
super(socket, in, out); super(socket, in, out);
this.name = name; this.name = name;
this.loginPacket = loginPacket; this.loginPacket = loginPacket;
} }
public static ServerConnection connect(String name, InetSocketAddress address, Packet2Handshake handshake, boolean retry) { public static ServerConnection connect(String name, InetSocketAddress address, Packet2Handshake handshake, boolean retry)
try { {
try
{
Socket socket = new Socket(); Socket socket = new Socket();
socket.connect(address, BungeeCord.instance.config.timeout); socket.connect(address, BungeeCord.instance.config.timeout);
BungeeCord.instance.setSocketOptions(socket); BungeeCord.instance.setSocketOptions(socket);
@ -49,7 +53,8 @@ public class ServerConnection extends GenericConnection {
out.write(response.getPacket()); out.write(response.getPacket());
int ciphId = Util.getId(in.readPacket()); int ciphId = Util.getId(in.readPacket());
if (ciphId != 0xFC) { if (ciphId != 0xFC)
{
throw new RuntimeException("Server did not send encryption enable"); throw new RuntimeException("Server did not send encryption enable");
} }
@ -58,20 +63,25 @@ public class ServerConnection extends GenericConnection {
out.write(new PacketCDClientStatus((byte) 0).getPacket()); out.write(new PacketCDClientStatus((byte) 0).getPacket());
byte[] loginResponse = in.readPacket(); byte[] loginResponse = in.readPacket();
if (Util.getId(loginResponse) == 0xFF) { if (Util.getId(loginResponse) == 0xFF)
{
throw new KickException("[Kicked] " + new PacketFFKick(loginResponse).message); throw new KickException("[Kicked] " + new PacketFFKick(loginResponse).message);
} }
Packet1Login login = new Packet1Login(loginResponse); Packet1Login login = new Packet1Login(loginResponse);
out.write(new PacketFAPluginMessage("REGISTER", "RubberBand".getBytes()).getPacket()); out.write(new PacketFAPluginMessage("REGISTER", "RubberBand".getBytes()).getPacket());
return new ServerConnection(name, socket, in, out, login); return new ServerConnection(name, socket, in, out, login);
} catch (KickException ex) { } catch (KickException ex)
{
throw ex; throw ex;
} catch (Exception ex) { } catch (Exception ex)
{
InetSocketAddress def = BungeeCord.instance.config.getServer(null); InetSocketAddress def = BungeeCord.instance.config.getServer(null);
if (retry && !address.equals(def)) { if (retry && !address.equals(def))
{
return connect(name, def, handshake, false); return connect(name, def, handshake, false);
} else { } else
{
throw new RuntimeException("Could not connect to target server"); throw new RuntimeException("Could not connect to target server");
} }
} }

View File

@ -2,7 +2,6 @@ package net.md_5.bungee;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -19,7 +18,8 @@ import net.md_5.bungee.packet.Packet9Respawn;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketInputStream; import net.md_5.bungee.packet.PacketInputStream;
public class UserConnection extends GenericConnection implements CommandSender { public class UserConnection extends GenericConnection implements CommandSender
{
public final Packet2Handshake handshake; public final Packet2Handshake handshake;
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>(); public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
@ -32,39 +32,48 @@ public class UserConnection extends GenericConnection implements CommandSender {
private int serverEntityId; private int serverEntityId;
private volatile boolean reconnecting; private volatile boolean reconnecting;
public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake) { public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake)
{
super(socket, in, out); super(socket, in, out);
this.handshake = handshake; this.handshake = handshake;
username = handshake.username; username = handshake.username;
BungeeCord.instance.connections.put(username, this); BungeeCord.instance.connections.put(username, this);
} }
public void connect(String server) { public void connect(String server)
{
InetSocketAddress addr = BungeeCord.instance.config.getServer(server); InetSocketAddress addr = BungeeCord.instance.config.getServer(server);
connect(server, addr); connect(server, addr);
} }
private void connect(String name, InetSocketAddress serverAddr) { private void connect(String name, InetSocketAddress serverAddr)
try { {
try
{
reconnecting = true; reconnecting = true;
if (server != null) { if (server != null)
{
out.write(new Packet9Respawn((byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT").getPacket()); out.write(new Packet9Respawn((byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT").getPacket());
out.write(new Packet9Respawn((byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT").getPacket()); out.write(new Packet9Respawn((byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT").getPacket());
} }
ServerConnection newServer = ServerConnection.connect(name, serverAddr, handshake, server == null); ServerConnection newServer = ServerConnection.connect(name, serverAddr, handshake, server == null);
if (server == null) { if (server == null)
{
clientEntityId = newServer.loginPacket.entityId; clientEntityId = newServer.loginPacket.entityId;
serverEntityId = newServer.loginPacket.entityId; serverEntityId = newServer.loginPacket.entityId;
out.write(newServer.loginPacket.getPacket()); out.write(newServer.loginPacket.getPacket());
upBridge = new UpstreamBridge(); upBridge = new UpstreamBridge();
upBridge.start(); upBridge.start();
} else { } else
try { {
try
{
downBridge.interrupt(); downBridge.interrupt();
downBridge.join(); downBridge.join();
} catch (InterruptedException ie) { } catch (InterruptedException ie)
{
} }
server.disconnect("Quitting"); server.disconnect("Quitting");
@ -73,7 +82,8 @@ public class UserConnection extends GenericConnection implements CommandSender {
serverEntityId = login.entityId; serverEntityId = login.entityId;
out.write(new Packet9Respawn(login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType).getPacket()); out.write(new Packet9Respawn(login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType).getPacket());
out.write(new Packet46GameState((byte) 2, (byte) 0).getPacket()); out.write(new Packet46GameState((byte) 2, (byte) 0).getPacket());
if (heldItem != null) { if (heldItem != null)
{
newServer.out.write(heldItem.getPacket()); newServer.out.write(heldItem.getPacket());
} }
} }
@ -81,100 +91,128 @@ public class UserConnection extends GenericConnection implements CommandSender {
downBridge = new DownstreamBridge(); downBridge = new DownstreamBridge();
server = newServer; server = newServer;
downBridge.start(); downBridge.start();
} catch (KickException ex) { } catch (KickException ex)
{
destroySelf(ex.getMessage()); destroySelf(ex.getMessage());
} catch (Exception ex) { } catch (Exception ex)
{
destroySelf("Could not connect to server"); destroySelf("Could not connect to server");
} }
} }
public SocketAddress getAddress() { public SocketAddress getAddress()
{
return socket.getRemoteSocketAddress(); return socket.getRemoteSocketAddress();
} }
private void destroySelf(String reason) { private void destroySelf(String reason)
if (BungeeCord.instance.isRunning) { {
if (BungeeCord.instance.isRunning)
{
BungeeCord.instance.connections.remove(username); BungeeCord.instance.connections.remove(username);
} }
disconnect(reason); disconnect(reason);
if (server != null) { if (server != null)
{
server.disconnect("Quitting"); server.disconnect("Quitting");
BungeeCord.instance.config.setServer(this, server.name); BungeeCord.instance.config.setServer(this, server.name);
} }
} }
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message)
{
packetQueue.add(new Packet3Chat(message)); packetQueue.add(new Packet3Chat(message));
} }
@Override @Override
public String getName() { public String getName()
{
return username; return username;
} }
private class UpstreamBridge extends Thread { private class UpstreamBridge extends Thread
{
public UpstreamBridge() { public UpstreamBridge()
{
super("Upstream Bridge - " + username); super("Upstream Bridge - " + username);
} }
@Override @Override
public void run() { public void run()
while (!socket.isClosed()) { {
try { while (!socket.isClosed())
{
try
{
byte[] packet = in.readPacket(); byte[] packet = in.readPacket();
boolean sendPacket = true; boolean sendPacket = true;
int id = Util.getId(packet); int id = Util.getId(packet);
if (id == 0x03) { if (id == 0x03)
{
Packet3Chat chat = new Packet3Chat(packet); Packet3Chat chat = new Packet3Chat(packet);
String message = chat.message; String message = chat.message;
if (message.startsWith("/")) { if (message.startsWith("/"))
{
sendPacket = !BungeeCord.instance.dispatchCommand(message.substring(1), UserConnection.this); sendPacket = !BungeeCord.instance.dispatchCommand(message.substring(1), UserConnection.this);
} }
} else if (id == 0x10) { } else if (id == 0x10)
{
heldItem = new Packet10HeldItem(packet); heldItem = new Packet10HeldItem(packet);
} }
EntityMap.rewrite(packet, clientEntityId, serverEntityId); EntityMap.rewrite(packet, clientEntityId, serverEntityId);
if (sendPacket && !server.socket.isClosed()) { if (sendPacket && !server.socket.isClosed())
{
server.out.write(packet); server.out.write(packet);
} }
} catch (IOException ex) { } catch (IOException ex)
{
destroySelf("Reached end of stream"); destroySelf("Reached end of stream");
} catch (Exception ex) { } catch (Exception ex)
{
destroySelf(Util.exception(ex)); destroySelf(Util.exception(ex));
} }
} }
} }
} }
private class DownstreamBridge extends Thread { private class DownstreamBridge extends Thread
{
public DownstreamBridge() { public DownstreamBridge()
{
super("Downstream Bridge - " + username); super("Downstream Bridge - " + username);
} }
@Override @Override
public void run() { public void run()
try { {
while (!reconnecting) { try
{
while (!reconnecting)
{
byte[] packet = server.in.readPacket(); byte[] packet = server.in.readPacket();
int id = Util.getId(packet); int id = Util.getId(packet);
if (id == 0xFA) { if (id == 0xFA)
{
PacketFAPluginMessage message = new PacketFAPluginMessage(packet); PacketFAPluginMessage message = new PacketFAPluginMessage(packet);
if (message.tag.equals("RubberBand")) { if (message.tag.equals("RubberBand"))
{
String server = new String(message.data); String server = new String(message.data);
connect(server); connect(server);
break; break;
} }
} }
while (!packetQueue.isEmpty()) { while (!packetQueue.isEmpty())
{
DefinedPacket p = packetQueue.poll(); DefinedPacket p = packetQueue.poll();
if (p != null) { if (p != null)
{
out.write(p.getPacket()); out.write(p.getPacket());
} }
} }
@ -182,7 +220,8 @@ public class UserConnection extends GenericConnection implements CommandSender {
EntityMap.rewrite(packet, serverEntityId, clientEntityId); EntityMap.rewrite(packet, serverEntityId, clientEntityId);
out.write(packet); out.write(packet);
} }
} catch (Exception ex) { } catch (Exception ex)
{
destroySelf(Util.exception(ex)); destroySelf(Util.exception(ex));
} }
} }

View File

@ -5,7 +5,8 @@ import java.net.InetSocketAddress;
/** /**
* Series of utility classes to perform various operations. * Series of utility classes to perform various operations.
*/ */
public class Util { public class Util
{
private static final int DEFAULT_PORT = 25565; private static final int DEFAULT_PORT = 25565;
@ -15,10 +16,12 @@ public class Util {
* @param hostline in the format of 'host:port' * @param hostline in the format of 'host:port'
* @return the constructed hostname + port. * @return the constructed hostname + port.
*/ */
public static InetSocketAddress getAddr(String hostline) { public static InetSocketAddress getAddr(String hostline)
{
String[] split = hostline.split(":"); String[] split = hostline.split(":");
int port = DEFAULT_PORT; int port = DEFAULT_PORT;
if (split.length > 1) { if (split.length > 1)
{
port = Integer.parseInt(split[1]); port = Integer.parseInt(split[1]);
} }
return new InetSocketAddress(split[0], port); return new InetSocketAddress(split[0], port);
@ -31,7 +34,8 @@ public class Util {
* @param b the array to read from * @param b the array to read from
* @return the unsigned value of the first byte * @return the unsigned value of the first byte
*/ */
public static int getId(byte[] b) { public static int getId(byte[] b)
{
return b[0] & 0xFF; return b[0] & 0xFF;
} }
@ -42,10 +46,13 @@ public class Util {
* @param s the string to normalize * @param s the string to normalize
* @return the normalized path * @return the normalized path
*/ */
public static String normalize(String s) { public static String normalize(String s)
{
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
for (char c : s.toCharArray()) { for (char c : s.toCharArray())
if (Character.isUpperCase(c)) { {
if (Character.isUpperCase(c))
{
result.append("_"); result.append("_");
} }
result.append(Character.toLowerCase(c)); result.append(Character.toLowerCase(c));
@ -59,7 +66,8 @@ public class Util {
* @param i the integer to format * @param i the integer to format
* @return the hex representation of the integer * @return the hex representation of the integer
*/ */
public static String hex(int i) { public static String hex(int i)
{
return String.format("0x%02X", i); return String.format("0x%02X", i);
} }
@ -70,7 +78,8 @@ public class Util {
* @param t the {@link Throwable} to format. * @param t the {@link Throwable} to format.
* @return a string representing information about the {@link Throwable} * @return a string representing information about the {@link Throwable}
*/ */
public static String exception(Throwable t) { public static String exception(Throwable t)
{
return t.getClass().getSimpleName() + " : " + t.getMessage() + " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber(); return t.getClass().getSimpleName() + " : " + t.getMessage() + " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber();
} }
} }

View File

@ -7,7 +7,8 @@ import net.md_5.bungee.Permission;
* Class which represents a proxy command. The {@link #execute(net.md_5.bungee.command.CommandSender, java.lang.String[]) * Class which represents a proxy command. The {@link #execute(net.md_5.bungee.command.CommandSender, java.lang.String[])
* } method will be called to dispatch the command. * } method will be called to dispatch the command.
*/ */
public abstract class Command { public abstract class Command
{
/** /**
* Execute this command. * Execute this command.
@ -18,7 +19,8 @@ public abstract class Command {
*/ */
public abstract void execute(CommandSender sender, String[] args); public abstract void execute(CommandSender sender, String[] args);
public Permission getPermission(CommandSender sender) { public Permission getPermission(CommandSender sender)
{
return BungeeCord.instance.config.getPermission(sender); return BungeeCord.instance.config.getPermission(sender);
} }
} }

View File

@ -5,25 +5,32 @@ import net.md_5.bungee.ChatColor;
import net.md_5.bungee.Permission; import net.md_5.bungee.Permission;
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
public class CommandAlert extends Command { public class CommandAlert extends Command
{
@Override @Override
public void execute(CommandSender sender, String[] args) { public void execute(CommandSender sender, String[] args)
if (getPermission(sender) != Permission.ADMIN) { {
if (getPermission(sender) != Permission.ADMIN)
{
sender.sendMessage(ChatColor.RED + "You do not have permission to execute this command!"); sender.sendMessage(ChatColor.RED + "You do not have permission to execute this command!");
return; return;
} }
if (args.length == 0) { if (args.length == 0)
{
sender.sendMessage(ChatColor.RED + "Please follow this command by an announcement to make"); sender.sendMessage(ChatColor.RED + "Please follow this command by an announcement to make");
} else { } else
{
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(ChatColor.DARK_PURPLE); builder.append(ChatColor.DARK_PURPLE);
builder.append(" [Alert] "); builder.append(" [Alert] ");
for (String s : args) { for (String s : args)
{
builder.append(s); builder.append(s);
} }
String message = builder.toString(); String message = builder.toString();
for (UserConnection con : BungeeCord.instance.connections.values()) { for (UserConnection con : BungeeCord.instance.connections.values())
{
con.sendMessage(message.toString()); con.sendMessage(message.toString());
} }
} }

View File

@ -7,13 +7,17 @@ import net.md_5.bungee.Permission;
/** /**
* Command to terminate the proxy instance. May only be used by the console. * Command to terminate the proxy instance. May only be used by the console.
*/ */
public class CommandEnd extends Command { public class CommandEnd extends Command
{
@Override @Override
public void execute(CommandSender sender, String[] args) { public void execute(CommandSender sender, String[] args)
if (getPermission(sender) != Permission.ADMIN) { {
if (getPermission(sender) != Permission.ADMIN)
{
sender.sendMessage(ChatColor.RED + "You do not have permission to use this command"); sender.sendMessage(ChatColor.RED + "You do not have permission to use this command");
} else { } else
{
BungeeCord.instance.stop(); BungeeCord.instance.stop();
} }
} }

View File

@ -5,22 +5,28 @@ import net.md_5.bungee.ChatColor;
import net.md_5.bungee.Permission; import net.md_5.bungee.Permission;
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
public class CommandIP extends Command { public class CommandIP extends Command
{
@Override @Override
public void execute(CommandSender sender, String[] args) { public void execute(CommandSender sender, String[] args)
if (getPermission(sender) != Permission.ADMIN) { {
if (getPermission(sender) != Permission.ADMIN)
{
sender.sendMessage(ChatColor.RED + "You do not have permission to use this command"); sender.sendMessage(ChatColor.RED + "You do not have permission to use this command");
return; return;
} }
if (args.length < 1) { if (args.length < 1)
{
sender.sendMessage(ChatColor.RED + "Please follow this command by a user name"); sender.sendMessage(ChatColor.RED + "Please follow this command by a user name");
return; return;
} }
UserConnection user = BungeeCord.instance.connections.get(args[0]); UserConnection user = BungeeCord.instance.connections.get(args[0]);
if (user == null) { if (user == null)
{
sender.sendMessage(ChatColor.RED + "That user is not online"); sender.sendMessage(ChatColor.RED + "That user is not online");
} else { } else
{
sender.sendMessage(ChatColor.BLUE + "IP of " + args[0] + " is " + user.getAddress()); sender.sendMessage(ChatColor.BLUE + "IP of " + args[0] + " is " + user.getAddress());
} }
} }

View File

@ -8,15 +8,19 @@ import net.md_5.bungee.UserConnection;
/** /**
* Command to list all players connected to the proxy. * Command to list all players connected to the proxy.
*/ */
public class CommandList extends Command { public class CommandList extends Command
{
@Override @Override
public void execute(CommandSender sender, String[] args) { public void execute(CommandSender sender, String[] args)
{
StringBuilder users = new StringBuilder(); StringBuilder users = new StringBuilder();
Collection<UserConnection> connections = BungeeCord.instance.connections.values(); Collection<UserConnection> connections = BungeeCord.instance.connections.values();
for (UserConnection con : connections) { for (UserConnection con : connections)
switch (getPermission(con)) { {
switch (getPermission(con))
{
case ADMIN: case ADMIN:
users.append(ChatColor.RED); users.append(ChatColor.RED);
break; break;

View File

@ -1,6 +1,7 @@
package net.md_5.bungee.command; package net.md_5.bungee.command;
public interface CommandSender { public interface CommandSender
{
/** /**
* Sends a message to the client at the earliest available opportunity. * Sends a message to the client at the earliest available opportunity.

View File

@ -8,28 +8,36 @@ import net.md_5.bungee.UserConnection;
/** /**
* Command to list and switch a player between available servers. * Command to list and switch a player between available servers.
*/ */
public class CommandServer extends Command { public class CommandServer extends Command
{
@Override @Override
public void execute(CommandSender sender, String[] args) { public void execute(CommandSender sender, String[] args)
if (!(sender instanceof UserConnection)) { {
if (!(sender instanceof UserConnection))
{
return; return;
} }
UserConnection con = (UserConnection) sender; UserConnection con = (UserConnection) sender;
Collection<String> servers = BungeeCord.instance.config.servers.keySet(); Collection<String> servers = BungeeCord.instance.config.servers.keySet();
if (args.length <= 0) { if (args.length <= 0)
{
StringBuilder serverList = new StringBuilder(); StringBuilder serverList = new StringBuilder();
for (String server : servers) { for (String server : servers)
{
serverList.append(server); serverList.append(server);
serverList.append(", "); serverList.append(", ");
} }
serverList.setLength(serverList.length() - 2); serverList.setLength(serverList.length() - 2);
con.sendMessage(ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString()); con.sendMessage(ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString());
} else { } else
{
String server = args[0]; String server = args[0];
if (!servers.contains(server)) { if (!servers.contains(server))
{
con.sendMessage(ChatColor.RED + "The specified server does not exist"); con.sendMessage(ChatColor.RED + "The specified server does not exist");
} else { } else
{
con.connect(server); con.connect(server);
} }
} }

View File

@ -5,17 +5,20 @@ import net.md_5.bungee.ChatColor;
/** /**
* Command sender representing the proxy console. * Command sender representing the proxy console.
*/ */
public class ConsoleCommandSender implements CommandSender { public class ConsoleCommandSender implements CommandSender
{
public static final ConsoleCommandSender instance = new ConsoleCommandSender(); public static final ConsoleCommandSender instance = new ConsoleCommandSender();
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message)
{
System.out.println(ChatColor.stripColor(message)); System.out.println(ChatColor.stripColor(message));
} }
@Override @Override
public String getName() { public String getName()
{
return "CONSOLE"; return "CONSOLE";
} }
} }

View File

@ -13,9 +13,11 @@ import net.md_5.bungee.Util;
* subclasses can read and write to the backing byte array which can be * subclasses can read and write to the backing byte array which can be
* retrieved via the {@link #getPacket()} method. * retrieved via the {@link #getPacket()} method.
*/ */
public abstract class DefinedPacket implements DataInput, DataOutput { public abstract class DefinedPacket implements DataInput, DataOutput
{
private interface Overriden { private interface Overriden
{
void readUTF(); void readUTF();
@ -34,16 +36,19 @@ public abstract class DefinedPacket implements DataInput, DataOutput {
*/ */
private byte[] packet; private byte[] packet;
public DefinedPacket(int id, byte[] buf) { public DefinedPacket(int id, byte[] buf)
{
in = ByteStreams.newDataInput(buf); in = ByteStreams.newDataInput(buf);
if (readUnsignedByte() != id) { if (readUnsignedByte() != id)
{
throw new IllegalArgumentException("Wasn't expecting packet id " + Util.hex(id)); throw new IllegalArgumentException("Wasn't expecting packet id " + Util.hex(id));
} }
this.id = id; this.id = id;
packet = buf; packet = buf;
} }
public DefinedPacket(int id) { public DefinedPacket(int id)
{
out = ByteStreams.newDataOutput(); out = ByteStreams.newDataOutput();
this.id = id; this.id = id;
writeByte(id); writeByte(id);
@ -55,32 +60,38 @@ public abstract class DefinedPacket implements DataInput, DataOutput {
* @return the bytes which make up this packet, either the original byte * @return the bytes which make up this packet, either the original byte
* array or the newly written one. * array or the newly written one.
*/ */
public byte[] getPacket() { public byte[] getPacket()
{
return packet == null ? out.toByteArray() : packet; return packet == null ? out.toByteArray() : packet;
} }
@Override @Override
public void writeUTF(String s) { public void writeUTF(String s)
{
writeShort(s.length()); writeShort(s.length());
writeChars(s); writeChars(s);
} }
@Override @Override
public String readUTF() { public String readUTF()
{
short len = readShort(); short len = readShort();
char[] chars = new char[len]; char[] chars = new char[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++)
{
chars[i] = this.readChar(); chars[i] = this.readChar();
} }
return new String(chars); return new String(chars);
} }
public void writeArray(byte[] b) { public void writeArray(byte[] b)
{
writeShort(b.length); writeShort(b.length);
write(b); write(b);
} }
public byte[] readArray() { public byte[] readArray()
{
short len = readShort(); short len = readShort();
byte[] ret = new byte[len]; byte[] ret = new byte[len];
readFully(ret); readFully(ret);

View File

@ -5,16 +5,19 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet10HeldItem extends DefinedPacket { public class Packet10HeldItem extends DefinedPacket
{
public short slot; public short slot;
public Packet10HeldItem(short slot) { public Packet10HeldItem(short slot)
{
super(0x10); super(0x10);
writeShort(slot); writeShort(slot);
} }
public Packet10HeldItem(byte[] buf) { public Packet10HeldItem(byte[] buf)
{
super(0x10, buf); super(0x10, buf);
this.slot = readShort(); this.slot = readShort();
} }

View File

@ -5,7 +5,8 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet1Login extends DefinedPacket { public class Packet1Login extends DefinedPacket
{
public int entityId; public int entityId;
public String levelType; public String levelType;
@ -15,7 +16,8 @@ public class Packet1Login extends DefinedPacket {
public byte unused; public byte unused;
public byte maxPlayers; public byte maxPlayers;
public Packet1Login(int entityId, String levelType, byte gameMode, byte dimension, byte difficulty, byte unused, byte maxPlayers) { public Packet1Login(int entityId, String levelType, byte gameMode, byte dimension, byte difficulty, byte unused, byte maxPlayers)
{
super(0x01); super(0x01);
writeInt(entityId); writeInt(entityId);
writeUTF(levelType); writeUTF(levelType);
@ -26,7 +28,8 @@ public class Packet1Login extends DefinedPacket {
writeByte(maxPlayers); writeByte(maxPlayers);
} }
public Packet1Login(byte[] buf) { public Packet1Login(byte[] buf)
{
super(0x01, buf); super(0x01, buf);
this.entityId = readInt(); this.entityId = readInt();
this.levelType = readUTF(); this.levelType = readUTF();

View File

@ -5,14 +5,16 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet2Handshake extends DefinedPacket { public class Packet2Handshake extends DefinedPacket
{
public byte procolVersion; public byte procolVersion;
public String username; public String username;
public String host; public String host;
public int port; public int port;
public Packet2Handshake(byte protocolVersion, String username, String host, int port) { public Packet2Handshake(byte protocolVersion, String username, String host, int port)
{
super(0x02); super(0x02);
writeByte(protocolVersion); writeByte(protocolVersion);
writeUTF(username); writeUTF(username);
@ -20,7 +22,8 @@ public class Packet2Handshake extends DefinedPacket {
writeInt(port); writeInt(port);
} }
public Packet2Handshake(byte[] buf) { public Packet2Handshake(byte[] buf)
{
super(0x02, buf); super(0x02, buf);
this.procolVersion = readByte(); this.procolVersion = readByte();
this.username = readUTF(); this.username = readUTF();

View File

@ -5,16 +5,19 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet3Chat extends DefinedPacket { public class Packet3Chat extends DefinedPacket
{
public String message; public String message;
public Packet3Chat(String message) { public Packet3Chat(String message)
{
super(0x03); super(0x03);
writeUTF(message); writeUTF(message);
} }
public Packet3Chat(byte[] buf) { public Packet3Chat(byte[] buf)
{
super(0x03, buf); super(0x03, buf);
this.message = readUTF(); this.message = readUTF();
} }

View File

@ -5,7 +5,8 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet46GameState extends DefinedPacket { public class Packet46GameState extends DefinedPacket
{
/** /**
* Update game state. When sent with a state of 2, rain will cease. * Update game state. When sent with a state of 2, rain will cease.
@ -13,7 +14,8 @@ public class Packet46GameState extends DefinedPacket {
* @param state the new game state * @param state the new game state
* @param mode the new game mode. Used when state == 3. * @param mode the new game mode. Used when state == 3.
*/ */
public Packet46GameState(byte state, byte mode) { public Packet46GameState(byte state, byte mode)
{
super(0x46); super(0x46);
writeByte(state); writeByte(state);
writeByte(mode); writeByte(mode);

View File

@ -5,7 +5,8 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class Packet9Respawn extends DefinedPacket { public class Packet9Respawn extends DefinedPacket
{
public int dimension; public int dimension;
public byte difficulty; public byte difficulty;
@ -13,7 +14,8 @@ public class Packet9Respawn extends DefinedPacket {
public short worldHeight; public short worldHeight;
public String levelType; public String levelType;
public Packet9Respawn(int dimension, byte difficulty, byte gameMode, short worldHeight, String levelType) { public Packet9Respawn(int dimension, byte difficulty, byte gameMode, short worldHeight, String levelType)
{
super(0x09); super(0x09);
writeInt(dimension); writeInt(dimension);
writeByte(difficulty); writeByte(difficulty);
@ -22,7 +24,8 @@ public class Packet9Respawn extends DefinedPacket {
writeUTF(levelType); writeUTF(levelType);
} }
public Packet9Respawn(byte[] buf) { public Packet9Respawn(byte[] buf)
{
super(0x09, buf); super(0x09, buf);
this.dimension = readInt(); this.dimension = readInt();
this.difficulty = readByte(); this.difficulty = readByte();

View File

@ -5,14 +5,16 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PacketCDClientStatus extends DefinedPacket { public class PacketCDClientStatus extends DefinedPacket
{
/** /**
* Sent from the client to the server upon respawn, * Sent from the client to the server upon respawn,
* *
* @param payload 0 if initial spawn, 1 if respawn after death. * @param payload 0 if initial spawn, 1 if respawn after death.
*/ */
public PacketCDClientStatus(byte payload) { public PacketCDClientStatus(byte payload)
{
super(0xCD); super(0xCD);
writeByte(payload); writeByte(payload);
} }

View File

@ -5,12 +5,14 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PacketFAPluginMessage extends DefinedPacket { public class PacketFAPluginMessage extends DefinedPacket
{
public String tag; public String tag;
public byte[] data; public byte[] data;
public PacketFAPluginMessage(String tag, byte[] data) { public PacketFAPluginMessage(String tag, byte[] data)
{
super(0xFA); super(0xFA);
writeUTF(tag); writeUTF(tag);
writeArray(data); writeArray(data);
@ -18,7 +20,8 @@ public class PacketFAPluginMessage extends DefinedPacket {
this.data = data; this.data = data;
} }
public PacketFAPluginMessage(byte[] buf) { public PacketFAPluginMessage(byte[] buf)
{
super(0xFA, buf); super(0xFA, buf);
this.tag = readUTF(); this.tag = readUTF();
this.data = readArray(); this.data = readArray();

View File

@ -5,24 +5,28 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PacketFCEncryptionResponse extends DefinedPacket { public class PacketFCEncryptionResponse extends DefinedPacket
{
public byte[] sharedSecret; public byte[] sharedSecret;
public byte[] verifyToken; public byte[] verifyToken;
public PacketFCEncryptionResponse() { public PacketFCEncryptionResponse()
{
super(0xFC); super(0xFC);
writeArray(new byte[0]); writeArray(new byte[0]);
writeArray(new byte[0]); writeArray(new byte[0]);
} }
public PacketFCEncryptionResponse(byte[] sharedSecret, byte[] verifyToken) { public PacketFCEncryptionResponse(byte[] sharedSecret, byte[] verifyToken)
{
super(0xFC); super(0xFC);
writeArray(sharedSecret); writeArray(sharedSecret);
writeArray(verifyToken); writeArray(verifyToken);
} }
public PacketFCEncryptionResponse(byte[] buf) { public PacketFCEncryptionResponse(byte[] buf)
{
super(0xFC, buf); super(0xFC, buf);
this.sharedSecret = readArray(); this.sharedSecret = readArray();
this.verifyToken = readArray(); this.verifyToken = readArray();

View File

@ -5,13 +5,15 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PacketFDEncryptionRequest extends DefinedPacket { public class PacketFDEncryptionRequest extends DefinedPacket
{
public String serverId; public String serverId;
public byte[] publicKey; public byte[] publicKey;
public byte[] verifyToken; public byte[] verifyToken;
public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken) { public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken)
{
super(0xFD); super(0xFD);
writeUTF(serverId); writeUTF(serverId);
writeArray(publicKey); writeArray(publicKey);
@ -21,7 +23,8 @@ public class PacketFDEncryptionRequest extends DefinedPacket {
this.verifyToken = verifyToken; this.verifyToken = verifyToken;
} }
public PacketFDEncryptionRequest(byte[] buf) { public PacketFDEncryptionRequest(byte[] buf)
{
super(0xFD, buf); super(0xFD, buf);
serverId = readUTF(); serverId = readUTF();
publicKey = readArray(); publicKey = readArray();

View File

@ -5,16 +5,19 @@ import lombok.ToString;
@ToString @ToString
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PacketFFKick extends DefinedPacket { public class PacketFFKick extends DefinedPacket
{
public String message; public String message;
public PacketFFKick(String message) { public PacketFFKick(String message)
{
super(0xFF); super(0xFF);
writeUTF(message); writeUTF(message);
} }
public PacketFFKick(byte[] buf) { public PacketFFKick(byte[] buf)
{
super(0xFF, buf); super(0xFF, buf);
this.message = readUTF(); this.message = readUTF();
} }

View File

@ -12,12 +12,14 @@ import net.minecraft.server.Packet;
* A specialized input stream to parse packets using the Mojang packet * A specialized input stream to parse packets using the Mojang packet
* definitions and then return them as a byte array. * definitions and then return them as a byte array.
*/ */
public class PacketInputStream { public class PacketInputStream
{
private final DataInputStream dataInput; private final DataInputStream dataInput;
private final TrackingInputStream tracker; private final TrackingInputStream tracker;
public PacketInputStream(InputStream in) { public PacketInputStream(InputStream in)
{
tracker = new TrackingInputStream(in); tracker = new TrackingInputStream(in);
dataInput = new DataInputStream(tracker); dataInput = new DataInputStream(tracker);
} }
@ -28,14 +30,17 @@ public class PacketInputStream {
* @return the read packet * @return the read packet
* @throws IOException when the underlying input stream throws an exception * @throws IOException when the underlying input stream throws an exception
*/ */
public byte[] readPacket() throws IOException { public byte[] readPacket() throws IOException
{
tracker.out.reset(); tracker.out.reset();
int id = tracker.read(); int id = tracker.read();
if (id == -1) { if (id == -1)
{
throw new EOFException(); throw new EOFException();
} }
Packet codec = VanillaPackets.packets[id]; Packet codec = VanillaPackets.packets[id];
if (codec == null) { if (codec == null)
{
throw new RuntimeException("No Packet id: " + Util.hex(id)); throw new RuntimeException("No Packet id: " + Util.hex(id));
} }
codec.a(dataInput); codec.a(dataInput);
@ -47,17 +52,20 @@ public class PacketInputStream {
* Input stream which will wrap another stream and copy all bytes read to a * Input stream which will wrap another stream and copy all bytes read to a
* {@link ByteArrayOutputStream}. * {@link ByteArrayOutputStream}.
*/ */
private class TrackingInputStream extends InputStream { private class TrackingInputStream extends InputStream
{
private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final ByteArrayOutputStream out = new ByteArrayOutputStream();
private final InputStream wrapped; private final InputStream wrapped;
public TrackingInputStream(InputStream wrapped) { public TrackingInputStream(InputStream wrapped)
{
this.wrapped = wrapped; this.wrapped = wrapped;
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException
{
int ret = wrapped.read(); int ret = wrapped.read();
out.write(ret); out.write(ret);
return ret; return ret;

View File

@ -83,7 +83,8 @@ import net.minecraft.server.Packet9Respawn;
/** /**
* Class containing instances of all Vanilla Minecraft packets. * Class containing instances of all Vanilla Minecraft packets.
*/ */
public class VanillaPackets { public class VanillaPackets
{
/** /**
* Array of packet instances ordered by packet id. * Array of packet instances ordered by packet id.
@ -96,15 +97,19 @@ public class VanillaPackets {
* @param id of the packet to add * @param id of the packet to add
* @param clazz class of the packet to add * @param clazz class of the packet to add
*/ */
private static void map(int id, Class<? extends Packet> clazz) { private static void map(int id, Class<? extends Packet> clazz)
try { {
try
{
packets[id] = clazz.getDeclaredConstructor().newInstance(); packets[id] = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException ex) { } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException ex)
{
$().severe("Could not register packet id " + Util.hex(id)); $().severe("Could not register packet id " + Util.hex(id));
} }
} }
static { static
{
map(0, Packet0KeepAlive.class); map(0, Packet0KeepAlive.class);
map(1, Packet1Login.class); map(1, Packet1Login.class);
map(2, Packet2Handshake.class); map(2, Packet2Handshake.class);

View File

@ -3,7 +3,8 @@ package net.md_5.bungee.plugin;
/** /**
* An event which may be canceled and this be prevented from happening. * An event which may be canceled and this be prevented from happening.
*/ */
public interface Cancellable { public interface Cancellable
{
/** /**
* Sets the canceled state of this event. * Sets the canceled state of this event.

View File

@ -9,7 +9,8 @@ import lombok.Data;
* such as logging at the username has not yet been verified with Mojang. * such as logging at the username has not yet been verified with Mojang.
*/ */
@Data @Data
public class HandshakeEvent implements Cancellable { public class HandshakeEvent implements Cancellable
{
/** /**
* Canceled state. * Canceled state.

View File

@ -3,15 +3,18 @@ package net.md_5.bungee.plugin;
/** /**
* Exception thrown when a plugin could not be loaded for any reason. * Exception thrown when a plugin could not be loaded for any reason.
*/ */
public class InvalidPluginException extends RuntimeException { public class InvalidPluginException extends RuntimeException
{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public InvalidPluginException(String message, Throwable cause) { public InvalidPluginException(String message, Throwable cause)
{
super(message, cause); super(message, cause);
} }
public InvalidPluginException(String message) { public InvalidPluginException(String message)
{
super(message); super(message);
} }
} }

View File

@ -6,7 +6,8 @@ import net.md_5.bungee.command.Command;
/** /**
* Base class which all proxy plugins should extend. * Base class which all proxy plugins should extend.
*/ */
public abstract class JavaPlugin { public abstract class JavaPlugin
{
/** /**
* Description file. * Description file.
@ -16,26 +17,30 @@ public abstract class JavaPlugin {
/** /**
* Called on enable. * Called on enable.
*/ */
public void onEnable() { public void onEnable()
{
} }
/** /**
* Called on disable. * Called on disable.
*/ */
public void onDisable() { public void onDisable()
{
} }
/** /**
* Called when a user connects with their name and address. To keep things * Called when a user connects with their name and address. To keep things
* simple this name has not been checked with minecraft.net. * simple this name has not been checked with minecraft.net.
*/ */
public void onHandshake(HandshakeEvent event) { public void onHandshake(HandshakeEvent event)
{
} }
/** /**
* Register a command for use with the proxy. * Register a command for use with the proxy.
*/ */
protected final void registerCommand(String label, Command command) { protected final void registerCommand(String label, Command command)
{
BungeeCord.instance.commandMap.put(label, command); BungeeCord.instance.commandMap.put(label, command);
} }
} }

View File

@ -16,7 +16,8 @@ import static net.md_5.bungee.Logger.$;
* Plugin manager to handle loading and saving other JavaPlugin's. This class is * Plugin manager to handle loading and saving other JavaPlugin's. This class is
* itself a plugin for ease of use. * itself a plugin for ease of use.
*/ */
public class JavaPluginManager extends JavaPlugin { public class JavaPluginManager extends JavaPlugin
{
/** /**
* Set of loaded plugins. * Set of loaded plugins.
@ -28,23 +29,31 @@ public class JavaPluginManager extends JavaPlugin {
* Load all plugins from the plugins folder. This method must only be called * Load all plugins from the plugins folder. This method must only be called
* once per instance. * once per instance.
*/ */
public void loadPlugins() { public void loadPlugins()
{
File dir = new File("plugins"); File dir = new File("plugins");
dir.mkdir(); dir.mkdir();
for (File file : dir.listFiles(new PatternFilenameFilter(".*\\.jar"))) { for (File file : dir.listFiles(new PatternFilenameFilter(".*\\.jar")))
try { {
try
{
JarFile jar = new JarFile(file); JarFile jar = new JarFile(file);
ZipEntry entry = jar.getEntry("plugin.yml"); ZipEntry entry = jar.getEntry("plugin.yml");
if (entry == null) { if (entry == null)
{
throw new InvalidPluginException("Jar does not contain a plugin.yml"); throw new InvalidPluginException("Jar does not contain a plugin.yml");
} }
PluginDescription description; PluginDescription description;
try (InputStream is = jar.getInputStream(entry)) { try (InputStream is = jar.getInputStream(entry))
{
description = PluginDescription.load(is); description = PluginDescription.load(is);
} }
URLClassLoader classloader = new URLClassLoader(new URL[]{file.toURI().toURL()}, getClass().getClassLoader()); URLClassLoader classloader = new URLClassLoader(new URL[]
{
file.toURI().toURL()
}, getClass().getClassLoader());
Class<?> clazz = Class.forName(description.getMain(), true, classloader); Class<?> clazz = Class.forName(description.getMain(), true, classloader);
Class<? extends JavaPlugin> subClazz = clazz.asSubclass(JavaPlugin.class); Class<? extends JavaPlugin> subClazz = clazz.asSubclass(JavaPlugin.class);
JavaPlugin plugin = subClazz.getDeclaredConstructor().newInstance(); JavaPlugin plugin = subClazz.getDeclaredConstructor().newInstance();
@ -54,7 +63,8 @@ public class JavaPluginManager extends JavaPlugin {
plugins.add(plugin); plugins.add(plugin);
$().info("Loaded plugin: " + plugin.description.getName()); $().info("Loaded plugin: " + plugin.description.getName());
} catch (Exception ex) { } catch (Exception ex)
{
$().severe("Could not load plugin: " + file); $().severe("Could not load plugin: " + file);
ex.printStackTrace(); ex.printStackTrace();
} }
@ -62,15 +72,19 @@ public class JavaPluginManager extends JavaPlugin {
} }
@Override @Override
public void onDisable() { public void onDisable()
for (JavaPlugin p : plugins) { {
for (JavaPlugin p : plugins)
{
p.onDisable(); p.onDisable();
} }
} }
@Override @Override
public void onHandshake(HandshakeEvent event) { public void onHandshake(HandshakeEvent event)
for (JavaPlugin p : plugins) { {
for (JavaPlugin p : plugins)
{
p.onHandshake(event); p.onHandshake(event);
} }
} }

View File

@ -10,28 +10,36 @@ import org.yaml.snakeyaml.Yaml;
* it. * it.
*/ */
@Data @Data
public class PluginDescription { public class PluginDescription
{
private String name; private String name;
private String main; private String main;
private String version; private String version;
private String author; private String author;
private PluginDescription() { private PluginDescription()
{
} }
public static PluginDescription load(InputStream is) { public static PluginDescription load(InputStream is)
{
PluginDescription ret = new Yaml().loadAs(is, PluginDescription.class); PluginDescription ret = new Yaml().loadAs(is, PluginDescription.class);
if (ret == null) { if (ret == null)
{
throw new InvalidPluginException("Could not load plugin description file."); throw new InvalidPluginException("Could not load plugin description file.");
} }
for (Field f : PluginDescription.class.getDeclaredFields()) { for (Field f : PluginDescription.class.getDeclaredFields())
try { {
if (f.get(ret) == null) { try
{
if (f.get(ret) == null)
{
throw new InvalidPluginException(f.getName() + " is not set properly in plugin description"); throw new InvalidPluginException(f.getName() + " is not set properly in plugin description");
} }
} catch (IllegalArgumentException | IllegalAccessException ex) { } catch (IllegalArgumentException | IllegalAccessException ex)
{
} }
} }

View File

@ -4,21 +4,26 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
public abstract class Packet { public abstract class Packet
{
public boolean lowPriority = false; public boolean lowPriority = false;
public static void a(DataOutputStream dataoutputstream, byte[] abyte) throws IOException { public static void a(DataOutputStream dataoutputstream, byte[] abyte) throws IOException
{
dataoutputstream.writeShort(abyte.length); dataoutputstream.writeShort(abyte.length);
dataoutputstream.write(abyte); dataoutputstream.write(abyte);
} }
public static byte[] b(DataInputStream datainputstream) throws IOException { public static byte[] b(DataInputStream datainputstream) throws IOException
{
short short1 = datainputstream.readShort(); short short1 = datainputstream.readShort();
if (short1 < 0) { if (short1 < 0)
{
throw new IOException("Key was smaller than nothing! Weird key!"); throw new IOException("Key was smaller than nothing! Weird key!");
} else { } else
{
byte[] abyte = new byte[short1]; byte[] abyte = new byte[short1];
datainputstream.read(abyte); datainputstream.read(abyte);
@ -26,15 +31,19 @@ public abstract class Packet {
} }
} }
public static String a(DataInputStream datainputstream, int i) throws IOException { public static String a(DataInputStream datainputstream, int i) throws IOException
{
short short1 = datainputstream.readShort(); short short1 = datainputstream.readShort();
if (short1 < 0) { if (short1 < 0)
{
throw new IOException("Received string length is less than zero! Weird string!"); throw new IOException("Received string length is less than zero! Weird string!");
} else { } else
{
StringBuilder stringbuilder = new StringBuilder(); StringBuilder stringbuilder = new StringBuilder();
for (int j = 0; j < short1; ++j) { for (int j = 0; j < short1; ++j)
{
stringbuilder.append(datainputstream.readChar()); stringbuilder.append(datainputstream.readChar());
} }
@ -50,11 +59,13 @@ public abstract class Packet {
public abstract int a(); public abstract int a();
public static ItemStack c(DataInputStream datainputstream) throws IOException { public static ItemStack c(DataInputStream datainputstream) throws IOException
{
ItemStack itemstack = null; ItemStack itemstack = null;
short short1 = datainputstream.readShort(); short short1 = datainputstream.readShort();
if (short1 >= 0) { if (short1 >= 0)
{
byte b0 = datainputstream.readByte(); byte b0 = datainputstream.readByte();
short short2 = datainputstream.readShort(); short short2 = datainputstream.readShort();
@ -65,12 +76,15 @@ public abstract class Packet {
return itemstack; return itemstack;
} }
public static NBTTagCompound d(DataInputStream datainputstream) throws IOException { public static NBTTagCompound d(DataInputStream datainputstream) throws IOException
{
short short1 = datainputstream.readShort(); short short1 = datainputstream.readShort();
if (short1 < 0) { if (short1 < 0)
{
return null; return null;
} else { } else
{
byte[] abyte = new byte[short1]; byte[] abyte = new byte[short1];
datainputstream.readFully(abyte); datainputstream.readFully(abyte);

View File

@ -4,10 +4,12 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
public class Packet51MapChunk extends Packet { public class Packet51MapChunk extends Packet
{
@Override @Override
public void a(DataInputStream datainputstream) throws IOException { public void a(DataInputStream datainputstream) throws IOException
{
datainputstream.readInt(); datainputstream.readInt();
datainputstream.readInt(); datainputstream.readInt();
datainputstream.readBoolean(); datainputstream.readBoolean();
@ -19,15 +21,18 @@ public class Packet51MapChunk extends Packet {
} }
@Override @Override
public void a(DataOutputStream dataoutputstream) throws IOException { public void a(DataOutputStream dataoutputstream) throws IOException
{
} }
@Override @Override
public void handle(NetHandler nethandler) { public void handle(NetHandler nethandler)
{
} }
@Override @Override
public int a() { public int a()
{
return 0; return 0;
} }
} }