Initial commit.

This commit is contained in:
md_5 2012-10-05 11:01:31 +10:00
commit b876fb2e1b
23 changed files with 1495 additions and 0 deletions

38
.gitignore vendored Normal file
View File

@ -0,0 +1,38 @@
# Eclipse stuff
/.classpath
/.project
/.settings
# netbeans
/nbproject
/nbactions.xml
/nb-configuration.xml
# we use maven!
/build.xml
# maven
/target
/dependency-reduced-pom.xml
# vim
.*.sw[a-p]
# various other potential build files
/build
/bin
/dist
/manifest.mf
# Mac filesystem dust
/.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# other files
*log*
*.yml

26
LICENSE Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2012, md_5. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

13
README.md Normal file
View File

@ -0,0 +1,13 @@
BungeeCord
======
The most reliable Minecraft server portal suite.
Coding guidelines
-----------
Its not fun to always have to stick to a set of rules, but here are are few to make sure everyone gets the best experience.
* Have verbose commit messages, this makes it much easier to revert commits if the need arises.
* Rebase your commits onto the tracked branch. No merges please.
* Use for spaces, NO tabs.
* Use lombok wherever possible.

159
pom.xml Normal file
View File

@ -0,0 +1,159 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord</name>
<url>https://github.com/Shadowraze/BungeeCord</url>
<inceptionYear>2012</inceptionYear>
<licenses>
<license>
<name>The BSD 3-Clause License</name>
<url>http://opensource.org/licenses/BSD-3-Clause</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<connection>scm:git:git@github.com:Shadowraze/BungeeCord.git</connection>
<developerConnection>scm:git:git@github.com:Shadowraze/BungeeCord.git</developerConnection>
<url>git@github.com:Shadowraze/BungeeCord.git</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/Shadowraze/BungeeCord/issues</url>
</issueManagement>
<ciManagement>
<system>jenkins</system>
<url>http://ci.md-5.net/job/BungeeCord</url>
</ciManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>unknown</build.number>
<main.class>net.md_5.bungee.BungeeCord</main.class>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>13.0.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>minecraft-server</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>0.11.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repobo-snap</id>
<url>http://repo.bukkit.org/content/groups/public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>bukkit-plugins</id>
<url>http://repo.bukkit.org/content/groups/public</url>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>com.lukegb.mojo</groupId>
<artifactId>gitdescribe-maven-plugin</artifactId>
<version>1.3</version>
<configuration>
<outputPrefix>git-${project.name}-${project.version}-</outputPrefix>
<outputPostfix>-${build.number}</outputPostfix>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>gitdescribe</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>${main.class}</Main-Class>
<Implementation-Version>${describe}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
</excludes>
</filter>
<filter>
<artifact>org.bukkit:minecraft-server</artifact>
<excludes>
<exclude>org/bouncycastle/**</exclude>
</excludes>
</filter>
</filters>
<minimizeJar>true</minimizeJar>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,108 @@
package net.md_5.bungee;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static net.md_5.bungee.Logger.$;
public class BungeeCord {
/**
* Current software instance.
*/
public static BungeeCord instance;
/**
* Current operation state.
*/
public volatile boolean isRunning;
/**
* Configuration.
*/
public final Configuration config = new Configuration();
/**
* Thread pool.
*/
public final ExecutorService threadPool = Executors.newCachedThreadPool();
/**
* locations.yml save thread.
*/
private final ReconnectSaveThread saveThread = new ReconnectSaveThread();
/**
* Server socket listener.
*/
private ListenThread listener;
/**
* Current version.
*/
private String version = (getClass().getPackage().getImplementationVersion() == null) ? "unknown" : getClass().getPackage().getImplementationVersion();
public static void main(String[] args) throws IOException {
System.out.println(Util.hex(15));
instance = new BungeeCord();
$().info("Enabled BungeeCord version " + instance.version);
instance.start();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (instance.isRunning) {
String line = br.readLine();
if (line != null) {
if (line.equals("end")) {
instance.stop();
}
}
}
}
public void start() throws IOException {
config.load();
isRunning = true;
InetSocketAddress addr = Util.getAddr(config.bindHost);
listener = new ListenThread(addr);
listener.start();
saveThread.start();
$().info("Listening on " + addr);
}
public void stop() {
this.isRunning = false;
$().info("Closing listen thread");
try {
listener.socket.close();
listener.join();
} catch (InterruptedException | IOException ex) {
$().severe("Could not close listen thread");
}
$().info("Closing pending connections");
threadPool.shutdown();
$().info("Disconnecting " + "x" + " connections");
// TODO: Kick everyone
$().info("Saving reconnect locations");
saveThread.interrupt();
try {
saveThread.join();
} catch (InterruptedException ex) {
}
$().info("Thank you and goodbye");
}
public int getOnlinePlayers() {
return 123;
}
public void setSocketOptions(Socket socket) throws IOException {
socket.setSoTimeout(config.timeout);
socket.setTrafficClass(0x18);
socket.setTcpNoDelay(true);
}
}

