Minecraft изнутри - статья 3, пакеты
Как я и обещал, продолжаем.
Сегодня мы разберем структуру пакетов и протокол авторизации.
Хотелось бы начать с аунтификации. В майнкрафте на данный момент есть две схемы - с поддержкой шифрования и без нее. Разберем обе.
С шифрованием
Схема
Без шифрования
Схема
Клиент может и не отправить пакет с ID 252, а сразу же 205, сервер просто проигнорирует и продолжит без шифрования. Стоить заметить, что при этом не отсылается запрос на сервера Minecraft.net
http://session.minecraft.net/game/joinserver.jsp?user=логин&sessionId=id_сессии&serverId=ключ_сервера
http://session.minecraft.net/game/checkserver.jsp?user=логин&serverId=ключ_сервера
Это же и не происходит при onlinemode=false
Классы для шифрования хранятся в пакете org.*. Когда я реализовывал свой флудер (и да, на гитхабе не самая рабочая версия, я ее выложил для демонстрации устройства сети), я поначалу тоже думал делать шифрование, но решил не реализовывать за ненадобностью.
Что же у нас происходит при авторизации? Для начала, сервер ждет от клиента Packet2Handshake, в котором передаются данные о сессии (логин и id). Далее сервер в любом случае присылает Packet253ServerAuthData с ключом сервера и кодом проверки, после чего клиент либо инициирует процесс активации шифрования, либо просто посылает пакет с ID 205 (Packet205ClientCommand), не реагируя на 253. Затем клиент получает пакет 1Login, в котором содержатся: ID его Entity, тип мира, текущее измерение, максимальное количество игроков, сложность и свой игровой режим (gamemode). Ладно, хватит занудного текста, перейдем к самим пакетам
Как же происходит отправка пакетов? Легко - все данные сваливаются в одну общую кучу в стандартный DataOutputStream. Сначала передаем ID пакета, а затем сами данные. Вот как это было реализовано у меня:
public void writePacket(Packet packet) throws IOException {
try {
this.sout.write(packet.getId());
packet.write(this.sout);
log.info("Write packet " + packet.getId());
} catch(Exception e) {
e.printStackTrace();
}
}
Хотелось бы рассказать о хитростях в плане отправки/чтения строк, массивов и всего прочего. Опять приведу свой код, так как я не стал реализовывать функции writeString и прочее.
public void write(DataOutputStream buf) throws IOException, NotSupportedOperationException {
buf.writeShort(message.length());
for(int i = 0; i ‹ message.length(); ++i) {
buf.writeChar(message.charAt(i));
}
}
передаем в функцию наш DataOutputStream и запихиваем данные.
Для передачи строки нам необходимо сначала передать ее длину, а затем каждый символ. При чтении делается тоже самое.
public static void writeString(String par0Str, DataOutputStream par1DataOutputStream) throws IOException
{
if (par0Str.length() › 32767)
{
throw new IOException("String too big");
}
else
{
par1DataOutputStream.writeShort(par0Str.length());
par1DataOutputStream.writeChars(par0Str);
}
}
public static void writeByteArray(DataOutputStream par0DataOutputStream, byte[] par1ArrayOfByte) throws IOException
{
par0DataOutputStream.writeShort(par1ArrayOfByte.length);
par0DataOutputStream.write(par1ArrayOfByte);
}
Ну, подытожим. Авторизоваться можно и без шифрования, а можно и с ним. При отсылки массива байтов и строки мы отсылаем размер, а затем само содержимое.
В следующем посте мы создадим свой пакет и попробуем что-нибудь отправить, а пока на сегодня все.
901 специально для MCGL. При копировании указание копирайта обязательно.