Small refactors around ProtocolPipeline and concurrent collections

Make concurrency handling in ProtocolPipelineImpl more defensive, as generally the pipeline is expected to never be called from multiple threads. The only case to look out for is pipeline additions during protocol transformation in a base protocol
This commit is contained in:
Nassim Jahnke 2024-02-19 22:37:51 +01:00
parent 343b403cf1
commit 4e1d4a75b2
No known key found for this signature in database
GPG Key ID: EF6771C01F6EF02F
17 changed files with 235 additions and 207 deletions

View File

@ -16,8 +16,6 @@ sourceSets {
dependencies {
api(libs.fastutil)
api(libs.flare)
api(libs.flareFastutil)
api(libs.vianbt)
api(libs.gson)
implementation(rootProject.libs.text) {

View File

@ -66,7 +66,7 @@ public interface ViaAPI<T> {
* @return API version incremented with meaningful API changes
*/
default int apiVersion() {
return 24;
return 25;
}
/**

View File

@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.protocol;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import java.util.Collection;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -61,25 +62,34 @@ public interface ProtocolPipeline extends SimpleProtocol {
* @param pipeClass protocol class
* @param <P> protocol
* @return protocol from class
* @see #contains(Class)
* @see ProtocolManager#getProtocol(Class) for a faster implementation
* @deprecated use {@link ProtocolManager#getProtocol(Class)} and/or {@link #contains(Class)}
*/
@Deprecated
@Nullable <P extends Protocol> P getProtocol(Class<P> pipeClass);
List<Protocol> pipes(@Nullable Class<? extends Protocol> protocolClass, boolean skipCurrentPipeline, Direction direction);
/**
* Returns the list of protocols this pipeline contains.
* Returns the list of protocols this pipeline contains, lead by base protocols.
*
* @return immutable list of protocols in this pipe
*/
List<Protocol> pipes();
/**
* Returns the list of protocols this pipeline contains in reversed order.
* Returns the list of protocols this pipeline contains in reversed order, although still lead by base protocols.
*
* @return immutable list of protocols in reversed direction
*/
List<Protocol> reversedPipes();
/**
* Returns the number of base protocols in this pipeline.
*
* @return the number of base protocols in this pipeline
*/
int baseProtocolCount();
/**
* Returns whether this pipe has protocols that are not base protocols, as given by {@link Protocol#isBaseProtocol()}.
*

View File

@ -92,6 +92,7 @@ public interface PacketWrapper {
* @param index The index of the part (relative to the type)
* @return True if the type is at the index
*/
@Deprecated
boolean is(Type type, int index);
/**
@ -212,11 +213,11 @@ public interface PacketWrapper {
* (Sends it after current)
* Also returns the packets ChannelFuture
*
* @param packetProtocol The protocol version of the packet.
* @return The packets ChannelFuture
* @param protocolClass the protocol class to start from in the pipeline
* @return new ChannelFuture for the write operation
* @throws Exception if it fails to write
*/
ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception;
ChannelFuture sendFuture(Class<? extends Protocol> protocolClass) throws Exception;
/**
* @deprecated misleading; use {@link #sendRaw()}. This method will be removed in 5.0.0
@ -287,18 +288,24 @@ public interface PacketWrapper {
*
* @param direction protocol direction
* @param state protocol state
* @param index index to start from, will be reversed depending on the reverse parameter
* @param pipeline protocol pipeline
* @param reverse whether the array should be looped in reverse, will also reverse the given index
* @return The current packetwrapper
* @throws Exception If it fails to transform a packet, exception will be thrown
*/
void apply(Direction direction, State state, List<Protocol> pipeline) throws Exception;
/**
* @deprecated use {@link #apply(Direction, State, List)}
*/
@Deprecated
PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline, boolean reverse) throws Exception;
/**
* @see #apply(Direction, State, int, List, boolean)
* @deprecated use {@link #apply(Direction, State, List)}
*/
PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception;
@Deprecated
default PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception {
return apply(direction, state, index, pipeline, false);
}
/**
* Check if this packet is cancelled.

View File

@ -25,7 +25,6 @@ fun ShadowJar.configureRelocations() {
relocate("com.google.gson", "com.viaversion.viaversion.libs.gson")
relocate("com.github.steveice10.opennbt", "com.viaversion.viaversion.libs.opennbt")
relocate("it.unimi.dsi.fastutil", "com.viaversion.viaversion.libs.fastutil")
relocate("space.vectrix.flare", "com.viaversion.viaversion.libs.flare")
relocate("net.lenni0451.mcstructs", "com.viaversion.viaversion.libs.mcstructs")
}
@ -49,9 +48,4 @@ fun ShadowJar.configureExcludes() {
exclude("it/unimi/dsi/fastutil/*/*Big*")
exclude("it/unimi/dsi/fastutil/*/*Synchronized*")
exclude("it/unimi/dsi/fastutil/*/*Unmodifiable*")
// Flare - only need int maps
exclude("space/vectrix/flare/fastutil/*Double*")
exclude("space/vectrix/flare/fastutil/*Float*")
exclude("space/vectrix/flare/fastutil/*Long*")
exclude("space/vectrix/flare/fastutil/*Short*")
}

View File

@ -44,6 +44,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.ServerConnectEvent;
@ -194,18 +195,15 @@ public class BungeeServerHandler implements Listener {
ProtocolPipeline pipeline = user.getProtocolInfo().getPipeline();
user.clearStoredObjects(true);
pipeline.cleanPipes();
if (protocolPath == null) {
if (protocolPath != null) {
info.setServerProtocolVersion(serverProtocolVersion);
pipeline.add(protocolPath.stream().map(ProtocolPathEntry::protocol).collect(Collectors.toList()));
} else {
// TODO Check Bungee Supported Protocols? *shrugs*
serverProtocolVersion = info.protocolVersion();
} else {
List<Protocol> protocols = new ArrayList<>(protocolPath.size());
for (ProtocolPathEntry entry : protocolPath) {
protocols.add(entry.protocol());
}
pipeline.add(protocols);
info.setServerProtocolVersion(serverProtocolVersion);
}
info.setServerProtocolVersion(serverProtocolVersion);
// Add version-specific base Protocol
pipeline.add(Via.getManager().getProtocolManager().getBaseProtocol(serverProtocolVersion));
@ -254,11 +252,6 @@ public class BungeeServerHandler implements Listener {
user.setActive(protocolPath != null);
// Init all protocols TODO check if this can get moved up to the previous for loop, and doesn't require the pipeline to already exist.
for (Protocol protocol : pipeline.pipes()) {
protocol.init(user);
}
ProxiedPlayer player = storage.getPlayer();
EntityTracker1_9 newTracker = user.getEntityTracker(Protocol1_9To1_8.class);
if (newTracker != null && Via.getConfig().isAutoTeam()) {

View File

@ -28,13 +28,13 @@ import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Collections;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
import space.vectrix.flare.fastutil.Int2ObjectSyncMap;
public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeListener {
private final Int2ObjectMap<TrackedEntity> entities = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<TrackedEntity> entities = new Int2ObjectOpenHashMap<>();
private final UserConnection connection;
private final EntityType playerType;
private int clientEntityId = -1;

View File

@ -18,6 +18,7 @@
package com.viaversion.viaversion.protocol;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.debug.DebugHandler;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
@ -31,19 +32,16 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements ProtocolPipeline {
private final UserConnection userConnection;
/**
* Protocol list ordered from client to server transformation with the base protocols at the end.
*/
private final List<Protocol> protocolList = new CopyOnWriteArrayList<>();
private final List<Protocol> protocolList = new ArrayList<>();
private final Set<Class<? extends Protocol>> protocolSet = new HashSet<>();
private List<Protocol> reversedProtocolList = new CopyOnWriteArrayList<>();
private final UserConnection userConnection;
private List<Protocol> reversedProtocolList = new ArrayList<>();
private int baseProtocols;
public ProtocolPipelineImpl(UserConnection userConnection) {
@ -53,13 +51,9 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
protected synchronized void registerPackets() {
protected void registerPackets() {
// This is a pipeline so we register basic pipes
final Protocol<?, ?, ?, ?> baseProtocol = Via.getManager().getProtocolManager().getBaseProtocol();
protocolList.add(baseProtocol);
reversedProtocolList.add(baseProtocol);
protocolSet.add(baseProtocol.getClass());
baseProtocols++;
this.add(Via.getManager().getProtocolManager().getBaseProtocol());
}
@Override
@ -68,15 +62,14 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
public synchronized void add(final Protocol protocol) {
public void add(final Protocol protocol) {
reversedProtocolList.add(baseProtocols, protocol);
if (protocol.isBaseProtocol()) {
// Add base protocol on top of previous ones
protocolList.add(baseProtocols, protocol);
reversedProtocolList.add(baseProtocols, protocol);
baseProtocols++;
} else {
protocolList.add(protocol);
reversedProtocolList.add(0, protocol);
}
protocolSet.add(protocol.getClass());
@ -84,22 +77,32 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
public synchronized void add(final Collection<Protocol> protocols) {
protocolList.addAll(protocols);
public void add(final Collection<Protocol> protocols) {
for (final Protocol protocol : protocols) {
if (protocol.isBaseProtocol()) {
throw new UnsupportedOperationException("Base protocols cannot be added in bulk");
}
protocol.init(userConnection);
protocolSet.add(protocol.getClass());
}
protocolList.addAll(protocols);
refreshReversedList();
}
private synchronized void refreshReversedList() {
final List<Protocol> protocols = new ArrayList<>(protocolList.subList(0, this.baseProtocols));
final List<Protocol> additionalProtocols = new ArrayList<>(protocolList.subList(this.baseProtocols, protocolList.size()));
Collections.reverse(additionalProtocols);
protocols.addAll(additionalProtocols);
reversedProtocolList = new CopyOnWriteArrayList<>(protocols);
private void refreshReversedList() {
final List<Protocol> reversedProtocols = new ArrayList<>(protocolList.size());
// Add base protocols in regular order first
for (int i = 0; i < baseProtocols; i++) {
reversedProtocols.add(protocolList.get(i));
}
// Add non-base protocols in reverse order
for (int i = protocolList.size() - 1; i >= baseProtocols; i--) {
reversedProtocols.add(protocolList.get(i));
}
reversedProtocolList = reversedProtocols;
}
@Override
@ -113,7 +116,7 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
// Apply protocols
packetWrapper.apply(direction, state, 0, protocolListFor(direction));
packetWrapper.apply(direction, state, protocolListFor(direction));
super.transform(direction, state, packetWrapper);
if (debug && debugHandler.logPostPacketTransform() && debugHandler.shouldLog(packetWrapper, direction)) {
@ -126,20 +129,21 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
private void logPacket(Direction direction, State state, PacketWrapper packetWrapper, int originalID) {
String actualUsername = packetWrapper.user().getProtocolInfo().getUsername();
ProtocolInfo protocolInfo = userConnection.getProtocolInfo();
String actualUsername = protocolInfo.getUsername();
String username = actualUsername != null ? actualUsername + " " : "";
Via.getPlatform().getLogger().log(Level.INFO, "{0}{1} {2}: {3} ({4}) -> {5} ({6}) [{7}] {8}",
new Object[]{
username,
direction,
state,
originalID,
AbstractSimpleProtocol.toNiceHex(originalID),
packetWrapper.getId(),
AbstractSimpleProtocol.toNiceHex(packetWrapper.getId()),
userConnection.getProtocolInfo().protocolVersion().getName(),
packetWrapper
});
new Object[]{
username,
direction,
state,
originalID,
AbstractSimpleProtocol.toNiceHex(originalID),
packetWrapper.getId(),
AbstractSimpleProtocol.toNiceHex(packetWrapper.getId()),
protocolInfo.protocolVersion().getName(),
packetWrapper
});
}
@Override
@ -157,6 +161,49 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
return null;
}
@Override
public List<Protocol> pipes(@Nullable final Class<? extends Protocol> protocolClass, final boolean skipCurrentPipeline, final Direction direction) {
final List<Protocol> protocolList = this.protocolListFor(direction);
final int index = indexOf(protocolClass, skipCurrentPipeline, protocolList);
final List<Protocol> pipes = new ArrayList<>(baseProtocols + protocolList.size() - index);
// Always add base protocols to the head
for (int i = 0, size = Math.min(index, baseProtocols); i < size; i++) {
pipes.add(protocolList.get(i));
}
// Add remaining protocols on top
for (int i = index, size = protocolList.size(); i < size; i++) {
pipes.add(protocolList.get(i));
}
return pipes;
}
private int indexOf(@Nullable Class<? extends Protocol> protocolClass, boolean skipCurrentPipeline, List<Protocol> protocolList) {
if (protocolClass == null) {
return 0;
}
// Find the index of the given protocol
int index = -1;
for (int i = 0; i < protocolList.size(); i++) {
if (protocolList.get(i).getClass() == protocolClass) {
index = i;
break;
}
}
if (index == -1) {
// The given protocol is not in the pipeline
throw new NoSuchElementException(protocolClass.getCanonicalName());
}
if (skipCurrentPipeline) {
index = Math.min(index + 1, protocolList.size());
}
return index;
}
@Override
public List<Protocol> pipes() {
return Collections.unmodifiableList(protocolList);
@ -168,17 +215,17 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
public boolean hasNonBaseProtocols() {
for (Protocol protocol : protocolList) {
if (!protocol.isBaseProtocol()) {
return true;
}
}
return false;
public int baseProtocolCount() {
return baseProtocols;
}
@Override
public synchronized void cleanPipes() {
public boolean hasNonBaseProtocols() {
return protocolList.size() > baseProtocols;
}
@Override
public void cleanPipes() {
protocolList.clear();
reversedProtocolList.clear();
protocolSet.clear();
@ -190,7 +237,7 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
@Override
public String toString() {
return "ProtocolPipelineImpl{" +
"protocolList=" + protocolList +
'}';
"protocolList=" + protocolList +
'}';
}
}

View File

@ -39,13 +39,10 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
public class PacketWrapperImpl implements PacketWrapper {
private static final Protocol[] PROTOCOL_ARRAY = new Protocol[0];
private final Deque<PacketValue<?>> readableObjects = new ArrayDeque<>();
private final List<PacketValue<?>> packetValues = new ArrayList<>();
private final ByteBuf inputBuffer;
@ -148,8 +145,8 @@ public class PacketWrapperImpl implements PacketWrapper {
PacketValue readValue = readableObjects.poll();
Type<?> readType = readValue.type();
if (readType == type
|| (type.getBaseClass() == readType.getBaseClass()
&& type.getOutputClass() == readType.getOutputClass())) {
|| (type.getBaseClass() == readType.getBaseClass()
&& type.getOutputClass() == readType.getOutputClass())) {
//noinspection unchecked
return (T) readValue.value();
} else {
@ -210,25 +207,24 @@ public class PacketWrapperImpl implements PacketWrapper {
readableObjects.clear();
}
int index = 0;
for (final PacketValue<?> packetValue : packetValues) {
for (int i = 0; i < packetValues.size(); i++) {
PacketValue<?> packetValue = packetValues.get(i);
try {
packetValue.write(buffer);
} catch (final Exception e) {
throw createInformativeException(e, packetValue.type(), index);
throw createInformativeException(e, packetValue.type(), i);
}
index++;
}
writeRemaining(buffer);
}
private InformativeException createInformativeException(final Exception cause, final Type<?> type, final int index) {
return new InformativeException(cause)
.set("Index", index)
.set("Type", type.getTypeName())
.set("Packet ID", this.id)
.set("Packet Type", this.packetType)
.set("Data", this.packetValues);
.set("Index", index)
.set("Type", type.getTypeName())
.set("Packet ID", this.id)
.set("Packet Type", this.packetType)
.set("Data", this.packetValues);
}
@Override
@ -298,46 +294,17 @@ public class PacketWrapperImpl implements PacketWrapper {
/**
* Let the packet go through the protocol pipes and write it to ByteBuf
*
* @param packetProtocol The protocol version of the packet.
* @param skipCurrentPipeline Skip the current pipeline
* @return Packet buffer
* @param protocolClass protocol class to send the packet from, or null to go through the full pipeline
* @param skipCurrentPipeline whether to start from the next protocol in the pipeline, or the provided one
* @return created packet buffer
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, Direction direction) throws Exception {
private ByteBuf constructPacket(@Nullable Class<? extends Protocol> protocolClass, boolean skipCurrentPipeline, Direction direction) throws Exception {
resetReader(); // Reset reader before we start
final ProtocolInfo protocolInfo = user().getProtocolInfo();
final List<Protocol> pipes = direction == Direction.SERVERBOUND ? protocolInfo.getPipeline().pipes() : protocolInfo.getPipeline().reversedPipes();
final List<Protocol> protocols = new ArrayList<>();
int index = -1;
for (int i = 0; i < pipes.size(); i++) {
// Always add base protocols to the head
final Protocol protocol = pipes.get(i);
if (protocol.isBaseProtocol()) {
protocols.add(protocol);
}
if (protocol.getClass() == packetProtocol) {
index = i;
break;
}
}
if (index == -1) {
// The given protocol is not in the pipeline
throw new NoSuchElementException(packetProtocol.getCanonicalName());
}
if (skipCurrentPipeline) {
index = Math.min(index + 1, pipes.size());
}
// Add remaining protocols on top
protocols.addAll(pipes.subList(index, pipes.size()));
// Reset reader before we start
resetReader();
// Apply other protocols
apply(direction, protocolInfo.getState(direction), 0, protocols);
final List<Protocol> protocols = protocolInfo.getPipeline().pipes(protocolClass, skipCurrentPipeline, direction);
apply(direction, protocolInfo.getState(direction), protocols);
final ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
@ -348,9 +315,9 @@ public class PacketWrapperImpl implements PacketWrapper {
}
@Override
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
public ChannelFuture sendFuture(Class<? extends Protocol> protocolClass) throws Exception {
if (!isCancelled()) {
ByteBuf output = constructPacket(packetProtocol, true, Direction.CLIENTBOUND);
ByteBuf output = constructPacket(protocolClass, true, Direction.CLIENTBOUND);
return user().sendRawPacketFuture(output);
}
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
@ -397,33 +364,36 @@ public class PacketWrapperImpl implements PacketWrapper {
}
@Override
public PacketWrapperImpl apply(Direction direction, State state, int index, List<Protocol> pipeline, boolean reverse) throws Exception {
Protocol[] array = pipeline.toArray(PROTOCOL_ARRAY);
return apply(direction, state, reverse ? array.length - 1 : index, array, reverse); // Copy to prevent from removal
public void apply(Direction direction, State state, List<Protocol> pipeline) throws Exception {
// Indexed loop to allow additions to the tail
for (int i = 0, size = pipeline.size(); i < size; i++) {
Protocol<?, ?, ?, ?> protocol = pipeline.get(i);
protocol.transform(direction, state, this);
resetReader();
if (this.packetType != null) {
state = this.packetType.state();
}
}
}
@Override
public PacketWrapperImpl apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception {
return apply(direction, state, index, pipeline.toArray(PROTOCOL_ARRAY), false);
}
private PacketWrapperImpl apply(Direction direction, State state, int index, Protocol[] pipeline, boolean reverse) throws Exception {
@Deprecated
public PacketWrapperImpl apply(Direction direction, State state, int index, List<Protocol> pipeline, boolean reverse) throws Exception {
// Reset the reader after every transformation for the packetWrapper, so it can be recycled across packets
State updatedState = state; // The state might change while transforming, so we need to check for that
if (reverse) {
for (int i = index; i >= 0; i--) {
pipeline[i].transform(direction, updatedState, this);
pipeline.get(i).transform(direction, state, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
state = this.packetType.state();
}
}
} else {
for (int i = index; i < pipeline.length; i++) {
pipeline[i].transform(direction, updatedState, this);
for (int i = index; i < pipeline.size(); i++) {
pipeline.get(i).transform(direction, state, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
state = this.packetType.state();
}
}
}
@ -447,7 +417,7 @@ public class PacketWrapperImpl implements PacketWrapper {
@Override
public void resetReader() {
// Move all packet values to the readable for next packet.
// Move all packet values to the readable for next Protocol
for (int i = packetValues.size() - 1; i >= 0; i--) {
this.readableObjects.addFirst(this.packetValues.get(i));
}
@ -557,11 +527,11 @@ public class PacketWrapperImpl implements PacketWrapper {
@Override
public String toString() {
return "PacketWrapper{" +
"type=" + packetType +
", id=" + id +
", values=" + packetValues +
", readable=" + readableObjects +
'}';
"type=" + packetType +
", id=" + id +
", values=" + packetValues +
", readable=" + readableObjects +
'}';
}
public static final class PacketValue<T> {

View File

@ -33,6 +33,7 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.Nullable;
public class VersionedPacketTransformerImpl<C extends ClientboundPacketType, S extends ServerboundPacketType> implements VersionedPacketTransformer<C, S> {
@ -44,7 +45,7 @@ public class VersionedPacketTransformerImpl<C extends ClientboundPacketType, S e
public VersionedPacketTransformerImpl(ProtocolVersion inputVersion, @Nullable Class<C> clientboundPacketsClass, @Nullable Class<S> serverboundPacketsClass) {
Preconditions.checkNotNull(inputVersion);
Preconditions.checkArgument(clientboundPacketsClass != null || serverboundPacketsClass != null,
"Either the clientbound or serverbound packets class has to be non-null");
"Either the clientbound or serverbound packets class has to be non-null");
this.inputProtocolVersion = inputVersion;
this.clientboundPacketsClass = clientboundPacketsClass;
this.serverboundPacketsClass = serverboundPacketsClass;
@ -108,7 +109,7 @@ public class VersionedPacketTransformerImpl<C extends ClientboundPacketType, S e
}
Class<? extends PacketType> expectedPacketClass =
packet.getPacketType().direction() == Direction.CLIENTBOUND ? clientboundPacketsClass : serverboundPacketsClass;
packet.getPacketType().direction() == Direction.CLIENTBOUND ? clientboundPacketsClass : serverboundPacketsClass;
if (packet.getPacketType().getClass() != expectedPacketClass) {
throw new IllegalArgumentException("PacketWrapper packet type is of the wrong packet class");
}
@ -139,34 +140,40 @@ public class VersionedPacketTransformerImpl<C extends ClientboundPacketType, S e
private void transformPacket(PacketWrapper packet) throws Exception {
// If clientbound: Constructor given inputProtocolVersion Client version
// If serverbound: Constructor given inputProtocolVersion Server version
PacketType packetType = packet.getPacketType();
UserConnection connection = packet.user();
PacketType packetType = packet.getPacketType();
boolean clientbound = packetType.direction() == Direction.CLIENTBOUND;
ProtocolVersion serverProtocolVersion = clientbound ? this.inputProtocolVersion : connection.getProtocolInfo().serverProtocolVersion();
ProtocolVersion clientProtocolVersion = clientbound ? connection.getProtocolInfo().protocolVersion() : this.inputProtocolVersion;
// Construct protocol pipeline
List<ProtocolPathEntry> path = Via.getManager().getProtocolManager().getProtocolPath(clientProtocolVersion, serverProtocolVersion);
List<Protocol> protocolList = null;
if (path != null) {
protocolList = new ArrayList<>(path.size());
if (path == null) {
if (serverProtocolVersion != clientProtocolVersion) {
throw new RuntimeException("No protocol path between client version " + clientProtocolVersion + " and server version " + serverProtocolVersion);
}
return;
}
final List<Protocol> protocolList = new ArrayList<>(path.size());
if (clientbound) {
for (int i = path.size() - 1; i >= 0; i--) {
protocolList.add(path.get(i).protocol());
}
} else {
for (ProtocolPathEntry entry : path) {
protocolList.add(entry.protocol());
}
} else if (serverProtocolVersion != clientProtocolVersion) {
throw new RuntimeException("No protocol path between client version " + clientProtocolVersion + " and server version " + serverProtocolVersion);
}
if (protocolList != null) {
// Reset reader and apply pipeline
packet.resetReader();
// Reset reader and apply pipeline
packet.resetReader();
try {
packet.apply(packetType.direction(), State.PLAY, 0, protocolList, clientbound);
} catch (Exception e) {
throw new Exception("Exception trying to transform packet between client version " + clientProtocolVersion
+ " and server version " + serverProtocolVersion + ". Are you sure you used the correct input version and packet write types?", e);
}
try {
packet.apply(packetType.direction(), packetType.state(), protocolList);
} catch (Exception e) {
throw new Exception("Exception trying to transform packet between client version " + clientProtocolVersion
+ " and server version " + serverProtocolVersion + ". Are you sure you used the correct input version and packet write types?", e);
}
}

View File

@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.Direction;
@ -72,18 +73,25 @@ public class BaseProtocol extends AbstractProtocol<BaseClientboundPacket, BaseCl
List<ProtocolPathEntry> protocolPath = null;
// Only allow newer clients (or 1.9.2 on 1.9.4 server if the server supports it)
ProtocolManager protocolManager = Via.getManager().getProtocolManager();
if (info.protocolVersion().newerThanOrEqualTo(serverProtocol) || Via.getPlatform().isOldClientsAllowed()) {
protocolPath = Via.getManager().getProtocolManager().getProtocolPath(info.protocolVersion(), serverProtocol);
protocolPath = protocolManager.getProtocolPath(info.protocolVersion(), serverProtocol);
}
ProtocolPipeline pipeline = wrapper.user().getProtocolInfo().getPipeline();
// Add Base Protocol
ProtocolPipeline pipeline = info.getPipeline();
if (serverProtocol.getVersionType() != VersionType.SPECIAL) {
pipeline.add(protocolManager.getBaseProtocol(serverProtocol));
}
// Add other protocols
if (protocolPath != null) {
List<Protocol> protocols = new ArrayList<>(protocolPath.size());
for (ProtocolPathEntry entry : protocolPath) {
protocols.add(entry.protocol());
// Ensure mapping data has already been loaded
Via.getManager().getProtocolManager().completeMappingDataLoading(entry.protocol().getClass());
protocolManager.completeMappingDataLoading(entry.protocol().getClass());
}
// Add protocols to pipeline
@ -93,11 +101,6 @@ public class BaseProtocol extends AbstractProtocol<BaseClientboundPacket, BaseCl
wrapper.set(Type.VAR_INT, 0, serverProtocol.getOriginalVersion());
}
// Add Base Protocol
if (!serverProtocol.getVersionType().equals(VersionType.SPECIAL)) {
pipeline.add(Via.getManager().getProtocolManager().getBaseProtocol(serverProtocol));
}
if (Via.getManager().isDebug()) {
Via.getPlatform().getLogger().info("User connected with protocol: " + info.protocolVersion() + " and serverProtocol: " + info.serverProtocolVersion());
Via.getPlatform().getLogger().info("Protocol pipeline: " + pipeline.pipes());

View File

@ -168,7 +168,7 @@ public class BaseProtocol1_7 extends AbstractProtocol<BaseClientboundPacket, Bas
wrapper.cancel(); // cancel current
// Send and close
ChannelFuture future = disconnectPacket.sendFuture(BaseProtocol.class);
ChannelFuture future = disconnectPacket.sendFuture(null);
future.addListener(f -> wrapper.user().getChannel().close());
}
});

View File

@ -20,11 +20,11 @@ package com.viaversion.viaversion.protocols.protocol1_11to1_10.storage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11.EntityType;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import space.vectrix.flare.fastutil.Int2ObjectSyncMap;
public class EntityTracker1_11 extends EntityTrackerBase {
private final IntSet holograms = Int2ObjectSyncMap.hashset();
private final IntSet holograms = new IntOpenHashSet();
public EntityTracker1_11(UserConnection user) {
super(user, EntityType.PLAYER);

View File

@ -21,13 +21,13 @@ import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.HashMap;
import java.util.Map;
import space.vectrix.flare.SyncMap;
// TODO Fix memory leak lolz (only a smol one tho)
public class BlockStorage implements StorableObject {
private static final IntSet WHITELIST = new IntOpenHashSet(46, .99F);
private final Map<Position, ReplacementData> blocks = SyncMap.hashmap();
private final Map<Position, ReplacementData> blocks = new HashMap<>();
static {
// Flower pots

View File

@ -21,13 +21,13 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import space.vectrix.flare.fastutil.Int2ObjectSyncMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class EntityTracker1_14 extends EntityTrackerBase {
private final Int2ObjectMap<Byte> insentientData = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<Byte> insentientData = new Int2ObjectOpenHashMap<>();
// 0x1 = sleeping, 0x2 = riptide
private final Int2ObjectMap<Byte> sleepingAndRiptideData = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<Byte> playerEntityFlags = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<Byte> sleepingAndRiptideData = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<Byte> playerEntityFlags = new Int2ObjectOpenHashMap<>();
private int latestTradeWindowId;
private boolean forceSendCenterChunk = true;
private int chunkCenterX;

View File

@ -39,26 +39,28 @@ import com.viaversion.viaversion.protocols.protocol1_9to1_8.chat.GameMode;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.metadata.MetadataRewriter1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.BossBarProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import space.vectrix.flare.fastutil.Int2ObjectSyncMap;
public class EntityTracker1_9 extends EntityTrackerBase {
public static final String WITHER_TRANSLATABLE = "{\"translate\":\"entity.WitherBoss.name\"}";
public static final String DRAGON_TRANSLATABLE = "{\"translate\":\"entity.EnderDragon.name\"}";
private final Int2ObjectMap<UUID> uuidMap = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<List<Metadata>> metadataBuffer = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<Integer> vehicleMap = Int2ObjectSyncMap.hashmap();
private final Int2ObjectMap<BossBar> bossBarMap = Int2ObjectSyncMap.hashmap();
private final IntSet validBlocking = Int2ObjectSyncMap.hashset();
private final Set<Integer> knownHolograms = Int2ObjectSyncMap.hashset();
private final Int2ObjectMap<UUID> uuidMap = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<List<Metadata>> metadataBuffer = new Int2ObjectOpenHashMap<>();
private final Int2IntMap vehicleMap = new Int2IntOpenHashMap();
private final Int2ObjectMap<BossBar> bossBarMap = new Int2ObjectOpenHashMap<>();
private final IntSet validBlocking = new IntOpenHashSet();
private final IntSet knownHolograms = new IntOpenHashSet();
private final Set<Position> blockInteractions = Collections.newSetFromMap(CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterAccess(250, TimeUnit.MILLISECONDS)
@ -368,27 +370,27 @@ public class EntityTracker1_9 extends EntityTrackerBase {
}
}
public Map<Integer, UUID> getUuidMap() {
public Int2ObjectMap<UUID> getUuidMap() {
return uuidMap;
}
public Map<Integer, List<Metadata>> getMetadataBuffer() {
public Int2ObjectMap<List<Metadata>> getMetadataBuffer() {
return metadataBuffer;
}
public Map<Integer, Integer> getVehicleMap() {
public Int2IntMap getVehicleMap() {
return vehicleMap;
}
public Map<Integer, BossBar> getBossBarMap() {
public Int2ObjectMap<BossBar> getBossBarMap() {
return bossBarMap;
}
public Set<Integer> getValidBlocking() {
public IntSet getValidBlocking() {
return validBlocking;
}
public Set<Integer> getKnownHolograms() {
public IntSet getKnownHolograms() {
return knownHolograms;
}

View File

@ -4,7 +4,6 @@ metadata.format.version = "1.1"
gson = "2.10.1"
fastutil = "8.5.12"
flare = "2.0.1"
vianbt = "4.2.0"
mcstructs = "2.4.2-SNAPSHOT"
@ -29,8 +28,6 @@ velocity = "3.1.1"
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
flare = { group = "space.vectrix.flare", name = "flare", version.ref = "flare" }
flareFastutil = { group = "space.vectrix.flare", name = "flare-fastutil", version.ref = "flare" }
vianbt = { group = "com.viaversion", name = "nbt", version.ref = "vianbt" }
# Custom version that uses ViaNBT instead of its own inbuilt NBT library
text = { group = "com.viaversion.mcstructs", name = "text", version.ref = "mcstructs" }