View File

@ -0,0 +1,236 @@
package net.md_5.bungee;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.regex.Pattern;
/**
* All supported color values for chat
*/
public enum ChatColor {
/**
* Represents black
*/
BLACK('0', 0x00),
/**
* Represents dark blue
*/
DARK_BLUE('1', 0x1),
/**
* Represents dark green
*/
DARK_GREEN('2', 0x2),
/**
* Represents dark blue (aqua)
*/
DARK_AQUA('3', 0x3),
/**
* Represents dark red
*/
DARK_RED('4', 0x4),
/**
* Represents dark purple
*/
DARK_PURPLE('5', 0x5),
/**
* Represents gold
*/
GOLD('6', 0x6),
/**
* Represents gray
*/
GRAY('7', 0x7),
/**
* Represents dark gray
*/
DARK_GRAY('8', 0x8),
/**
* Represents blue
*/
BLUE('9', 0x9),
/**
* Represents green
*/
GREEN('a', 0xA),
/**
* Represents aqua
*/
AQUA('b', 0xB),
/**
* Represents red
*/
RED('c', 0xC),
/**
* Represents light purple
*/
LIGHT_PURPLE('d', 0xD),
/**
* Represents yellow
*/
YELLOW('e', 0xE),
/**
* Represents white
*/
WHITE('f', 0xF),
/**
* Represents magical characters that change around randomly
*/
MAGIC('k', 0x10, true),
/**
* Makes the text bold.
*/
BOLD('l', 0x11, true),
/**
* Makes a line appear through the text.
*/
STRIKETHROUGH('m', 0x12, true),
/**
* Makes the text appear underlined.
*/
UNDERLINE('n', 0x13, true),
/**
* Makes the text italic.
*/
ITALIC('o', 0x14, true),
/**
* Resets all previous chat colors or formats.
*/
RESET('r', 0x15);
/**
* The special character which prefixes all chat colour codes. Use this if
* you need to dynamically convert colour codes from your custom format.
*/
public static final char COLOR_CHAR = '\u00A7';
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]");
private final int intCode;
private final char code;
private final boolean isFormat;
private final String toString;
private final static Map<Integer, ChatColor> BY_ID = Maps.newHashMap();
private final static Map<Character, ChatColor> BY_CHAR = Maps.newHashMap();
private ChatColor(char code, int intCode) {
this(code, intCode, false);
}
private ChatColor(char code, int intCode, boolean isFormat) {
this.code = code;
this.intCode = intCode;
this.isFormat = isFormat;
this.toString = new String(new char[]{COLOR_CHAR, code});
}
/**
* Gets the char value associated with this color
*
* @return A char value of this color code
*/
public char getChar() {
return code;
}
@Override
public String toString() {
return toString;
}
/**
* Checks if this code is a format code as opposed to a color code.
*/
public boolean isFormat() {
return isFormat;
}
/**
* Checks if this code is a color code as opposed to a format code.
*/
public boolean isColor() {
return !isFormat && this != RESET;
}
/**
* Gets the color represented by the specified color code
*
* @param code Code to check
* @return Associative {@link org.bukkit.ChatColor} with the given code, or
* null if it doesn't exist
*/
public static ChatColor getByChar(char code) {
return BY_CHAR.get(code);
}
/**
* Strips the given message of all color codes
*
* @param input String to strip of color
* @return A copy of the input string, without any coloring
*/
public static String stripColor(final String input) {
if (input == null) {
return null;
}
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
}
/**
* Translates a string using an alternate color code character into a string
* that uses the internal ChatColor.COLOR_CODE color code character. The
* alternate color code character will only be replaced if it is immediately
* followed by 0-9, A-F, or a-f.
*
* @param altColorChar The alternate color code character to replace. Ex: &
* @param textToTranslate Text containing the alternate color code
* character.
* @return Text containing the ChatColor.COLOR_CODE color code character.
*/
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
char[] b = textToTranslate.toCharArray();
for (int i = 0; i < b.length - 1; i++) {
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
b[i] = ChatColor.COLOR_CHAR;
b[i + 1] = Character.toLowerCase(b[i + 1]);
}
}
return new String(b);
}
/**
* Gets the ChatColors used at the end of the given input string.
*
* @param input Input string to retrieve the colors from.
* @return Any remaining ChatColors to pass onto the next line.
*/
public static String getLastColors(String input) {
String result = "";
int length = input.length();
// Search backwards from the end as it is faster
for (int index = length - 1; index > -1; index--) {
char section = input.charAt(index);
if (section == COLOR_CHAR && index < length - 1) {
char c = input.charAt(index + 1);
ChatColor color = getByChar(c);
if (color != null) {
result = color.toString() + result;
// Once we find a color or reset we can stop searching
if (color.isColor() || color.equals(RESET)) {
break;
}
}
}
}
return result;
}
static {
for (ChatColor color : values()) {
BY_ID.put(color.intCode, color);
BY_CHAR.put(color.code, color);
}
}
}

