7cd074e13c
Протокол до сих пор умел только рукопожатие: поздороваться, доказать пароль, получить токен и войти. Дальше играть было нечем. Добавлено: - Packet, интерфейс, который умеет написать себя сам. Раньше отправитель разбирал тип пакета в ветке instanceof, и с каждым новым видом пакета эта ветка росла. Теперь отправитель знает только, что у пакета есть write(). - Блоки и ChunkFormat переехали в протокол. Идентификаторы блоков и геометрия чанка — это словарь провода, а не внутреннее дело сервера: клиент получает чанк байт на блок и обязан знать, что означает каждый байт. Один источник правды вместо двух, которые могут разойтись. - Игровые пакеты: JoinWorld, WorldJoin, ChunkData, BlockChange, PlayerMove, EntityMove, Ping/Pong, EntitySpawn, EntityRemove. - Чанк уходит по проводу сжатым. Чанк это 65536 байт, и почти все они это воздух, поэтому дефлейт тут не украшение, а разница между «сервер принял» и «клиент вошёл». - Имена всем пакетам в таблице names(), чтобы забытый пакет был виден сразу, а не всплывал через месяц. Проверено: 22 теста протокола, включая круг чанка через сжатие, отказ битому сжатому блоку и отказ обрезанному.
65 lines
2.1 KiB
Java
65 lines
2.1 KiB
Java
package net.rustnotmc.protocol;
|
|
|
|
/**
|
|
* The block ids a world is made of.
|
|
*
|
|
* These live in the protocol rather than in the server because they are the
|
|
* vocabulary of the wire: a chunk that arrives at the client is a byte per
|
|
* block, and both ends have to agree on what each byte means. A server that
|
|
* invented its own ids would leave the client guessing.
|
|
*
|
|
* Kept as plain constants rather than an enum so that a chunk can store them in
|
|
* a byte array. A world holding an enum per block costs sixteen bytes a block
|
|
* and a garbage collector problem for every chunk that moves.
|
|
*
|
|
* The ids do not have to match anything right now. What matters is that they are
|
|
* stable, because they go into save files and onto the wire, and that air is
|
|
* zero, because every "is this chunk generated" shortcut in the code assumes it.
|
|
*/
|
|
public final class Blocks {
|
|
|
|
public static final byte AIR = 0;
|
|
public static final byte STONE = 1;
|
|
public static final byte DIRT = 2;
|
|
public static final byte GRASS = 3;
|
|
public static final byte SAND = 4;
|
|
public static final byte WATER = 5;
|
|
public static final byte WOOD = 6;
|
|
public static final byte LEAVES = 7;
|
|
|
|
/** Lowest and highest legal block id. */
|
|
public static final byte MIN = AIR;
|
|
public static final byte MAX = LEAVES;
|
|
|
|
private Blocks() {
|
|
}
|
|
|
|
public static String name(byte id) {
|
|
switch (id) {
|
|
case AIR: return "air";
|
|
case STONE: return "stone";
|
|
case DIRT: return "dirt";
|
|
case GRASS: return "grass";
|
|
case SAND: return "sand";
|
|
case WATER: return "water";
|
|
case WOOD: return "wood";
|
|
case LEAVES: return "leaves";
|
|
default: return "unknown(" + id + ")";
|
|
}
|
|
}
|
|
|
|
/** True when a player can walk through it. */
|
|
public static boolean isPassable(byte id) {
|
|
return id == AIR || id == WATER;
|
|
}
|
|
|
|
/** True when light passes through it. */
|
|
public static boolean isTransparent(byte id) {
|
|
return id == AIR || id == WATER;
|
|
}
|
|
|
|
public static boolean isValid(byte id) {
|
|
return id >= MIN && id <= MAX;
|
|
}
|
|
}
|