Code:
package org.virtue.network.codec.login;
import java.math.BigInteger;
import java.net.ProtocolException;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.virtue.Constants;
import org.virtue.game.character.FileManager;
import org.virtue.game.character.player.Player;
import org.virtue.game.character.player.PlayerDefinition;
import org.virtue.game.character.player.Player.GameState;
import org.virtue.network.UpstreamHandler;
import org.virtue.network.message.login.LoginRequest;
import org.virtue.network.message.login.LoginRequest.LoginType;
import org.virtue.utilities.base37.Base37Utils;
import org.virtue.utilities.io.BufferUtils;
import org.virtue.utilities.krypto.XTEA;
/**
* @author James, Kyle
*
*/
public class LoginDecoder extends FrameDecoder {
public enum Stage {
CONNECTION_TYPE, CLIENT_DETAILS, LOBBY_PAYLOAD, GAME_PAYLOAD;
}
public enum LoginTypes {
LOBBY, WORLD;
}
private Stage stage = Stage.CONNECTION_TYPE;
private LoginTypes type;
private int size;
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
switch (stage) {
case CONNECTION_TYPE:
return decodeConnectionType(buffer);
case CLIENT_DETAILS:
return decodeClientDetails(buffer);
case LOBBY_PAYLOAD:
return decodeLobbyPayload(channel, buffer);
case GAME_PAYLOAD:
return decodeGamePayload(channel, buffer);
}
return null;
}
private Object decodeConnectionType(ChannelBuffer buffer) throws ProtocolException {
if (!buffer.readable())
throw new IllegalStateException("Not enough readable bytes from buffer!");
int loginType = buffer.readByte() & 0xFF;
if (loginType != 16 && loginType != 19 && loginType != 18)
throw new ProtocolException("Invalid login type: " + loginType);
type = loginType == 19 ? LoginTypes.LOBBY : LoginTypes.WORLD;
size = buffer.readShort() & 0xFFFF;
stage = Stage.CLIENT_DETAILS;
return null;
}
private Object decodeClientDetails(ChannelBuffer buffer) throws ProtocolException {
if (buffer.readableBytes() < size)
throw new IllegalStateException("Not enough readable bytes from buffer!");
int version = buffer.readInt();
int subVersion = buffer.readInt();
if (version != Constants.REVISION && subVersion != Constants.SUB_REVISION)
throw new IllegalStateException("Invalid client version/sub-version!");
if (type.equals(LoginTypes.WORLD))
buffer.readByte();
if (type.equals(LoginTypes.LOBBY))
stage = Stage.LOBBY_PAYLOAD;
else
stage = Stage.GAME_PAYLOAD;
return null;
}
private Object decodeLobbyPayload(Channel channel, ChannelBuffer buffer) throws ProtocolException {
if (buffer.readableBytes() < 2)
throw new IllegalStateException("Not enough readable bytes from buffer.");
int secureBufferSize = buffer.readShort() & 0xFFFF;
if (buffer.readableBytes() < secureBufferSize)
throw new IllegalStateException("Not enough readable bytes from buffer.");
byte[] secureBytes = new byte[secureBufferSize];
buffer.readBytes(secureBytes);
ChannelBuffer secureBuffer = ChannelBuffers.wrappedBuffer(new BigInteger(secureBytes).modPow(Constants.LOGIN_EXPONENT, Constants.LOGIN_MODULUS).toByteArray());
int blockOpcode = secureBuffer.readByte() & 0xFF;
if (blockOpcode != 10)
throw new ProtocolException("Invalid block opcode: " + blockOpcode);
int[] xteaKey = new int[4];
for (int i = 0; i < xteaKey.length; i++)
xteaKey[i] = secureBuffer.readInt();
long loginHash = secureBuffer.readLong();
//if (loginHash != 0)
//throw new ProtocolException("Invalid login hash: " + loginHash);
secureBuffer.readByte();
secureBuffer.readerIndex(secureBuffer.readerIndex() - 4);
String password = BufferUtils.readString(secureBuffer);
long[] loginSeeds = new long[2];
for (int i = 0; i < loginSeeds.length; i++)
loginSeeds[i] = secureBuffer.readLong();
byte[] xteaBlock = new byte[buffer.readableBytes()];
buffer.readBytes(xteaBlock);
XTEA xtea = new XTEA(xteaKey);
xtea.decrypt(xteaBlock, 0, xteaBlock.length);
ChannelBuffer xteaBuffer = ChannelBuffers.wrappedBuffer(xteaBlock);
boolean decodeAsString = xteaBuffer.readByte() == 1;
String username = decodeAsString ? BufferUtils.readString(xteaBuffer) : Base37Utils.decodeBase37(xteaBuffer.readLong());
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedShort();
xteaBuffer.readUnsignedShort();
xteaBuffer.readUnsignedByte();
int[] randomData = new int[24];
for (int index = 0; index < randomData.length; index++) {
randomData[index] = xteaBuffer.readUnsignedByte();
}
BufferUtils.readString(xteaBuffer);
int length = xteaBuffer.readUnsignedByte();
byte[] machineData = new byte[length];
for (int data = 0; data < machineData.length; data++) {
machineData[data] = (byte) xteaBuffer.readUnsignedByte();
}
xteaBuffer.readInt();
BufferUtils.readString(xteaBuffer);
xteaBuffer.readInt();
xteaBuffer.readInt();
BufferUtils.readString(xteaBuffer);
xteaBuffer.readUnsignedByte();
int[] cacheCRC = new int[36];
for (int index = 0; index < cacheCRC.length; index++) {
cacheCRC[index] = xteaBuffer.readInt();
}
return new LoginRequest(username, password, LoginType.LOBBY);
}
private Object decodeGamePayload(Channel channel, ChannelBuffer buffer) throws ProtocolException {
if (buffer.readableBytes() < 2)
throw new IllegalStateException("Not enough readable bytes from buffer.");
int secureBufferSize = buffer.readShort() & 0xFFFF;
if (buffer.readableBytes() < secureBufferSize)
throw new IllegalStateException("Not enough readable bytes from buffer.");
byte[] secureBytes = new byte[secureBufferSize];
buffer.readBytes(secureBytes);
ChannelBuffer secureBuffer = ChannelBuffers.wrappedBuffer(new BigInteger(secureBytes).modPow(Constants.LOGIN_EXPONENT, Constants.LOGIN_MODULUS).toByteArray());
int blockOpcode = secureBuffer.readByte() & 0xFF;
if (blockOpcode != 10)
throw new ProtocolException("Invalid block opcode: " + blockOpcode);
int[] xteaKey = new int[4];
for (int i = 0; i < xteaKey.length; i++)
xteaKey[i] = secureBuffer.readInt();
long loginHash = secureBuffer.readLong();
//if (loginHash != 0)
//throw new ProtocolException("Invalid login hash: " + loginHash);
secureBuffer.readByte();
secureBuffer.readerIndex(secureBuffer.readerIndex() - 4);
String password = BufferUtils.readString(secureBuffer);
long[] loginSeeds = new long[2];
for (int i = 0; i < loginSeeds.length; i++)
loginSeeds[i] = secureBuffer.readLong();
byte[] xteaBlock = new byte[buffer.readableBytes()];
buffer.readBytes(xteaBlock);
XTEA xtea = new XTEA(xteaKey);
xtea.decrypt(xteaBlock, 0, xteaBlock.length);
ChannelBuffer xteaBuffer = ChannelBuffers.wrappedBuffer(xteaBlock);
boolean decodeAsString = xteaBuffer.readByte() == 1;
String username = decodeAsString ? BufferUtils.readString(xteaBuffer) : Base37Utils.decodeBase37(xteaBuffer.readLong());
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedShort();
xteaBuffer.readUnsignedShort();
xteaBuffer.readUnsignedByte();
int[] randomData = new int[24];
for (int index = 0; index < randomData.length; index++) {
randomData[index] = xteaBuffer.readUnsignedByte();
}
BufferUtils.readString(xteaBuffer);
xteaBuffer.readInt();
int length = xteaBuffer.readUnsignedByte();
byte[] machineData = new byte[length];
for (int data = 0; data < machineData.length; data++) {
machineData[data] = (byte) xteaBuffer.readUnsignedByte();
}
xteaBuffer.readInt();
xteaBuffer.readInt();
xteaBuffer.readInt();
BufferUtils.readString(xteaBuffer);
boolean extra = xteaBuffer.readUnsignedByte() == 1;
if (extra) {
BufferUtils.readString(xteaBuffer);
}
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedByte();
xteaBuffer.readInt();
BufferUtils.readString(xteaBuffer);
xteaBuffer.readUnsignedByte();
xteaBuffer.readUnsignedShort();
xteaBuffer.readUnsignedShort();
int[] cacheCRC = new int[36];
for (int index = 0; index < cacheCRC.length; index++) {
cacheCRC[index] = xteaBuffer.readInt();
}
return new LoginRequest(username, password, LoginType.WORLD);
}
}