View File

@ -0,0 +1,201 @@
package net.md_5.bungee;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static net.md_5.bungee.Logger.$;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
public class Configuration {
/**
* Reconnect locations file.
*/
private transient File reconnect = new File("locations.yml");
/**
* Loaded reconnect locations.
*/
private transient Map<String, String> reconnectLocations;
/**
* Config file.
*/
private transient File file = new File("config.yml");
/**
* Yaml instance.
*/
private transient Yaml yaml;
/**
* Loaded config.
*/
private transient Map<String, Object> config;
/**
* Bind host.
*/
public String bindHost = "0.0.0.0:25577";
/**
* Server ping motd.
*/
public String motd = "BungeeCord Proxy Instance";
/**
* Name of default server.
*/
public String defaultServerName = "default";
/**
* Max players as displayed in list ping. Soft limit.
*/
public int maxPlayers = 1;
/**
* Socket timeout.
*/
public int timeout = 15000;
/**
* All servers.
*/
public Map<String, String> servers = new HashMap<String, String>() {
{
put(defaultServerName, "127.0.0.1");
put("pvp", "127.0.0.1:1337");
}
};
/**
* Forced servers.
*/
public Map<String, String> forcedServers = new HashMap<String, String>() {
{
put("pvp.md-5.net", "pvp");
}
};
/**
* Proxy admins.
*/
public List<String> admins = new ArrayList<String>() {
{
add("md_5");
}
};
/**
* Proxy moderators.
*/
public List<String> moderators = new ArrayList<String>() {
{
add("mbaxter");
}
};
public void load() {
try {
file.createNewFile();
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yaml = new Yaml(options);
try (InputStream is = new FileInputStream(file)) {
config = (Map) yaml.load(is);
}
if (config == null) {
config = new LinkedHashMap<>();
}
$().info("-------------- Loading configuration ----------------");
for (Field field : getClass().getDeclaredFields()) {
if (!Modifier.isTransient(field.getModifiers())) {
String name = Util.normalize(field.getName());
try {
Object def = field.get(this);
Object value = get(name, def);
field.set(this, value);
$().info(name + ": " + value);
} catch (IllegalAccessException ex) {
$().severe("Could not get config node: " + name);
}
}
}
$().info("-----------------------------------------------------");
if (servers.get(defaultServerName) == null) {
throw new IllegalArgumentException("Server '" + defaultServerName + "' not defined");
}
for (String server : forcedServers.values()) {
if (!servers.containsKey(server)) {
throw new IllegalArgumentException("Forced server " + server + " is not defined in servers");
}
}
reconnect.createNewFile();
try (FileInputStream recon = new FileInputStream(reconnect)) {
reconnectLocations = (Map) yaml.load(recon);
}
if (reconnectLocations == null) {
reconnectLocations = new LinkedHashMap<>();
}
} catch (IOException ex) {
$().severe("Could not load config!");
ex.printStackTrace();
}
}
private <T> T get(String path, T def) {
if (!config.containsKey(path)) {
config.put(path, def);
save(file, config);
}
return (T) config.get(path);
}
private void save(File fileToSave, Map toSave) {
try {
try (FileWriter wr = new FileWriter(fileToSave)) {
yaml.dump(toSave, wr);
}
} catch (IOException ex) {
$().severe("Could not save config file " + fileToSave);
ex.printStackTrace();
}
}
public InetSocketAddress getHostFor(String user, InetSocketAddress requestedHost) {
String entry = user + ";" + Util.getAddr(requestedHost);
String host = requestedHost.getHostString();
String hostLine;
if (forcedServers.containsKey(host)) {
hostLine = servers.get(forcedServers.get(host));
} else {
hostLine = reconnectLocations.get(entry);
}
if (hostLine == null) {
hostLine = servers.get(defaultServerName);
}
return Util.getAddr(hostLine);
}
public InetSocketAddress getServer(String name) {
String hostline = servers.get(name);
if (hostline != null) {
return Util.getAddr(hostline);
} else {
return null;
}
}
public void saveHosts() {
save(reconnect, reconnectLocations);
$().info("Saved reconnect locations to " + reconnect);
}
}

