3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-17 05:20:14 +01:00

Started work on revamped protocol registry

Dieser Commit ist enthalten in:
Andrew Steinborn 2021-02-21 22:43:12 -05:00
Ursprung ba7ba70f4b
Commit 602ed04dee
11 geänderte Dateien mit 460 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,100 @@
package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketReader;
import com.velocitypowered.proxy.network.packet.PacketWriter;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryBuilder.PacketMapping;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Provides a packet registry map that is "dense", ideal for registries that are tightly packed
* together by ID. Lookups for readers are very fast (O(1)) and for writers uses an embedded
* open-addressing, probing hash map to conserve memory.
*/
public class DensePacketRegistryMap implements PacketRegistryMap {
private final PacketReader<?>[] readersById;
private final PacketWriter[] writersByClass;
private final Class<?>[] classesById;
public DensePacketRegistryMap(Int2ObjectMap<PacketMapping<?>> mappings) {
int size = mappings.keySet().stream().mapToInt(x -> x).max().orElse(0) + 1;
this.readersById = new PacketReader[size];
this.writersByClass = new PacketWriter[size * 2];
this.classesById = new Class[size * 2];
for (PacketMapping<?> value : mappings.values()) {
this.readersById[value.id] = value.reader;
this.place(value.packetClass, value.writer);
}
}
private void place(Class<?> key, PacketWriter<?> value) {
int bucket = findEmpty(key);
this.writersByClass[bucket] = value;
this.classesById[bucket] = key;
}
private int findEmpty(Class<?> key) {
int start = key.hashCode() % this.classesById.length;
int index = start;
for (;;) {
if (this.classesById[index] == null || this.classesById[index].equals(key)) {
// It's available, so no chance that this value exists anywhere in the map.
return index;
}
if ((index = (index + 1) % this.classesById.length) == start) {
return -1;
}
}
}
private int index(Class<?> key) {
int start = key.hashCode() % this.classesById.length;
int index = start;
for (;;) {
if (this.classesById[index] == null) {
// It's available, so no chance that this value exists anywhere in the map.
return -1;
}
if (key.equals(this.classesById[index])) {
return index;
}
// Conflict, keep probing ...
if ((index = (index + 1) % this.classesById.length) == start) {
return -1;
}
}
}
@Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) {
if (id < 0 || id >= this.readersById.length) {
return null;
}
return this.readersById[id].read(buf, version);
}
@Override
public <P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version) {
int id = this.index(packet.getClass());
if (id != -1) {
this.writersByClass[id].write(buf, packet, version);
} else {
throw new IllegalArgumentException(String.format(
"Unable to find id for packet of type %s in protocol %s",
packet.getClass().getName(), version
));
}
}
}

Datei anzeigen

@ -0,0 +1,28 @@
package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class EmptyPacketRegistryMap implements PacketRegistryMap {
public static final EmptyPacketRegistryMap INSTANCE = new EmptyPacketRegistryMap();
private EmptyPacketRegistryMap() {
}
@Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) {
return null;
}
@Override
public <P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version) {
throw new IllegalArgumentException(String.format(
"Unable to find id for packet of type %s in protocol %s",
packet.getClass().getName(), version
));
}
}

Datei anzeigen

@ -0,0 +1,70 @@
package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketReader;
import com.velocitypowered.proxy.network.packet.PacketWriter;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public class PacketRegistryBuilder {
private final Int2ObjectMap<PacketMapping<?>> mappings;
private boolean dense = false;
public PacketRegistryBuilder() {
this.mappings = new Int2ObjectOpenHashMap<>();
}
public PacketRegistryBuilder(
Int2ObjectMap<PacketMapping<?>> mappings, boolean dense) {
this.mappings = new Int2ObjectOpenHashMap<>(mappings);
this.dense = dense;
}
public <P extends Packet> PacketRegistryBuilder register(int id, Class<P> packetClass,
PacketWriter<P> writer) {
mappings.put(id, new PacketMapping(id, packetClass, writer, null));
return this;
}
public <P extends Packet> PacketRegistryBuilder register(int id, Class<P> packetClass,
PacketWriter<P> writer, PacketReader<P> reader) {
mappings.put(id, new PacketMapping(id, packetClass, writer, reader));
return this;
}
public PacketRegistryBuilder dense() {
this.dense = true;
return this;
}
public PacketRegistryBuilder copy() {
return new PacketRegistryBuilder(this.mappings, this.dense);
}
public PacketRegistryMap build() {
if (this.dense) {
return new DensePacketRegistryMap(mappings);
} else {
return new RegularPacketRegistryMap(mappings);
}
}
static final class PacketMapping<P extends Packet> {
int id;
final Class<P> packetClass;
final PacketWriter<P> writer;
final @Nullable PacketReader<P> reader;
PacketMapping(int id, Class<P> packetClass,
PacketWriter<P> writer,
@Nullable PacketReader<P> reader) {
this.id = id;
this.packetClass = packetClass;
this.writer = writer;
this.reader = reader;
}
}
}