View File

@ -0,0 +1,61 @@
package net.md_5.bungee;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import net.md_5.bungee.packet.Packet2Handshake;
import net.md_5.bungee.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketInputStream;
public class InitialHandler implements Runnable {
private final Socket socket;
private final PacketInputStream in;
private final OutputStream out;
public InitialHandler(Socket socket) throws IOException {
this.socket = socket;
in = new PacketInputStream(socket.getInputStream());
out = socket.getOutputStream();
}
@Override
public void run() {
try {
byte[] packet = in.readPacket();
int id = Util.getId(packet);
switch (id) {
case 0x02:
Packet2Handshake handshake = new Packet2Handshake(packet);
break;
case 0xFE:
throw new KickException(BungeeCord.instance.config.motd + ChatColor.COLOR_CHAR + BungeeCord.instance.getOnlinePlayers() + ChatColor.COLOR_CHAR + BungeeCord.instance.config.maxPlayers);
default:
throw new IllegalArgumentException("Wasn't ready for packet id " + Util.hex(id));
}
} catch (KickException ex) {
kick(ex.getMessage());
} catch (Exception ex) {
kick("[Proxy Error] " + Util.exception(ex));
}
}
private void kick(String message) {
try {
out.write(new PacketFFKick(message).getPacket());
} catch (IOException ioe) {
} finally {
try {
socket.close();
} catch (IOException ioe2) {
}
}
}
private class KickException extends RuntimeException {
public KickException(String message) {
super(message);
}
}
}

View File

@ -0,0 +1,35 @@
package net.md_5.bungee;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import static net.md_5.bungee.Logger.$;
public class ListenThread extends Thread {
public final ServerSocket socket;
public ListenThread(InetSocketAddress addr) throws IOException {
super("Listen Thread");
socket = new ServerSocket();
socket.bind(addr);
}
@Override
public void run() {
while (BungeeCord.instance.isRunning) {
try {
Socket client = socket.accept();
BungeeCord.instance.setSocketOptions(client);
$().info(client.getInetAddress() + " has connected");
InitialHandler handler = new InitialHandler(client);
BungeeCord.instance.threadPool.submit(handler);
} catch (SocketException ex) {
} catch (IOException ex) {
ex.printStackTrace(); // TODO
}
}
}
}

View File

@ -0,0 +1,20 @@
package net.md_5.bungee;
public class ReconnectSaveThread extends Thread {
public ReconnectSaveThread() {
super("Location Save Thread");
setPriority(Thread.MIN_PRIORITY);
}
@Override
public void run() {
while (BungeeCord.instance.isRunning) {
try {
Thread.sleep(5 * 1000 * 60); // 5 minutes
} catch (InterruptedException ex) {
}
BungeeCord.instance.config.saveHosts();
}
}
}

View File

@ -0,0 +1,70 @@
package net.md_5.bungee;
import java.net.InetSocketAddress;
public class Util {
private static final int DEFAULT_PORT = 25565;
/**
* Basic method to transform human readable addresses into usable address
* objects.
*
* @param hostline in the format of 'host:port'
* @return the constructed hostname + port.
*/
public static InetSocketAddress getAddr(String hostline) {
String[] split = hostline.split(":");
if (split.length < 2) {
throw new IllegalArgumentException("Invalid split format");
}
int port = DEFAULT_PORT;
if (split.length > 1) {
port = Integer.parseInt(split[1]);
}
return new InetSocketAddress(split[0], port);
}
/**
* Turns a InetSocketAddress into a string that can be reconstructed later.
*
* @param address the address to serialize
* @return
*/
public static String getAddr(InetSocketAddress address) {
return address.getAddress().getHostAddress() + ((address.getPort() != DEFAULT_PORT) ? ":" + address.getPort() : "");
}
/**
* Get the packet id of specified byte array.
*/
public static int getId(byte[] b) {
return b[0] & 0xFF;
}
/**
* Normalizes a config path by prefix upper case letters with '_' and
* turning them to lowercase.
*
* @param s the string to normalize
* @return the normalized path
*/
public static String normalize(String s) {
StringBuilder result = new StringBuilder();
for (char c : s.toCharArray()) {
if (Character.isUpperCase(c)) {
result.append("_");
}
result.append(Character.toLowerCase(c));
}
return result.toString();
}
public static String hex(int i) {
return String.format("0x%02X", i);
}
public static String exception(Throwable t) {
return t.getClass().getSimpleName() + " : " + t.getMessage() + " @ " + t.getStackTrace()[0].getFileName() + ":" + t.getStackTrace()[0].getLineNumber();
}
}

View File

@ -0,0 +1,88 @@
package net.md_5.bungee.packet;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.io.DataInput;
import java.io.DataOutput;
import lombok.Delegate;
import net.md_5.bungee.Util;
public abstract class DefinedPacket implements DataInput, DataOutput {
private interface Overriden {
void readUTF();
void writeUTF(String s);
}
@Delegate(excludes = Overriden.class)
private ByteArrayDataInput in;
@Delegate(excludes = Overriden.class)
private ByteArrayDataOutput out;
/**
* Packet id.
*/
public final int id;
/**
* Already constructed packet.
*/
private byte[] packet;
public DefinedPacket(int id, byte[] buf) {
in = ByteStreams.newDataInput(buf);
if (readUnsignedByte() != id) {
throw new IllegalArgumentException("Wasn't expecting packet id " + Util.hex(id));
}
this.id = id;
packet = buf;
}
public DefinedPacket(int id) {
out = ByteStreams.newDataOutput();
this.id = id;
writeByte(id);
packet = null;
}
public byte[] getPacket() {
return packet == null ? out.toByteArray() : packet;
}
@Override
public void writeUTF(String s) {
writeShort(s.length());
writeChars(s);
}
@Override
public String readUTF() {
short len = readShort();
char[] chars = new char[len];
for (int i = 0; i < len; i++) {
chars[i] = this.readChar();
}
return new String(chars);
}
public void writeArray(byte[] b) {
writeShort(b.length);
write(b);
}
public byte[] readArray() {
short len = readShort();
byte[] ret = new byte[len];
readFully(ret);
return ret;
}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
@Override
public abstract String toString();
}

View File