Datei anzeigen

@ -0,0 +1,12 @@
package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface PacketRegistryMap {
@Nullable Packet readPacket(final int id, ByteBuf buf, ProtocolVersion version);
<P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version);
}

Datei anzeigen

@ -0,0 +1,66 @@
package com.velocitypowered.proxy.network.registry.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketReader;
import com.velocitypowered.proxy.network.packet.PacketWriter;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryBuilder.PacketMapping;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* The canonical implementation of the packet registry map.
*/
public class RegularPacketRegistryMap implements PacketRegistryMap {
private final Int2ObjectMap<PacketReader<?>> readersById;
private final Int2ObjectMap<PacketWriter<?>> writersById;
private final Object2IntMap<Class<?>> classesById;
public RegularPacketRegistryMap(Int2ObjectMap<PacketMapping<?>> mappings) {
int size = mappings.size();
this.readersById = new Int2ObjectOpenHashMap<>(size);
this.writersById = new Int2ObjectOpenHashMap<>(size);
this.classesById = new Object2IntOpenHashMap<>(size);
this.classesById.defaultReturnValue(Integer.MIN_VALUE);
for (PacketMapping<?> value : mappings.values()) {
if (value.reader != null) {
this.readersById.put(value.id, value.reader);
}
this.writersById.put(value.id, value.writer);
this.classesById.put(value.packetClass, value.id);
}
}
@Override
public @Nullable Packet readPacket(int id, ByteBuf buf, ProtocolVersion version) {
PacketReader<?> reader = this.readersById.get(id);
if (reader == null) {
return null;
}
return reader.read(buf, version);
}
@Override
public <P extends Packet> void writePacket(P packet, ByteBuf buf, ProtocolVersion version) {
int packetId = this.classesById.getInt(packet.getClass());
if (packetId == Integer.MIN_VALUE) {
throw new IllegalArgumentException(String.format(
"Unable to find id for packet of type %s in protocol %s",
packet.getClass().getName(), version
));
}
PacketWriter writer = this.writersById.get(packetId);
assert writer != null;
writer.write(buf, packet, version);
}
}

Datei anzeigen

@ -0,0 +1,9 @@
package com.velocitypowered.proxy.network.registry.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.PacketDirection;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap;
public interface ProtocolRegistry {
PacketRegistryMap lookup(PacketDirection direction, ProtocolVersion version);
}

Datei anzeigen

@ -0,0 +1,32 @@
package com.velocitypowered.proxy.network.registry.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.PacketDirection;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap;
/**
* A flat protocol registry that does not care about the protocol version.
*/
public class SimpleProtocolRegistry implements ProtocolRegistry {
private final PacketRegistryMap serverbound;
private final PacketRegistryMap clientbound;
public SimpleProtocolRegistry(
PacketRegistryMap serverbound,
PacketRegistryMap clientbound) {
this.serverbound = serverbound;
this.clientbound = clientbound;
}
@Override
public PacketRegistryMap lookup(PacketDirection direction, ProtocolVersion version) {
if (direction == PacketDirection.SERVERBOUND) {
return this.serverbound;
} else if (direction == PacketDirection.CLIENTBOUND) {
return this.clientbound;
} else {
throw new NullPointerException("direction");
}
}
}

Datei anzeigen

@ -0,0 +1,42 @@
package com.velocitypowered.proxy.network.registry.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.PacketDirection;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
/**
* A version-aware protocol registry.
*/
public class VersionSpecificProtocolRegistry implements ProtocolRegistry {
private final Map<ProtocolVersion, PacketRegistryMap> serverboundByVersion;
private final Map<ProtocolVersion, PacketRegistryMap> clientboundByVersion;
public VersionSpecificProtocolRegistry() {
this.serverboundByVersion = new EnumMap<>(ProtocolVersion.class);
this.clientboundByVersion = new EnumMap<>(ProtocolVersion.class);
}
public VersionSpecificProtocolRegistry register(ProtocolVersion min, ProtocolVersion max,
PacketRegistryMap serverbound, PacketRegistryMap clientbound) {
for (ProtocolVersion version : EnumSet.range(min, max)) {
this.serverboundByVersion.put(version, serverbound);
this.clientboundByVersion.put(version, clientbound);
}
return this;
}
@Override
public PacketRegistryMap lookup(PacketDirection direction, ProtocolVersion version) {
if (direction == PacketDirection.SERVERBOUND) {
return this.serverboundByVersion.get(version);
} else if (direction == PacketDirection.CLIENTBOUND) {
return this.clientboundByVersion.get(version);
} else {
throw new NullPointerException("direction");
}
}
}