@ -0,0 +1,21 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet10HeldItem extends DefinedPacket {
public short slot;
public Packet10HeldItem(short slot) {
super(0x10);
writeShort(slot);
}
public Packet10HeldItem(byte[] buf) {
super(0x10, buf);
this.slot = readShort();
}
}

View File

@ -0,0 +1,30 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet2Handshake extends DefinedPacket {
public byte procolVersion;
public String username;
public String host;
public int port;
public Packet2Handshake(byte protocolVersion, String username, String host, int port) {
super(0x02);
writeByte(protocolVersion);
writeUTF(username);
writeUTF(host);
writeInt(port);
}
public Packet2Handshake(byte[] buf) {
super(0x02, buf);
this.procolVersion = readByte();
this.username = readUTF();
this.host = readUTF();
this.port = readInt();
}
}

View File

@ -0,0 +1,21 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet3Chat extends DefinedPacket {
public String message;
public Packet3Chat(String message) {
super(0x03);
writeUTF(message);
}
public Packet3Chat(byte[] buf) {
super(0x03, buf);
this.message = readUTF();
}
}

View File

@ -0,0 +1,21 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet46GameState extends DefinedPacket {
/**
* Update game state. When sent with a state of 2, rain will cease.
*
* @param state the new game state
* @param mode the new game mode. Used when state == 3.
*/
public Packet46GameState(byte state, byte mode) {
super(0x46);
writeByte(state);
writeByte(mode);
}
}

View File

@ -0,0 +1,33 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet9Respawn extends DefinedPacket {
public int dimension;
public byte difficulty;
public byte gameMode;
public short worldHeight;
public String levelType;
public Packet9Respawn(int dimension, byte difficulty, byte gameMode, short worldHeight, String levelType) {
super(0x09);
writeInt(dimension);
writeByte(difficulty);
writeByte(gameMode);
writeShort(worldHeight);
writeUTF(levelType);
}
public Packet9Respawn(byte[] buf) {
super(0x09, buf);
this.dimension = readInt();
this.difficulty = readByte();
this.gameMode = readByte();
this.worldHeight = readShort();
this.levelType = readUTF();
}
}

View File

@ -0,0 +1,26 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFAPluginMessage extends DefinedPacket {
public String tag;
public byte[] data;
public PacketFAPluginMessage(String tag, byte[] data) {
super(0xFA);
writeUTF(tag);
writeArray(data);
this.tag = tag;
this.data = data;
}
public PacketFAPluginMessage(byte[] buf) {
super(0xFA, buf);
this.tag = readUTF();
this.data = readArray();
}
}

View File

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

View File

@ -0,0 +1,23 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFDEncryptionRequest extends DefinedPacket {
public String serverId;
public byte[] publicKey;
public byte[] verifyToken;
public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken) {
super(0xFD);
writeUTF(serverId);
writeArray(publicKey);
writeArray(verifyToken);
this.serverId = serverId;
this.publicKey = publicKey;
this.verifyToken = verifyToken;
}
}

View File

@ -0,0 +1,21 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFFKick extends DefinedPacket {
public String message;
public PacketFFKick(String message) {
super(0xFF);
writeUTF(message);
}
public PacketFFKick(byte[] buf) {
super(0xFF, buf);
this.message = readUTF();
}
}

View File

@ -0,0 +1,48 @@
package net.md_5.bungee.packet;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import net.md_5.bungee.Util;
import net.minecraft.server.Packet;
public class PacketInputStream {
private final DataInputStream dataInput;
private final TrackingInputStream tracker;
public PacketInputStream(InputStream in) {
tracker = new TrackingInputStream(in);
dataInput = new DataInputStream(tracker);
}
public byte[] readPacket() throws IOException {
tracker.out.reset();
int id = tracker.read();
Packet codec = VanillaPackets.packets[id];
if (codec == null) {
throw new RuntimeException("No Packet id: " + Util.hex(id));
}
codec.a(dataInput);
return tracker.out.toByteArray();
}
private class TrackingInputStream extends InputStream {
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
private final InputStream wrapped;
public TrackingInputStream(InputStream wrapped) {
this.wrapped = wrapped;
}
@Override
public int read() throws IOException {
int ret = wrapped.read();
out.write(ret);
return ret;
}
}
}

View File

@ -0,0 +1,172 @@
package net.md_5.bungee.packet;
import java.lang.reflect.InvocationTargetException;
import static net.md_5.bungee.Logger.$;
import net.md_5.bungee.Util;
import net.minecraft.server.Packet;
import net.minecraft.server.Packet0KeepAlive;
import net.minecraft.server.Packet100OpenWindow;
import net.minecraft.server.Packet101CloseWindow;
import net.minecraft.server.Packet102WindowClick;
import net.minecraft.server.Packet103SetSlot;
import net.minecraft.server.Packet104WindowItems;
import net.minecraft.server.Packet105CraftProgressBar;
import net.minecraft.server.Packet106Transaction;
import net.minecraft.server.Packet107SetCreativeSlot;
import net.minecraft.server.Packet108ButtonClick;
import net.minecraft.server.Packet10Flying;
import net.minecraft.server.Packet11PlayerPosition;
import net.minecraft.server.Packet12PlayerLook;
import net.minecraft.server.Packet130UpdateSign;
import net.minecraft.server.Packet131ItemData;
import net.minecraft.server.Packet132TileEntityData;
import net.minecraft.server.Packet13PlayerLookMove;
import net.minecraft.server.Packet14BlockDig;
import net.minecraft.server.Packet15Place;
import net.minecraft.server.Packet16BlockItemSwitch;
import net.minecraft.server.Packet17EntityLocationAction;
import net.minecraft.server.Packet18ArmAnimation;
import net.minecraft.server.Packet19EntityAction;
import net.minecraft.server.Packet1Login;
import net.minecraft.server.Packet200Statistic;
import net.minecraft.server.Packet201PlayerInfo;
import net.minecraft.server.Packet202Abilities;
import net.minecraft.server.Packet203TabComplete;
import net.minecraft.server.Packet204LocaleAndViewDistance;
import net.minecraft.server.Packet205ClientCommand;
import net.minecraft.server.Packet20NamedEntitySpawn;
import net.minecraft.server.Packet21PickupSpawn;
import net.minecraft.server.Packet22Collect;
import net.minecraft.server.Packet23VehicleSpawn;
import net.minecraft.server.Packet24MobSpawn;
import net.minecraft.server.Packet250CustomPayload;
import net.minecraft.server.Packet252KeyResponse;
import net.minecraft.server.Packet253KeyRequest;
import net.minecraft.server.Packet254GetInfo;
import net.minecraft.server.Packet255KickDisconnect;
import net.minecraft.server.Packet25EntityPainting;
import net.minecraft.server.Packet26AddExpOrb;
import net.minecraft.server.Packet28EntityVelocity;
import net.minecraft.server.Packet29DestroyEntity;
import net.minecraft.server.Packet2Handshake;
import net.minecraft.server.Packet30Entity;
import net.minecraft.server.Packet31RelEntityMove;
import net.minecraft.server.Packet32EntityLook;
import net.minecraft.server.Packet33RelEntityMoveLook;
import net.minecraft.server.Packet34EntityTeleport;
import net.minecraft.server.Packet35EntityHeadRotation;
import net.minecraft.server.Packet38EntityStatus;
import net.minecraft.server.Packet39AttachEntity;
import net.minecraft.server.Packet3Chat;
import net.minecraft.server.Packet40EntityMetadata;
import net.minecraft.server.Packet41MobEffect;
import net.minecraft.server.Packet42RemoveMobEffect;
import net.minecraft.server.Packet43SetExperience;
import net.minecraft.server.Packet4UpdateTime;
import net.minecraft.server.Packet51MapChunk;
import net.minecraft.server.Packet52MultiBlockChange;
import net.minecraft.server.Packet53BlockChange;
import net.minecraft.server.Packet54PlayNoteBlock;
import net.minecraft.server.Packet55BlockBreakAnimation;
import net.minecraft.server.Packet56MapChunkBulk;
import net.minecraft.server.Packet5EntityEquipment;
import net.minecraft.server.Packet60Explosion;
import net.minecraft.server.Packet61WorldEvent;
import net.minecraft.server.Packet62NamedSoundEffect;
import net.minecraft.server.Packet6SpawnPosition;
import net.minecraft.server.Packet70Bed;
import net.minecraft.server.Packet71Weather;
import net.minecraft.server.Packet7UseEntity;
import net.minecraft.server.Packet8UpdateHealth;
import net.minecraft.server.Packet9Respawn;
public class VanillaPackets {
public static Packet[] packets = new Packet[256];
private static void map(int id, Class clazz) {
try {
packets[id] = (Packet) clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException ex) {
$().severe("Could not register packet id " + Util.hex(id));
}
}
static {
map(0, Packet0KeepAlive.class);
map(1, Packet1Login.class);
map(2, Packet2Handshake.class);
map(3, Packet3Chat.class);
map(4, Packet4UpdateTime.class);
map(5, Packet5EntityEquipment.class);
map(6, Packet6SpawnPosition.class);
map(7, Packet7UseEntity.class);
map(8, Packet8UpdateHealth.class);
map(9, Packet9Respawn.class);
map(10, Packet10Flying.class);
map(11, Packet11PlayerPosition.class);
map(12, Packet12PlayerLook.class);
map(13, Packet13PlayerLookMove.class);
map(14, Packet14BlockDig.class);
map(15, Packet15Place.class);
map(16, Packet16BlockItemSwitch.class);
map(17, Packet17EntityLocationAction.class);
map(18, Packet18ArmAnimation.class);
map(19, Packet19EntityAction.class);
map(20, Packet20NamedEntitySpawn.class);
map(21, Packet21PickupSpawn.class);
map(22, Packet22Collect.class);
map(23, Packet23VehicleSpawn.class);
map(24, Packet24MobSpawn.class);
map(25, Packet25EntityPainting.class);
map(26, Packet26AddExpOrb.class);
map(28, Packet28EntityVelocity.class);
map(29, Packet29DestroyEntity.class);
map(30, Packet30Entity.class);
map(31, Packet31RelEntityMove.class);
map(32, Packet32EntityLook.class);
map(33, Packet33RelEntityMoveLook.class);
map(34, Packet34EntityTeleport.class);
map(35, Packet35EntityHeadRotation.class);
map(38, Packet38EntityStatus.class);
map(39, Packet39AttachEntity.class);
map(40, Packet40EntityMetadata.class);
map(41, Packet41MobEffect.class);
map(42, Packet42RemoveMobEffect.class);
map(43, Packet43SetExperience.class);
map(51, Packet51MapChunk.class);
map(52, Packet52MultiBlockChange.class);
map(53, Packet53BlockChange.class);
map(54, Packet54PlayNoteBlock.class);
map(55, Packet55BlockBreakAnimation.class);
map(56, Packet56MapChunkBulk.class);
map(60, Packet60Explosion.class);
map(61, Packet61WorldEvent.class);
map(62, Packet62NamedSoundEffect.class);
map(70, Packet70Bed.class);
map(71, Packet71Weather.class);
map(100, Packet100OpenWindow.class);
map(101, Packet101CloseWindow.class);
map(102, Packet102WindowClick.class);
map(103, Packet103SetSlot.class);
map(104, Packet104WindowItems.class);
map(105, Packet105CraftProgressBar.class);
map(106, Packet106Transaction.class);
map(107, Packet107SetCreativeSlot.class);
map(108, Packet108ButtonClick.class);
map(130, Packet130UpdateSign.class);
map(131, Packet131ItemData.class);
map(132, Packet132TileEntityData.class);
map(200, Packet200Statistic.class);
map(201, Packet201PlayerInfo.class);
map(202, Packet202Abilities.class);
map(203, Packet203TabComplete.class);
map(204, Packet204LocaleAndViewDistance.class);
map(205, Packet205ClientCommand.class);
map(250, Packet250CustomPayload.class);
map(252, Packet252KeyResponse.class);
map(253, Packet253KeyRequest.class);
map(254, Packet254GetInfo.class);
map(255, Packet255KickDisconnect.class);
}
}