Datei anzeigen

@ -0,0 +1,57 @@
package com.velocitypowered.proxy.network.registry.state;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundStatusPingPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundStatusResponsePacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundHandshakePacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundStatusPingPacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundStatusRequestPacket;
import com.velocitypowered.proxy.network.registry.packet.EmptyPacketRegistryMap;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryBuilder;
import com.velocitypowered.proxy.network.registry.protocol.ProtocolRegistry;
import com.velocitypowered.proxy.network.registry.protocol.SimpleProtocolRegistry;
import com.velocitypowered.proxy.network.registry.protocol.VersionSpecificProtocolRegistry;
public class ProtocolStates {
public static final ProtocolRegistry HANDSHAKE;
public static final ProtocolRegistry STATUS;
public static final ProtocolRegistry LOGIN;
static {
HANDSHAKE = new SimpleProtocolRegistry(
new PacketRegistryBuilder()
.dense()
.register(0x00, ServerboundHandshakePacket.class, ServerboundHandshakePacket.ENCODER,
ServerboundHandshakePacket.DECODER)
.build(),
EmptyPacketRegistryMap.INSTANCE);
STATUS = new SimpleProtocolRegistry(
new PacketRegistryBuilder()
.dense()
.register(0x00, ServerboundStatusRequestPacket.class,
ServerboundStatusRequestPacket.ENCODER,
ServerboundStatusRequestPacket.DECODER)
.register(0x01, ServerboundStatusPingPacket.class,
ServerboundStatusPingPacket.ENCODER,
ServerboundStatusPingPacket.DECODER)
.build(),
new PacketRegistryBuilder()
.dense()
.register(0x00, ClientboundStatusResponsePacket.class,
ClientboundStatusResponsePacket.ENCODER,
ClientboundStatusResponsePacket.DECODER)
.register(0x01, ClientboundStatusPingPacket.class,
ClientboundStatusPingPacket.ENCODER,
ClientboundStatusPingPacket.DECODER)
.build());
LOGIN = new VersionSpecificProtocolRegistry()
.register(ProtocolVersion.MINECRAFT_1_7_2, ProtocolVersion.MINECRAFT_1_8,
Version172To176.SERVERBOUND_LOGIN, Version172To176.CLIENTBOUND_LOGIN);
}
private ProtocolStates() {
throw new AssertionError();
}
}

Datei anzeigen

@ -0,0 +1,39 @@
package com.velocitypowered.proxy.network.registry.state;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundDisconnectPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundEncryptionRequestPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundServerLoginSuccessPacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundEncryptionResponsePacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundServerLoginPacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundTabCompleteRequestPacket;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryBuilder;
import com.velocitypowered.proxy.network.registry.packet.PacketRegistryMap;
public class Version172To176 {
private Version172To176() {
throw new AssertionError();
}
public static final PacketRegistryMap SERVERBOUND_LOGIN = new PacketRegistryBuilder()
.dense()
.register(0x00, ServerboundServerLoginPacket.class, ServerboundServerLoginPacket.ENCODER,
ServerboundServerLoginPacket.DECODER)
.register(0x01, ServerboundEncryptionResponsePacket.class,
ServerboundEncryptionResponsePacket.ENCODER, ServerboundEncryptionResponsePacket.DECODER)
.build();
public static final PacketRegistryMap CLIENTBOUND_LOGIN = new PacketRegistryBuilder()
.dense()
.register(0x00, ClientboundDisconnectPacket.class, ClientboundDisconnectPacket.ENCODER,
ClientboundDisconnectPacket.DECODER)
.register(0x01, ClientboundEncryptionRequestPacket.class,
ClientboundEncryptionRequestPacket.ENCODER, ClientboundEncryptionRequestPacket.DECODER)
.register(0x02, ClientboundServerLoginSuccessPacket.class,
ClientboundServerLoginSuccessPacket.ENCODER, ClientboundServerLoginSuccessPacket.DECODER)
.build();
public static final PacketRegistryMap SERVERBOUND_PLAY = new PacketRegistryBuilder()
.register(0x14, ServerboundTabCompleteRequestPacket.class,
ServerboundTabCompleteRequestPacket.ENCODER, ServerboundTabCompleteRequestPacket.DECODER)
.build();
}

Datei anzeigen

@ -0,0 +1,5 @@
package com.velocitypowered.proxy.network.registry.version;
public class GenericVersions {
}