Create some fancy interfaces

This commit is contained in:
KennyTV 2021-04-26 22:54:43 +02:00
parent 49d386063d
commit 30d122e7fa
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
53 changed files with 1839 additions and 1164 deletions

View File

@ -22,7 +22,7 @@
*/
package com.viaversion.viaversion.api;
import com.viaversion.viaversion.api.connection.ViaConnectionManager;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import io.netty.buffer.ByteBuf;
import com.viaversion.viaversion.api.boss.BossBar;
import com.viaversion.viaversion.api.boss.BossColor;
@ -40,7 +40,7 @@ import java.util.UUID;
* @param <T> The player type for the specific platform, for bukkit it's {@code ViaAPI<Player>}
* @see ViaManager
* @see ProtocolManager
* @see ViaConnectionManager
* @see ConnectionManager
* @see ViaPlatform
*/
public interface ViaAPI<T> {

View File

@ -28,7 +28,7 @@ import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.ViaPlatformLoader;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.connection.ViaConnectionManager;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import java.util.Set;
@ -53,7 +53,7 @@ public interface ViaManager {
*
* @return userconnection manager
*/
ViaConnectionManager getConnectionManager();
ConnectionManager getConnectionManager();
/**
* Returns the manager for Via providers.

View File

@ -0,0 +1,92 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.connection;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Handles injected UserConnections
*/
public interface ConnectionManager {
/**
* Returns if Via injected into this player connection.
*
* @param playerId player uuid
* @return true if the player is handled by Via
*/
boolean isClientConnected(UUID playerId);
/**
* Frontend connections will have the UUID stored. Override this if your platform isn't always frontend.
* UUIDs can't be duplicate between frontend connections.
*/
boolean isFrontEnd(UserConnection connection);
/**
* Returns the frontend UserConnection from the player connected to this proxy server
* Returns null when there isn't a server or connection was not found
* When ViaVersion is reloaded, this method may not return some players.
* May not return ProtocolSupport players.
* <p>
* Note that connections are removed as soon as their channel is closed,
* so avoid using this method during player quits for example.
*/
@Nullable UserConnection getConnectedClient(UUID clientIdentifier);
/**
* Returns the UUID from the frontend connection to this proxy server
* Returns null when there isn't a server or this connection isn't frontend or it doesn't have an id
* When ViaVersion is reloaded, this method may not return some players.
* May not return ProtocolSupport players.
* <p>
* Note that connections are removed as soon as their channel is closed,
* so avoid using this method during player quits for example.
*/
@Nullable UUID getConnectedClientId(UserConnection connection);
/**
* Returns all UserConnections which are registered
* May contain duplicated UUIDs on multiple ProtocolInfo.
* May contain frontend, backend and/or client-sided connections.
* When ViaVersion is reloaded, this method may not return some players.
* May not contain ProtocolSupport players.
*/
Set<UserConnection> getConnections();
/**
* Returns a map containing the UUIDs and frontend UserConnections from players connected to this proxy server
* Returns empty list when there isn't a server
* When ViaVersion is reloaded, this method may not return some players.
* May not contain ProtocolSupport players.
*/
Map<UUID, UserConnection> getConnectedClients();
void onLoginSuccess(UserConnection connection);
void onDisconnect(UserConnection connection);
}

View File

@ -23,36 +23,21 @@
package com.viaversion.viaversion.api.connection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import com.viaversion.viaversion.api.protocol.packet.State;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.util.UUID;
public class ProtocolInfo extends StoredObject {
private State state = State.HANDSHAKE;
private int protocolVersion = -1;
private int serverProtocolVersion = -1;
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
public ProtocolInfo(UserConnection user) {
super(user);
}
public interface ProtocolInfo {
/**
* Returns the protocol state the user is currently in.
*
* @return protocol state
*/
public State getState() {
return state;
}
State getState();
public void setState(State state) {
this.state = state;
}
void setState(State state);
/**
* Returns the user's protocol version, or -1 if not set.
@ -60,15 +45,9 @@ public class ProtocolInfo extends StoredObject {
*
* @return protocol version, or -1 if not set
*/
public int getProtocolVersion() {
return protocolVersion;
}
int getProtocolVersion();
public void setProtocolVersion(int protocolVersion) {
// Map snapshot versions to the higher/orderer release version
ProtocolVersion protocol = ProtocolVersion.getProtocol(protocolVersion);
this.protocolVersion = protocol.getVersion();
}
void setProtocolVersion(int protocolVersion);
/**
* Returns the server protocol version the user is connected to, or -1 if not set.
@ -76,14 +55,9 @@ public class ProtocolInfo extends StoredObject {
*
* @return server protocol version, or -1 if not set
*/
public int getServerProtocolVersion() {
return serverProtocolVersion;
}
int getServerProtocolVersion();
public void setServerProtocolVersion(int serverProtocolVersion) {
ProtocolVersion protocol = ProtocolVersion.getProtocol(serverProtocolVersion);
this.serverProtocolVersion = protocol.getVersion();
}
void setServerProtocolVersion(int serverProtocolVersion);
/**
* Returns the username associated with this connection.
@ -91,13 +65,9 @@ public class ProtocolInfo extends StoredObject {
*
* @return username, set when entering the {@link State#PLAY} state
*/
public @MonotonicNonNull String getUsername() {
return username;
}
@MonotonicNonNull String getUsername();
public void setUsername(String username) {
this.username = username;
}
void setUsername(String username);
/**
* Returns the uuid associated with this connection.
@ -105,35 +75,23 @@ public class ProtocolInfo extends StoredObject {
*
* @return uuid, set when entering the {@link State#PLAY} state
*/
public UUID getUuid() {
return uuid;
}
UUID getUuid();
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
void setUuid(UUID uuid);
/**
* Returns the user's pipeline.
*
* @return protocol pipeline
*/
public ProtocolPipeline getPipeline() {
return pipeline;
}
ProtocolPipeline getPipeline();
public void setPipeline(ProtocolPipeline pipeline) {
this.pipeline = pipeline;
}
void setPipeline(ProtocolPipeline pipeline);
@Override
public String toString() {
return "ProtocolInfo{" +
"state=" + state +
", protocolVersion=" + protocolVersion +
", serverProtocolVersion=" + serverProtocolVersion +
", username='" + username + '\'' +
", uuid=" + uuid +
'}';
}
/**
* Returns the user connection this info represents.
*
* @return user connection
*/
UserConnection getUser();
}

View File

@ -22,10 +22,10 @@
*/
package com.viaversion.viaversion.api.connection;
public class StoredObject {
public abstract class StoredObject {
private final UserConnection user;
public StoredObject(UserConnection user) {
protected StoredObject(UserConnection user) {
this.user = user;
}

View File

@ -22,63 +22,21 @@
*/
package com.viaversion.viaversion.api.connection;
import com.google.common.cache.CacheBuilder;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.protocol.packet.PacketTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.util.ChatColorUtil;
import com.viaversion.viaversion.util.PipelineUtil;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
public class UserConnection {
private static final AtomicLong IDS = new AtomicLong();
private final long id = IDS.incrementAndGet();
private final Map<Class<?>, StoredObject> storedObjects = new ConcurrentHashMap<>();
private final PacketTracker packetTracker = new PacketTracker(this);
private final Set<UUID> passthroughTokens = Collections.newSetFromMap(CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.<UUID, Boolean>build().asMap());
private final Channel channel;
private final boolean clientSide;
private ProtocolInfo protocolInfo;
private boolean active = true;
private boolean pendingDisconnect;
/**
* Creates an UserConnection. When it's a client-side connection, some method behaviors are modified.
*
* @param channel netty channel.
* @param clientSide true if it's a client-side connection
*/
public UserConnection(@Nullable Channel channel, boolean clientSide) {
this.channel = channel;
this.clientSide = clientSide;
}
/**
* @see #UserConnection(Channel, boolean)
*/
public UserConnection(@Nullable Channel channel) {
this(channel, false);
}
public interface UserConnection {
/**
* Get an object from the storage.
@ -87,9 +45,7 @@ public class UserConnection {
* @param <T> The type of the class you want to get.
* @return The requested object
*/
public @Nullable <T extends StoredObject> T get(Class<T> objectClass) {
return (T) storedObjects.get(objectClass);
}
@Nullable <T extends StoredObject> T get(Class<T> objectClass);
/**
* Check if the storage has an object.
@ -97,26 +53,20 @@ public class UserConnection {
* @param objectClass The object class to check
* @return True if the object is in the storage
*/
public boolean has(Class<? extends StoredObject> objectClass) {
return storedObjects.containsKey(objectClass);
}
boolean has(Class<? extends StoredObject> objectClass);
/**
* Put an object into the stored objects based on class.
*
* @param object The object to store.
*/
public void put(StoredObject object) {
storedObjects.put(object.getClass(), object);
}
void put(StoredObject object);
/**
* Clear all the stored objects.
* Used for bungee when switching servers.
*/
public void clearStoredObjects() {
storedObjects.clear();
}
void clearStoredObjects();
/**
* Send a raw packet to the player.
@ -124,26 +74,7 @@ public class UserConnection {
* @param packet The raw packet to send
* @param currentThread Should it run in the same thread
*/
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
Runnable act;
if (clientSide) {
// We'll just assume that Via decoder isn't wrapping the original decoder
act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
} else {
act = () -> channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(act);
} catch (Throwable e) {
packet.release(); // Couldn't schedule
e.printStackTrace();
}
}
}
void sendRawPacket(ByteBuf packet, boolean currentThread);
/**
* Send a raw packet to the player with returning the future.
@ -151,57 +82,28 @@ public class UserConnection {
* @param packet The raw packet to send
* @return ChannelFuture of the packet being sent
*/
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
if (clientSide) {
return sendRawPacketFutureClientSide(packet);
} else {
return sendRawPacketFutureServerSide(packet);
}
}
private ChannelFuture sendRawPacketFutureServerSide(final ByteBuf packet) {
return channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
private ChannelFuture sendRawPacketFutureClientSide(final ByteBuf packet) {
// Assume that decoder isn't wrapping
getChannel().pipeline().context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
return getChannel().newSucceededFuture();
}
ChannelFuture sendRawPacketFuture(ByteBuf packet);
/**
* Send a raw packet to the player (netty thread).
*
* @param packet The packet to send
*/
public void sendRawPacket(ByteBuf packet) {
sendRawPacket(packet, false);
}
void sendRawPacket(ByteBuf packet);
/**
* Returns the user's packet tracker used for the inbuilt packet-limiter.
*
* @return packet tracker
*/
public PacketTracker getPacketTracker() {
return packetTracker;
}
PacketTracker getPacketTracker();
/**
* Disconnect a connection.
*
* @param reason The reason to use, not used if player is not active.
*/
public void disconnect(String reason) {
if (!channel.isOpen() || pendingDisconnect) return;
pendingDisconnect = true;
Via.getPlatform().runSync(() -> {
if (!Via.getPlatform().disconnect(this, ChatColorUtil.translateAlternateColorCodes(reason))) {
channel.close(); // =)
}
});
}
void disconnect(String reason);
/**
* Sends a raw packet to the server.
@ -209,120 +111,35 @@ public class UserConnection {
* @param packet Raw packet to be sent
* @param currentThread If {@code true} executes immediately, {@code false} submits a task to EventLoop
*/
public void sendRawPacketToServer(final ByteBuf packet, boolean currentThread) {
if (clientSide) {
sendRawPacketToServerClientSide(packet, currentThread);
} else {
sendRawPacketToServerServerSide(packet, currentThread);
}
}
private void sendRawPacketToServerServerSide(final ByteBuf packet, boolean currentThread) {
final ByteBuf buf = packet.alloc().buffer();
try {
// We'll use passing through because there are some encoder wrappers
ChannelHandlerContext context = PipelineUtil
.getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline());
try {
Type.VAR_INT.writePrimitive(buf, PacketWrapper.PASSTHROUGH_ID);
Type.UUID.write(buf, generatePassthroughToken());
} catch (Exception shouldNotHappen) {
throw new RuntimeException(shouldNotHappen);
}
buf.writeBytes(packet);
Runnable act = () -> {
if (context != null) {
context.fireChannelRead(buf);
} else {
channel.pipeline().fireChannelRead(buf);
}
};
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(act);
} catch (Throwable t) {
// Couldn't schedule
buf.release();
throw t;
}
}
} finally {
packet.release();
}
}
private void sendRawPacketToServerClientSide(final ByteBuf packet, boolean currentThread) {
Runnable act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
if (currentThread) {
act.run();
} else {
try {
getChannel().eventLoop().submit(act);
} catch (Throwable e) {
e.printStackTrace();
packet.release(); // Couldn't schedule
}
}
}
void sendRawPacketToServer(ByteBuf packet, boolean currentThread);
/**
* Sends a raw packet to the server. It will submit a task to EventLoop.
*
* @param packet Raw packet to be sent
*/
public void sendRawPacketToServer(ByteBuf packet) {
sendRawPacketToServer(packet, false);
}
void sendRawPacketToServer(ByteBuf packet);
/**
* Monitors incoming packets
*
* @return false if this packet should be cancelled
*/
public boolean checkIncomingPacket() {
if (clientSide) {
return checkClientbound();
} else {
return checkServerbound();
}
}
private boolean checkClientbound() {
packetTracker.incrementSent();
return true;
}
private boolean checkServerbound() {
// Ignore if pending disconnect
if (pendingDisconnect) return false;
// Increment received + Check PPS
return !packetTracker.incrementReceived() || !packetTracker.exceedsMaxPPS();
}
boolean checkIncomingPacket();
/**
* Monitors outgoing packets
*
* @return false if this packet should be cancelled
*/
public boolean checkOutgoingPacket() {
if (clientSide) {
return checkServerbound();
} else {
return checkClientbound();
}
}
boolean checkOutgoingPacket();
/**
* Checks if packets needs transforming.
*
* @return if packets should be passed through
*/
public boolean shouldTransformPacket() {
return active;
}
boolean shouldTransformPacket();
/**
* Transforms the outgoing packet contained in ByteBuf. When clientSide is true, this packet is considered
@ -335,10 +152,7 @@ public class UserConnection {
* @throws InformativeException if packet transforming failed
* @throws Exception if any other processing outside of transforming fails
*/
public void transformOutgoing(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, clientSide ? Direction.INCOMING : Direction.OUTGOING, cancelSupplier);
}
void transformOutgoing(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception;
/**
* Transforms the incoming packet contained in ByteBuf. When clientSide is true, this packet is considered
@ -351,71 +165,30 @@ public class UserConnection {
* @throws InformativeException if packet transforming failed
* @throws Exception if any other processing outside of transforming fails
*/
public void transformIncoming(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, clientSide ? Direction.OUTGOING : Direction.INCOMING, cancelSupplier);
}
private void transform(ByteBuf buf, Direction direction, Function<Throwable, Exception> cancelSupplier) throws Exception {
int id = Type.VAR_INT.readPrimitive(buf);
if (id == PacketWrapper.PASSTHROUGH_ID) {
if (!passthroughTokens.remove(Type.UUID.read(buf))) {
throw new IllegalArgumentException("Invalid token");
}
return;
}
PacketWrapper wrapper = new PacketWrapper(id, buf, this);
try {
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(), wrapper);
} catch (CancelException ex) {
throw cancelSupplier.apply(ex);
}
ByteBuf transformed = buf.alloc().buffer();
try {
wrapper.writeToBuffer(transformed);
buf.clear().writeBytes(transformed);
} finally {
transformed.release();
}
}
void transformIncoming(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception;
/**
* Returns the internal id incremented for each new connection.
*
* @return internal id
*/
public long getId() {
return id;
}
long getId();
/**
* Returns the netty channel if present.
*
* @return netty channel if present
*/
public @Nullable Channel getChannel() {
return channel;
}
@Nullable Channel getChannel();
/**
* Returns info containing the current protocol state and userdata.
*
* @return info containing the current protocol state and userdata
*/
public @Nullable ProtocolInfo getProtocolInfo() {
return protocolInfo;
}
@Nullable ProtocolInfo getProtocolInfo();
public void setProtocolInfo(@Nullable ProtocolInfo protocolInfo) {
this.protocolInfo = protocolInfo;
if (protocolInfo != null) {
storedObjects.put(ProtocolInfo.class, protocolInfo);
} else {
storedObjects.remove(ProtocolInfo.class);
}
}
void setProtocolInfo(@Nullable ProtocolInfo protocolInfo);
/**
* Returns a map of stored objects.
@ -425,35 +198,25 @@ public class UserConnection {
* @see #get(Class)
* @see #put(StoredObject)
*/
public Map<Class<?>, StoredObject> getStoredObjects() {
return storedObjects;
}
Map<Class<?>, StoredObject> getStoredObjects();
/**
* Returns whether the connection has protocols other than the base protocol applied.
*
* @return whether the connection is active
*/
public boolean isActive() {
return active;
}
boolean isActive();
public void setActive(boolean active) {
this.active = active;
}
void setActive(boolean active);
/**
* Returns whether the connection is pending a disconnect, initiated through {@link #disconnect(String)}.
*
* @return whether the connection is pending a disconnect
*/
public boolean isPendingDisconnect() {
return pendingDisconnect;
}
boolean isPendingDisconnect();
public void setPendingDisconnect(boolean pendingDisconnect) {
this.pendingDisconnect = pendingDisconnect;
}
void setPendingDisconnect(boolean pendingDisconnect);
/**
* Returns whether this is a client-side connection.
@ -461,18 +224,14 @@ public class UserConnection {
*
* @return whether this is a client-side connection
*/
public boolean isClientSide() {
return clientSide;
}
boolean isClientSide();
/**
* Returns whether {@link ViaVersionConfig#getBlockedProtocols()} should be checked for this connection.
*
* @return whether blocked protocols should be applied
*/
public boolean shouldApplyBlockProtocol() {
return !clientSide; // Don't apply protocol blocking on client-side
}
boolean shouldApplyBlockProtocol();
/**
* Returns a newly generated uuid that will let a packet be passed through without
@ -480,22 +239,5 @@ public class UserConnection {
*
* @return generated passthrough token
*/
public UUID generatePassthroughToken() {
UUID token = UUID.randomUUID();
passthroughTokens.add(token);
return token;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserConnection that = (UserConnection) o;
return id == that.id;
}
@Override
public int hashCode() {
return Long.hashCode(id);
}
UUID generatePassthroughToken();
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.protocol;
import com.viaversion.viaversion.api.protocol.base.SimpleProtocol;
public abstract class AbstractSimpleProtocol extends Protocol<SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes,
SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes> implements SimpleProtocol {
}

View File

@ -23,20 +23,19 @@
package com.viaversion.viaversion.api.protocol;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.State;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays;
import java.util.HashMap;
@ -44,17 +43,8 @@ import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* Abstract protocol class handling packet transformation between two protocol versions.
* Clientbound and serverbount packet types can be set to enforce correct usage of them.
*
* @param <C1> old clientbound packet types
* @param <C2> new clientbound packet types
* @param <S1> old serverbound packet types
* @param <S2> new serverbound packet types
* @see SimpleProtocol for a helper class if you do not want to define any of the types above
*/
public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends ClientboundPacketType, S1 extends ServerboundPacketType, S2 extends ServerboundPacketType> {
public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends ClientboundPacketType, S1 extends ServerboundPacketType, S2 extends ServerboundPacketType>
implements com.viaversion.viaversion.api.protocol.base.Protocol<C1, C2, S1, S2> {
private final Map<Packet, ProtocolPacket> incoming = new HashMap<>();
private final Map<Packet, ProtocolPacket> outgoing = new HashMap<>();
private final Map<Class, Object> storedObjects = new HashMap<>(); // currently only used for MetadataRewriters
@ -137,26 +127,8 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
}
}
/**
* Should this protocol filter an object packet from this class.
* Default: false
*
* @param packetClass The class of the current input
* @return True if it should handle the filtering
*/
public boolean isFiltered(Class packetClass) {
return false;
}
/**
* Filter a packet into the output
*
* @param info The current user connection
* @param packet The input packet as an object (NMS)
* @param output The list to put the object into.
* @throws Exception Throws exception if cancelled / error.
*/
protected void filterPacket(UserConnection info, Object packet, List output) throws Exception {
@Override
public void filterPacket(UserConnection info, Object packet, List output) throws Exception {
output.add(packet);
}
@ -166,9 +138,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
protected void registerPackets() {
}
/**
* Loads the mappingdata.
*/
@Override
public final void loadMappingData() {
getMappingData().load();
onMappingDataLoaded();
@ -182,50 +152,25 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
protected void onMappingDataLoaded() {
}
/**
* Handle protocol registration phase, use this to register providers / tasks.
* <p>
* To be overridden if needed.
*
* @param providers The current providers
*/
@Override
public void register(ViaProviders providers) {
}
/**
* Initialise a user for this protocol setting up objects.
* /!\ WARNING - May be called more than once in a single {@link UserConnection}
* <p>
* To be overridden if needed.
*
* @param userConnection The user to initialise
*/
@Override
public void init(UserConnection userConnection) {
}
/**
* Register an incoming packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
@Override
public void registerIncoming(State state, int oldPacketID, int newPacketID) {
registerIncoming(state, oldPacketID, newPacketID, null);
}
/**
* Register an incoming packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
@Override
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
registerIncoming(state, oldPacketID, newPacketID, packetRemapper, false);
}
@Override
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper, boolean override) {
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
Packet packet = new Packet(state, newPacketID);
@ -236,6 +181,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
incoming.put(packet, protocolPacket);
}
@Override
public void cancelIncoming(State state, int oldPacketID, int newPacketID) {
registerIncoming(state, oldPacketID, newPacketID, new PacketRemapper() {
@Override
@ -245,33 +191,22 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
});
}
@Override
public void cancelIncoming(State state, int newPacketID) {
cancelIncoming(state, -1, newPacketID);
}
/**
* Register an outgoing packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
@Override
public void registerOutgoing(State state, int oldPacketID, int newPacketID) {
registerOutgoing(state, oldPacketID, newPacketID, null);
}
/**
* Register an outgoing packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
@Override
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
registerOutgoing(state, oldPacketID, newPacketID, packetRemapper, false);
}
@Override
public void cancelOutgoing(State state, int oldPacketID, int newPacketID) {
registerOutgoing(state, oldPacketID, newPacketID, new PacketRemapper() {
@Override
@ -281,10 +216,12 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
});
}
@Override
public void cancelOutgoing(State state, int oldPacketID) {
cancelOutgoing(state, oldPacketID, -1);
}
@Override
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper, boolean override) {
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
Packet packet = new Packet(state, oldPacketID);
@ -296,12 +233,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
}
/**
* Registers an outgoing protocol and automatically maps it to the new id.
*
* @param packetType clientbound packet type the server sends
* @param packetRemapper remapper
*/
@Override
public void registerOutgoing(C1 packetType, @Nullable PacketRemapper packetRemapper) {
checkPacketType(packetType, packetType.getClass() == oldClientboundPacketEnum);
@ -314,13 +246,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
registerOutgoing(State.PLAY, oldId, newId, packetRemapper);
}
/**
* Registers an outgoing protocol.
*
* @param packetType clientbound packet type the server initially sends
* @param mappedPacketType clientbound packet type after transforming for the client
* @param packetRemapper remapper
*/
@Override
public void registerOutgoing(C1 packetType, @Nullable C2 mappedPacketType, @Nullable PacketRemapper packetRemapper) {
checkPacketType(packetType, packetType.getClass() == oldClientboundPacketEnum);
checkPacketType(mappedPacketType, mappedPacketType == null || mappedPacketType.getClass() == newClientboundPacketEnum);
@ -328,27 +254,17 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
registerOutgoing(State.PLAY, packetType.ordinal(), mappedPacketType != null ? mappedPacketType.ordinal() : -1, packetRemapper);
}
/**
* Maps a packet type to another packet type without a packet handler.
* Note that this should not be called for simple channel mappings of the same packet; this is already done automatically.
*
* @param packetType clientbound packet type the server initially sends
* @param mappedPacketType clientbound packet type after transforming for the client
*/
@Override
public void registerOutgoing(C1 packetType, @Nullable C2 mappedPacketType) {
registerOutgoing(packetType, mappedPacketType, null);
}
@Override
public void cancelOutgoing(C1 packetType) {
cancelOutgoing(State.PLAY, packetType.ordinal(), packetType.ordinal());
}
/**
* Registers an incoming protocol and automatically maps it to the server's id.
*
* @param packetType serverbound packet type the client sends
* @param packetRemapper remapper
*/
@Override
public void registerIncoming(S2 packetType, @Nullable PacketRemapper packetRemapper) {
checkPacketType(packetType, packetType.getClass() == newServerboundPacketEnum);
@ -361,13 +277,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
registerIncoming(State.PLAY, oldId, newId, packetRemapper);
}
/**
* Registers an incoming protocol.
*
* @param packetType serverbound packet type initially sent by the client
* @param mappedPacketType serverbound packet type after transforming for the server
* @param packetRemapper remapper
*/
@Override
public void registerIncoming(S2 packetType, @Nullable S1 mappedPacketType, @Nullable PacketRemapper packetRemapper) {
checkPacketType(packetType, packetType.getClass() == newServerboundPacketEnum);
checkPacketType(mappedPacketType, mappedPacketType == null || mappedPacketType.getClass() == oldServerboundPacketEnum);
@ -375,44 +285,26 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
registerIncoming(State.PLAY, mappedPacketType != null ? mappedPacketType.ordinal() : -1, packetType.ordinal(), packetRemapper);
}
@Override
public void cancelIncoming(S2 packetType) {
Preconditions.checkArgument(packetType.getClass() == newServerboundPacketEnum);
cancelIncoming(State.PLAY, -1, packetType.ordinal());
}
/**
* Checks if an outgoing packet has already been registered.
*
* @param state state which the packet is sent in
* @param oldPacketID old packet ID
* @return true if already registered
*/
@Override
public boolean hasRegisteredOutgoing(State state, int oldPacketID) {
Packet packet = new Packet(state, oldPacketID);
return outgoing.containsKey(packet);
}
/**
* Checks if an incoming packet has already been registered.
*
* @param state state which the packet is sent in
* @param newPacketId packet ID
* @return true if already registered
*/
@Override
public boolean hasRegisteredIncoming(State state, int newPacketId) {
Packet packet = new Packet(state, newPacketId);
return incoming.containsKey(packet);
}
/**
* Transform a packet using this protocol
*
* @param direction The direction the packet is going in
* @param state The current protocol state
* @param packetWrapper The packet wrapper to transform
* @throws Exception Throws exception if it fails to transform
*/
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
Packet statePacket = new Packet(state, packetWrapper.getId());
Map<Packet, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming);
@ -475,39 +367,21 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
}
}
@Override
public @Nullable <T> T get(Class<T> objectClass) {
return (T) storedObjects.get(objectClass);
}
@Override
public void put(Object object) {
storedObjects.put(object.getClass(), object);
}
/**
* Returns true if this Protocol's {@link #loadMappingData()} method should be called.
* <p>
* This does *not* necessarily mean that {@link #getMappingData()} is non-null, since this may be
* overriden, depending on special cases.
*
* @return true if this Protocol's {@link #loadMappingData()} method should be called
*/
@Override
public boolean hasMappingDataToLoad() {
return getMappingData() != null;
}
public @Nullable MappingData getMappingData() {
return null; // Let the protocols hold the mappings to still have easy, static singleton access there
}
/**
* Returns whether this protocol is a base protocol.
*
* @return whether this represents a base protocol
*/
public boolean isBaseProtocol() {
return false;
}
@Override
public String toString() {
return "Protocol:" + getClass().getSimpleName();

View File

@ -23,8 +23,13 @@
package com.viaversion.viaversion.api.protocol;
import com.google.common.collect.Range;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
@ -44,9 +49,10 @@ public interface ProtocolManager {
* Returns a protocol instance by its class.
*
* @param protocolClass class of the protocol
* @param <T> protocol
* @return protocol if present
*/
@Nullable Protocol getProtocol(Class<? extends Protocol> protocolClass);
@Nullable <T extends Protocol> T getProtocol(Class<T> protocolClass);
/**
* Returns the base protocol handling incoming handshake packets.
@ -180,4 +186,15 @@ public interface ProtocolManager {
* @return data loading future bound to the protocol, or null if all loading is complete
*/
@Nullable CompletableFuture<Void> getMappingLoaderFuture(Class<? extends Protocol> protocolClass);
/**
* Creates a new packet wrapper instance.
*
* @param packetId packet id
* @param buf input buffer
* @param connection user connection
* @return new packet wrapper instance
* @see PacketWrapper#create(PacketType, ByteBuf, UserConnection)
*/
PacketWrapper createPacketWrapper(int packetId, ByteBuf buf, UserConnection connection);
}

View File

@ -22,6 +22,8 @@
*/
package com.viaversion.viaversion.api.protocol;
import com.viaversion.viaversion.api.protocol.base.Protocol;
public interface ProtocolPathEntry {
/**

View File

@ -22,52 +22,14 @@
*/
package com.viaversion.viaversion.api.protocol;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
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.platform.ViaPlatform;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.base.SimpleProtocol;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
public class ProtocolPipeline extends SimpleProtocol {
/**
* Protocol list ordered from client to server transforation with the base protocols at the end.
*/
private List<Protocol> protocolList;
private UserConnection userConnection;
public ProtocolPipeline(UserConnection userConnection) {
init(userConnection);
}
@Override
protected void registerPackets() {
protocolList = new CopyOnWriteArrayList<>();
// This is a pipeline so we register basic pipes
protocolList.add(Via.getManager().getProtocolManager().getBaseProtocol());
}
@Override
public void init(UserConnection userConnection) {
this.userConnection = userConnection;
ProtocolInfo protocolInfo = new ProtocolInfo(userConnection);
protocolInfo.setPipeline(this);
userConnection.setProtocolInfo(protocolInfo);
/* Init through all our pipes */
for (Protocol protocol : protocolList) {
protocol.init(userConnection);
}
}
public interface ProtocolPipeline extends SimpleProtocol {
/**
* Adds a protocol to the current pipeline.
@ -75,16 +37,7 @@ public class ProtocolPipeline extends SimpleProtocol {
*
* @param protocol protocol to add to the end
*/
public void add(Protocol protocol) {
Preconditions.checkNotNull(protocolList, "Tried to add protocol too early");
protocolList.add(protocol);
protocol.init(userConnection);
if (!protocol.isBaseProtocol()) {
moveBaseProtocolsToTail();
}
}
void add(Protocol protocol);
/**
* Adds a list of protocols to the current pipeline.
@ -92,70 +45,7 @@ public class ProtocolPipeline extends SimpleProtocol {
*
* @param protocols protocols to add to the end
*/
public void add(List<Protocol> protocols) {
Preconditions.checkNotNull(protocolList, "Tried to add protocol too early");
protocolList.addAll(protocols);
for (Protocol protocol : protocols) {
protocol.init(userConnection);
}
moveBaseProtocolsToTail();
}
private void moveBaseProtocolsToTail() {
// Move base Protocols to the end, so the login packets can be modified by other protocols
List<Protocol> baseProtocols = null;
for (Protocol protocol : protocolList) {
if (protocol.isBaseProtocol()) {
if (baseProtocols == null) {
baseProtocols = new ArrayList<>();
}
baseProtocols.add(protocol);
}
}
if (baseProtocols != null) {
protocolList.removeAll(baseProtocols);
protocolList.addAll(baseProtocols);
}
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
int originalID = packetWrapper.getId();
// Apply protocols
packetWrapper.apply(direction, state, 0, protocolList, direction == Direction.OUTGOING);
super.transform(direction, state, packetWrapper);
if (Via.getManager().isDebug()) {
logPacket(direction, state, packetWrapper, originalID);
}
}
private void logPacket(Direction direction, State state, PacketWrapper packetWrapper, int originalID) {
// Debug packet
int clientProtocol = userConnection.getProtocolInfo().getProtocolVersion();
ViaPlatform platform = Via.getPlatform();
String actualUsername = packetWrapper.user().getProtocolInfo().getUsername();
String username = actualUsername != null ? actualUsername + " " : "";
platform.getLogger().log(Level.INFO, "{0}{1} {2}: {3} (0x{4}) -> {5} (0x{6}) [{7}] {8}",
new Object[]{
username,
direction,
state,
originalID,
Integer.toHexString(originalID),
packetWrapper.getId(),
Integer.toHexString(packetWrapper.getId()),
Integer.toString(clientProtocol),
packetWrapper
});
}
void add(List<Protocol> protocols);
/**
* Check if the pipeline contains a protocol
@ -163,19 +53,18 @@ public class ProtocolPipeline extends SimpleProtocol {
* @param pipeClass The class to check
* @return True if the protocol class is in the pipeline
*/
public boolean contains(Class<? extends Protocol> pipeClass) {
for (Protocol protocol : protocolList) {
if (protocol.getClass().equals(pipeClass)) return true;
}
return false;
}
boolean contains(Class<? extends Protocol> pipeClass);
public <P extends Protocol> P getProtocol(Class<P> pipeClass) {
for (Protocol protocol : protocolList) {
if (protocol.getClass() == pipeClass) return (P) protocol;
}
return null;
}
/**
* Returns the protocol from the given class if present in the pipeline.
*
* @param pipeClass protocol class
* @param <P> protocol
* @return protocol from class
* @see #contains(Class)
* @see ProtocolManager#getProtocol(Class) for a faster alternative
*/
@Nullable <P extends Protocol> P getProtocol(Class<P> pipeClass);
/**
* Use the pipeline to filter a NMS packet
@ -185,27 +74,13 @@ public class ProtocolPipeline extends SimpleProtocol {
* @return If it should not write the input object to te list.
* @throws Exception If it failed to convert / packet cancelld.
*/
public boolean filter(Object o, List list) throws Exception {
for (Protocol protocol : protocolList) {
if (protocol.isFiltered(o.getClass())) {
protocol.filterPacket(userConnection, o, list);
return true;
}
}
boolean filter(Object o, List list) throws Exception;
return false;
}
public List<Protocol> pipes() {
return protocolList;
}
List<Protocol> pipes();
/**
* Cleans the pipe and adds the base protocol.
* /!\ WARNING - It doesn't add version-specific base Protocol.
*/
public void cleanPipes() {
pipes().clear();
registerPackets();
}
void cleanPipes();
}

View File

@ -0,0 +1,247 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.protocol.base;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
/**
* Abstract protocol class handling packet transformation between two protocol versions.
* Clientbound and serverbount packet types can be set to enforce correct usage of them.
*
* @param <C1> old clientbound packet types
* @param <C2> new clientbound packet types
* @param <S1> old serverbound packet types
* @param <S2> new serverbound packet types
* @see AbstractSimpleProtocol for a helper class if you do not want to define any of the types above
*/
public interface Protocol<C1 extends ClientboundPacketType, C2 extends ClientboundPacketType, S1 extends ServerboundPacketType, S2 extends ServerboundPacketType> {
/**
* Should this protocol filter an object packet from this class.
* Default: false
*
* @param packetClass The class of the current input
* @return True if it should handle the filtering
*/
default boolean isFiltered(Class packetClass) {
return false;
}
/**
* Filter a packet into the output
*
* @param info The current user connection
* @param packet The input packet as an object (NMS)
* @param output The list to put the object into.
* @throws Exception Throws exception if cancelled / error.
*/
void filterPacket(UserConnection info, Object packet, List output) throws Exception;
/**
* Loads the mappingdata.
*/
void loadMappingData();
/**
* Handle protocol registration phase, use this to register providers / tasks.
* <p>
* To be overridden if needed.
*
* @param providers The current providers
*/
void register(ViaProviders providers);
/**
* Initialise a user for this protocol setting up objects.
* /!\ WARNING - May be called more than once in a single {@link UserConnection}
* <p>
* To be overridden if needed.
*
* @param userConnection The user to initialise
*/
void init(UserConnection userConnection);
/**
* Register an incoming packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
void registerIncoming(State state, int oldPacketID, int newPacketID);
/**
* Register an incoming packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper);
void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper, boolean override);
void cancelIncoming(State state, int oldPacketID, int newPacketID);
void cancelIncoming(State state, int newPacketID);
/**
* Register an outgoing packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
void registerOutgoing(State state, int oldPacketID, int newPacketID);
/**
* Register an outgoing packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper);
void cancelOutgoing(State state, int oldPacketID, int newPacketID);
void cancelOutgoing(State state, int oldPacketID);
void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper, boolean override);
/**
* Registers an outgoing protocol and automatically maps it to the new id.
*
* @param packetType clientbound packet type the server sends
* @param packetRemapper remapper
*/
void registerOutgoing(C1 packetType, @Nullable PacketRemapper packetRemapper);
/**
* Registers an outgoing protocol.
*
* @param packetType clientbound packet type the server initially sends
* @param mappedPacketType clientbound packet type after transforming for the client
* @param packetRemapper remapper
*/
void registerOutgoing(C1 packetType, C2 mappedPacketType, @Nullable PacketRemapper packetRemapper);
/**
* Maps a packet type to another packet type without a packet handler.
* Note that this should not be called for simple channel mappings of the same packet; this is already done automatically.
*
* @param packetType clientbound packet type the server initially sends
* @param mappedPacketType clientbound packet type after transforming for the client
*/
void registerOutgoing(C1 packetType, C2 mappedPacketType);
void cancelOutgoing(C1 packetType);
/**
* Registers an incoming protocol and automatically maps it to the server's id.
*
* @param packetType serverbound packet type the client sends
* @param packetRemapper remapper
*/
void registerIncoming(S2 packetType, @Nullable PacketRemapper packetRemapper);
/**
* Registers an incoming protocol.
*
* @param packetType serverbound packet type initially sent by the client
* @param mappedPacketType serverbound packet type after transforming for the server
* @param packetRemapper remapper
*/
void registerIncoming(S2 packetType, S1 mappedPacketType, @Nullable PacketRemapper packetRemapper);
void cancelIncoming(S2 packetType);
/**
* Checks if an outgoing packet has already been registered.
*
* @param state state which the packet is sent in
* @param oldPacketID old packet ID
* @return true if already registered
*/
boolean hasRegisteredOutgoing(State state, int oldPacketID);
/**
* Checks if an incoming packet has already been registered.
*
* @param state state which the packet is sent in
* @param newPacketId packet ID
* @return true if already registered
*/
boolean hasRegisteredIncoming(State state, int newPacketId);
/**
* Transform a packet using this protocol
*
* @param direction The direction the packet is going in
* @param state The current protocol state
* @param packetWrapper The packet wrapper to transform
* @throws Exception Throws exception if it fails to transform
*/
void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception;
@Nullable <T> T get(Class<T> objectClass);
void put(Object object);
/**
* Returns true if this Protocol's {@link #loadMappingData()} method should be called.
* <p>
* This does *not* necessarily mean that {@link #getMappingData()} is non-null, since this may be
* overriden, depending on special cases.
*
* @return true if this Protocol's {@link #loadMappingData()} method should be called
*/
boolean hasMappingDataToLoad();
default @Nullable MappingData getMappingData() {
return null;
}
/**
* Returns whether this protocol is a base protocol.
*
* @return whether this represents a base protocol
*/
default boolean isBaseProtocol() {
return false;
}
}

View File

@ -20,7 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.protocol;
package com.viaversion.viaversion.api.protocol.base;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
@ -29,13 +29,10 @@ import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
* Dummy protocol class when there is no need of any of the
* existing packet type enums or automated channel mappings.
*
* @see Protocol
* @see com.viaversion.viaversion.api.protocol.base.Protocol
*/
public abstract class SimpleProtocol extends Protocol<SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes> {
public interface SimpleProtocol extends Protocol<SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes, SimpleProtocol.DummyPacketTypes> {
protected SimpleProtocol() {
}
public enum DummyPacketTypes implements ClientboundPacketType, ServerboundPacketType {
enum DummyPacketTypes implements ClientboundPacketType, ServerboundPacketType {
}
}

View File

@ -22,42 +22,55 @@
*/
package com.viaversion.viaversion.api.protocol.packet;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.remapper.ValueCreator;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.TypeConverter;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.NoSuchElementException;
public class PacketWrapper {
public static final int PASSTHROUGH_ID = 1000;
private static final Protocol[] PROTOCOL_ARRAY = new Protocol[0];
public interface PacketWrapper {
private final ByteBuf inputBuffer;
private final UserConnection userConnection;
private boolean send = true;
private int id = -1;
private final Deque<Pair<Type, Object>> readableObjects = new ArrayDeque<>();
private final List<Pair<Type, Object>> packetValues = new ArrayList<>();
int PASSTHROUGH_ID = 1000;
public PacketWrapper(int packetID, ByteBuf inputBuffer, UserConnection userConnection) {
this.id = packetID;
this.inputBuffer = inputBuffer;
this.userConnection = userConnection;
/**
* Creates a new packet wrapper instance.
*
* @param packetType packet
* @param connection user connection
* @return new packet wrapper
*/
static PacketWrapper create(PacketType packetType, UserConnection connection) {
return create(packetType.ordinal(), null, connection);
}
/**
* Creates a new packet wrapper instance.
*
* @param packetType packet type
* @param inputBuffer input buffer
* @param connection user connection
* @return new packet wrapper
*/
static PacketWrapper create(PacketType packetType, @Nullable ByteBuf inputBuffer, UserConnection connection) {
return create(packetType.ordinal(), inputBuffer, connection);
}
/**
* Creates a new packet wrapper instance.
*
* @param packetId packet id
* @param inputBuffer input buffer
* @param connection user connection
* @return new packet wrapper
*/
static PacketWrapper create(int packetId, @Nullable ByteBuf inputBuffer, UserConnection connection) {
return Via.getManager().getProtocolManager().createPacketWrapper(packetId, inputBuffer, connection);
}
/**
@ -69,20 +82,7 @@ public class PacketWrapper {
* @return The requested type or throws ArrayIndexOutOfBounds
* @throws InformativeException If it fails to find it, an exception will be thrown.
*/
public <T> T get(Type<T> type, int index) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return (T) packetValue.getValue();
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId()).set("Data", packetValues);
}
<T> T get(Type<T> type, int index) throws Exception;
/**
* Check if a type is at an index
@ -91,18 +91,7 @@ public class PacketWrapper {
* @param index The index of the part (relative to the type)
* @return True if the type is at the index
*/
public boolean is(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
boolean is(Type type, int index);
/**
* Check if a type is at an index
@ -111,19 +100,7 @@ public class PacketWrapper {
* @param index The index of the part (relative to the type)
* @return True if the type is at the index
*/
public boolean isReadable(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : readableObjects) {
if (packetValue.getKey().getBaseClass() == type.getBaseClass()) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
boolean isReadable(Type type, int index);
/**
* Set a currently existing part in the output
@ -134,20 +111,7 @@ public class PacketWrapper {
* @param value The value of the part you wish to set it to.
* @throws InformativeException If it fails to set it, an exception will be thrown.
*/
public <T> void set(Type<T> type, int index, T value) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
packetValue.setValue(value);
return;
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId());
}
<T> void set(Type<T> type, int index, T value) throws Exception;
/**
* Read a type from the input.
@ -157,33 +121,7 @@ public class PacketWrapper {
* @return The requested type
* @throws InformativeException If it fails to read
*/
public <T> T read(Type<T> type) throws Exception {
if (type == Type.NOTHING) return null;
if (readableObjects.isEmpty()) {
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
try {
return type.read(inputBuffer);
} catch (Exception e) {
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
} else {
Pair<Type, Object> read = readableObjects.poll();
Type rtype = read.getKey();
if (rtype.equals(type)
|| (type.getBaseClass().equals(rtype.getBaseClass())
&& type.getOutputClass().equals(rtype.getOutputClass()))) {
return (T) read.getValue();
} else {
if (rtype == Type.NOTHING) {
return read(type); // retry
} else {
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
}
}
}
<T> T read(Type<T> type) throws Exception;
/**
* Write a type to the output.
@ -192,19 +130,7 @@ public class PacketWrapper {
* @param <T> The return type of the type you wish to write.
* @param value The value of the type to write.
*/
public <T> void write(Type<T> type, T value) {
if (value != null) {
if (!type.getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (type instanceof TypeConverter) {
value = (T) ((TypeConverter) type).from(value);
} else {
Via.getPlatform().getLogger().warning("Possible type mismatch: " + value.getClass().getName() + " -> " + type.getOutputClass());
}
}
}
packetValues.add(new Pair<>(type, value));
}
<T> void write(Type<T> type, T value);
/**
* Take a value from the input and write to the output.
@ -214,26 +140,14 @@ public class PacketWrapper {
* @return The type which was read/written.
* @throws Exception If it failed to read or write
*/
public <T> T passthrough(Type<T> type) throws Exception {
T value = read(type);
write(type, value);
return value;
}
<T> T passthrough(Type<T> type) throws Exception;
/**
* Take all the inputs and write them to the output.
*
* @throws Exception If it failed to read or write
*/
public void passthroughAll() throws Exception {
// Copy previous objects
packetValues.addAll(readableObjects);
readableObjects.clear();
// If the buffer has readable bytes, copy them.
if (inputBuffer.isReadable()) {
passthrough(Type.REMAINING_BYTES);
}
}
void passthroughAll() throws Exception;
/**
* Write the current output to a buffer.
@ -241,61 +155,17 @@ public class PacketWrapper {
* @param buffer The buffer to write to.
* @throws InformativeException Throws an exception if it fails to write a value.
*/
public void writeToBuffer(ByteBuf buffer) throws Exception {
if (id != -1) {
Type.VAR_INT.writePrimitive(buffer, id);
}
if (!readableObjects.isEmpty()) {
packetValues.addAll(readableObjects);
readableObjects.clear();
}
int index = 0;
for (Pair<Type, Object> packetValue : packetValues) {
try {
Object value = packetValue.getValue();
if (value != null) {
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (packetValue.getKey() instanceof TypeConverter) {
value = ((TypeConverter) packetValue.getKey()).from(value);
} else {
Via.getPlatform().getLogger().warning("Possible type mismatch: " + value.getClass().getName() + " -> " + packetValue.getKey().getOutputClass());
}
}
}
packetValue.getKey().write(buffer, value);
} catch (Exception e) {
throw new InformativeException(e).set("Index", index).set("Type", packetValue.getKey().getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
index++;
}
writeRemaining(buffer);
}
void writeToBuffer(ByteBuf buffer) throws Exception;
/**
* Clear the input buffer / readable objects
*/
public void clearInputBuffer() {
if (inputBuffer != null) {
inputBuffer.clear();
}
readableObjects.clear(); // :(
}
void clearInputBuffer();
/**
* Clear the packet, used if you have to change the packet completely
*/
public void clearPacket() {
clearInputBuffer();
packetValues.clear();
}
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer);
}
}
void clearPacket();
/**
* Send this packet to the associated user.
@ -306,9 +176,7 @@ public class PacketWrapper {
* @param skipCurrentPipeline Skip the current pipeline
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
send(packetProtocol, skipCurrentPipeline, false);
}
void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception;
/**
* Send this packet to the associated user.
@ -320,61 +188,7 @@ public class PacketWrapper {
* @param currentThread Run in the same thread
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception {
if (!isCancelled()) {
try {
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.OUTGOING);
user().sendRawPacket(output, currentThread);
} catch (Exception e) {
if (!PipelineUtil.containsCause(e, CancelException.class)) {
throw e;
}
}
}
}
/**
* 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
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, Direction direction) throws Exception {
// Apply current pipeline - for outgoing protocol, the collection will be reversed in the apply method
Protocol[] protocols = user().getProtocolInfo().getPipeline().pipes().toArray(PROTOCOL_ARRAY);
boolean reverse = direction == Direction.OUTGOING;
int index = -1;
for (int i = 0; i < protocols.length; i++) {
if (protocols[i].getClass() == packetProtocol) {
index = i;
break;
}
}
if (index == -1) {
// The given protocol is not in the pipeline
throw new NoSuchElementException(packetProtocol.getCanonicalName());
}
if (skipCurrentPipeline) {
index = reverse ? index - 1 : index + 1;
}
// Reset reader before we start
resetReader();
// Apply other protocols
apply(direction, user().getProtocolInfo().getState(), index, protocols, reverse);
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
return output.retain();
} finally {
output.release();
}
}
void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception;
/**
* Send this packet to the associated user.
@ -384,7 +198,7 @@ public class PacketWrapper {
* @param packetProtocol The protocol version of the packet.
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol) throws Exception {
default void send(Class<? extends Protocol> packetProtocol) throws Exception {
send(packetProtocol, true);
}
@ -398,13 +212,7 @@ public class PacketWrapper {
* @return The packets ChannelFuture
* @throws Exception if it fails to write
*/
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
if (!isCancelled()) {
ByteBuf output = constructPacket(packetProtocol, true, Direction.OUTGOING);
return user().sendRawPacketFuture(output);
}
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
}
ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception;
/**
* Send this packet to the associated user.
@ -415,18 +223,7 @@ public class PacketWrapper {
* @throws Exception if it fails to write
*/
@Deprecated
public void send() throws Exception {
if (!isCancelled()) {
// Send
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
user().sendRawPacket(output.retain());
} finally {
output.release();
}
}
}
void send() throws Exception;
/**
* Create a new packet for the target of this packet.
@ -434,9 +231,7 @@ public class PacketWrapper {
* @param packetType packet type of the new packedt
* @return The newly created packet wrapper
*/
public PacketWrapper create(PacketType packetType) {
return new PacketWrapper(packetType.ordinal(), null, user());
}
PacketWrapper create(PacketType packetType);
/**
* Create a new packet for the target of this packet.
@ -444,9 +239,7 @@ public class PacketWrapper {
* @param packetID The ID of the new packet
* @return The newly created packet wrapper
*/
public PacketWrapper create(int packetID) {
return new PacketWrapper(packetID, null, user());
}
PacketWrapper create(int packetID);
/**
* Create a new packet with values.
@ -456,11 +249,7 @@ public class PacketWrapper {
* @return The newly created packet wrapper
* @throws Exception If it failed to write the values from the ValueCreator.
*/
public PacketWrapper create(int packetID, ValueCreator init) throws Exception {
PacketWrapper wrapper = create(packetID);
init.write(wrapper);
return wrapper;
}
PacketWrapper create(int packetID, ValueCreator init) throws Exception;
/**
* Applies a pipeline from an index to the wrapper.
@ -473,69 +262,36 @@ public class PacketWrapper {
* @return The current packetwrapper
* @throws Exception If it fails to transform a packet, exception will be thrown
*/
public PacketWrapper 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
}
PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline, boolean reverse) throws Exception;
/**
* @see #apply(Direction, State, int, List, boolean)
*/
public PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception {
return apply(direction, state, index, pipeline.toArray(PROTOCOL_ARRAY), false);
}
private PacketWrapper apply(Direction direction, State state, int index, Protocol[] pipeline, boolean reverse) throws Exception {
// Reset the reader after every transformation for the packetWrapper, so it can be recycled across packets
if (reverse) {
for (int i = index; i >= 0; i--) {
pipeline[i].transform(direction, state, this);
resetReader();
}
} else {
for (int i = index; i < pipeline.length; i++) {
pipeline[i].transform(direction, state, this);
resetReader();
}
}
return this;
}
PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception;
/**
* Cancel this packet from sending
*/
public void cancel() {
this.send = false;
}
void cancel();
/**
* Check if this packet is cancelled.
*
* @return True if the packet won't be sent.
*/
public boolean isCancelled() {
return !this.send;
}
boolean isCancelled();
/**
* Get the user associated with this Packet
*
* @return The user
*/
public UserConnection user() {
return this.userConnection;
}
UserConnection user();
/**
* Reset the reader, so that it can be read again.
*/
public void resetReader() {
// Move all packet values to the readable for next packet.
for (int i = packetValues.size() - 1; i >= 0; i--) {
this.readableObjects.addFirst(this.packetValues.get(i));
}
this.packetValues.clear();
}
void resetReader();
/**
* Send the current packet to the server.
@ -544,18 +300,7 @@ public class PacketWrapper {
* @throws Exception If it failed to write
*/
@Deprecated
public void sendToServer() throws Exception {
if (!isCancelled()) {
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
user().sendRawPacketToServer(output.retain(), true);
} finally {
output.release();
}
}
}
void sendToServer() throws Exception;
/**
* Send this packet to the server.
@ -565,41 +310,17 @@ public class PacketWrapper {
* @param currentThread Run in the same thread
* @throws Exception if it fails to write
*/
public void sendToServer(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception {
if (!isCancelled()) {
try {
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.INCOMING);
user().sendRawPacketToServer(output, currentThread);
} catch (Exception e) {
if (!PipelineUtil.containsCause(e, CancelException.class)) {
throw e;
}
}
}
}
void sendToServer(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception;
public void sendToServer(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
default void sendToServer(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
sendToServer(packetProtocol, skipCurrentPipeline, false);
}
public void sendToServer(Class<? extends Protocol> packetProtocol) throws Exception {
default void sendToServer(Class<? extends Protocol> packetProtocol) throws Exception {
sendToServer(packetProtocol, true);
}
public int getId() {
return id;
}
int getId();
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "PacketWrapper{" +
"packetValues=" + packetValues +
", readableObjects=" + readableObjects +
", id=" + id +
'}';
}
void setId(int id);
}

View File

@ -23,10 +23,10 @@
package com.viaversion.viaversion.api.protocol.remapper;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.Pair;
import java.util.ArrayList;
import java.util.List;

View File

@ -58,7 +58,7 @@ public class ArmorListener extends ViaBukkitListener {
armor += ArmorType.findById(stack.getTypeId()).getArmorPoints();
}
PacketWrapper wrapper = new PacketWrapper(0x4B, null, getUserConnection(player));
PacketWrapper wrapper = PacketWrapper.create(0x4B, null, getUserConnection(player));
try {
wrapper.write(Type.VAR_INT, player.getEntityId()); // Player ID
wrapper.write(Type.INT, 1); // only 1 property

View File

@ -58,7 +58,7 @@ public class DeathListener extends ViaBukkitListener {
// If online
UserConnection userConnection = getUserConnection(p);
if (userConnection != null) {
PacketWrapper wrapper = new PacketWrapper(0x2C, null, userConnection);
PacketWrapper wrapper = PacketWrapper.create(0x2C, null, userConnection);
try {
wrapper.write(Type.VAR_INT, 2); // Event - Entity dead
wrapper.write(Type.VAR_INT, p.getEntityId()); // Player ID

View File

@ -17,13 +17,14 @@
*/
package com.viaversion.viaversion.bukkit.handlers;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.classgenerator.HandlerConstructor;
@ -50,9 +51,9 @@ public class BukkitChannelInitializer extends ChannelInitializer<SocketChannel>
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
UserConnection info = new UserConnection(socketChannel);
UserConnection info = new UserConnectionImpl(socketChannel);
// init protocol
new ProtocolPipeline(info);
new ProtocolPipelineImpl(info);
// Add originals
this.method.invoke(this.original, socketChannel);

View File

@ -55,7 +55,7 @@ public class EntityToggleGlideListener extends ViaBukkitListener {
// Cancelling can only be done by updating the player's metadata
if (event.isGliding() && event.isCancelled()) {
PacketWrapper packet = new PacketWrapper(0x44, null, getUserConnection(player));
PacketWrapper packet = PacketWrapper.create(0x44, null, getUserConnection(player));
try {
packet.write(Type.VAR_INT, player.getEntityId());

View File

@ -17,10 +17,11 @@
*/
package com.viaversion.viaversion.bungee.handlers;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import java.lang.reflect.Method;
@ -44,9 +45,9 @@ public class BungeeChannelInitializer extends ChannelInitializer<Channel> {
return;
}
UserConnection info = new UserConnection(socketChannel);
UserConnection info = new UserConnectionImpl(socketChannel);
// init protocol
new ProtocolPipeline(info);
new ProtocolPipelineImpl(info);
// Add originals
this.method.invoke(this.original, socketChannel);

View File

@ -17,6 +17,23 @@
*/
package com.viaversion.viaversion.bungee.handlers;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.ExternalJoinGameListener;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.bungee.storage.BungeeStorage;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
@ -25,23 +42,6 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.score.Team;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.protocol.packet.PluginMessage;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.ExternalJoinGameListener;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.bungee.storage.BungeeStorage;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@ -162,7 +162,7 @@ public class BungeeServerHandler implements Listener {
// This ensures we can encode it properly as only the 1.9 protocol is currently implemented.
if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) {
for (UUID uuid : storage.getBossbar()) {
PacketWrapper wrapper = new PacketWrapper(0x0C, null, user);
PacketWrapper wrapper = PacketWrapper.create(0x0C, null, user);
wrapper.write(Type.UUID, uuid);
wrapper.write(Type.VAR_INT, 1); // remove
wrapper.send(Protocol1_9To1_8.class, true, true);
@ -230,7 +230,7 @@ public class BungeeServerHandler implements Listener {
plMsg.setTag(channel);
}
user.put(info);
user.setProtocolInfo(info);
user.put(storage);
user.setActive(protocolPath != null);

View File

@ -47,7 +47,7 @@ public class ElytraPatch implements Listener {
if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) {
int entityId = user.get(EntityTracker1_9.class).getProvidedEntityId();
PacketWrapper wrapper = new PacketWrapper(0x39, null, user);
PacketWrapper wrapper = PacketWrapper.create(0x39, null, user);
wrapper.write(Type.VAR_INT, entityId);
wrapper.write(Types1_9.METADATA_LIST, Collections.singletonList(new Metadata(0, MetaType1_9.Byte, (byte) 0)));

View File

@ -38,7 +38,7 @@ public class BungeeMovementTransmitter extends MovementTransmitterProvider {
public void sendPlayer(UserConnection userConnection) {
if (userConnection.getProtocolInfo().getState() == State.PLAY) {
PacketWrapper wrapper = new PacketWrapper(0x03, null, userConnection);
PacketWrapper wrapper = PacketWrapper.create(0x03, null, userConnection);
wrapper.write(Type.BOOLEAN, userConnection.get(MovementTracker.class).isGround());
try {
wrapper.sendToServer(Protocol1_9To1_8.class);

View File

@ -17,26 +17,27 @@
*/
package com.viaversion.viaversion;
import com.viaversion.viaversion.api.ViaManager;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaManager;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import com.viaversion.viaversion.api.platform.TaskId;
import com.viaversion.viaversion.api.connection.ViaConnectionManager;
import com.viaversion.viaversion.api.platform.UnsupportedSoftware;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.ViaPlatformLoader;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion;
import com.viaversion.viaversion.commands.ViaCommandHandler;
import com.viaversion.viaversion.connection.ConnectionManagerImpl;
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
import com.viaversion.viaversion.protocol.ServerProtocolVersionRange;
import com.viaversion.viaversion.protocol.ServerProtocolVersionSingleton;
import com.viaversion.viaversion.commands.ViaCommandHandler;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.TabCompleteThread;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ViaIdleThread;
import com.viaversion.viaversion.update.UpdateUtil;
import com.viaversion.viaversion.api.platform.UnsupportedSoftware;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import java.util.ArrayList;
import java.util.Arrays;
@ -46,7 +47,7 @@ import java.util.Set;
public class ViaManagerImpl implements ViaManager {
private final ProtocolManagerImpl protocolManager = new ProtocolManagerImpl();
private final ViaConnectionManager connectionManager = new ViaConnectionManager();
private final ConnectionManager connectionManager = new ConnectionManagerImpl();
private final ViaProviders providers = new ViaProviders();
private final ViaPlatform<?> platform;
private final ViaInjector injector;
@ -237,7 +238,7 @@ public class ViaManagerImpl implements ViaManager {
}
@Override
public ViaConnectionManager getConnectionManager() {
public ConnectionManager getConnectionManager() {
return connectionManager;
}

View File

@ -226,7 +226,7 @@ public abstract class CommonBoss<T> extends BossBar<T> {
private PacketWrapper getPacket(UpdateAction action, UserConnection connection) {
try {
PacketWrapper wrapper = new PacketWrapper(0x0C, null, connection); // TODO don't use fixed packet ids for future support
PacketWrapper wrapper = PacketWrapper.create(0x0C, null, connection); // TODO don't use fixed packet ids for future support
wrapper.write(Type.UUID, uuid);
wrapper.write(Type.VAR_INT, action.getId());
switch (action) {

View File

@ -20,9 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.connection;
package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import com.viaversion.viaversion.api.connection.UserConnection;
import io.netty.channel.ChannelFutureListener;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -33,13 +35,11 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles injected UserConnections
*/
public class ViaConnectionManager {
public class ConnectionManagerImpl implements ConnectionManager {
protected final Map<UUID, UserConnection> clients = new ConcurrentHashMap<>();
protected final Set<UserConnection> connections = Collections.newSetFromMap(new ConcurrentHashMap<>());
@Override
public void onLoginSuccess(UserConnection connection) {
Objects.requireNonNull(connection, "connection is null!");
connections.add(connection);
@ -56,6 +56,7 @@ public class ViaConnectionManager {
}
}
@Override
public void onDisconnect(UserConnection connection) {
Objects.requireNonNull(connection, "connection is null!");
connections.remove(connection);
@ -66,74 +67,39 @@ public class ViaConnectionManager {
}
}
/**
* Frontend connections will have the UUID stored. Override this if your platform isn't always frontend.
* UUIDs can't be duplicate between frontend connections.
*/
public boolean isFrontEnd(UserConnection conn) {
return !conn.isClientSide();
@Override
public boolean isFrontEnd(UserConnection connection) {
return !connection.isClientSide();
}
/**
* Returns a map containing the UUIDs and frontend UserConnections from players connected to this proxy server
* Returns empty list when there isn't a server
* When ViaVersion is reloaded, this method may not return some players.
* May not contain ProtocolSupport players.
*/
@Override
public Map<UUID, UserConnection> getConnectedClients() {
return Collections.unmodifiableMap(clients);
}
/**
* Returns the frontend UserConnection from the player connected to this proxy server
* Returns null when there isn't a server or connection was not found
* When ViaVersion is reloaded, this method may not return some players.
* May not return ProtocolSupport players.
* <p>
* Note that connections are removed as soon as their channel is closed,
* so avoid using this method during player quits for example.
*/
@Override
public @Nullable UserConnection getConnectedClient(UUID clientIdentifier) {
return clients.get(clientIdentifier);
}
/**
* Returns the UUID from the frontend connection to this proxy server
* Returns null when there isn't a server or this connection isn't frontend or it doesn't have an id
* When ViaVersion is reloaded, this method may not return some players.
* May not return ProtocolSupport players.
* <p>
* Note that connections are removed as soon as their channel is closed,
* so avoid using this method during player quits for example.
*/
public @Nullable UUID getConnectedClientId(UserConnection conn) {
if (conn.getProtocolInfo() == null) return null;
UUID uuid = conn.getProtocolInfo().getUuid();
@Override
public @Nullable UUID getConnectedClientId(UserConnection connection) {
if (connection.getProtocolInfo() == null) return null;
UUID uuid = connection.getProtocolInfo().getUuid();
UserConnection client = clients.get(uuid);
if (conn.equals(client)) {
if (connection.equals(client)) {
// This is frontend
return uuid;
}
return null;
}
/**
* Returns all UserConnections which are registered
* May contain duplicated UUIDs on multiple ProtocolInfo.
* May contain frontend, backend and/or client-sided connections.
* When ViaVersion is reloaded, this method may not return some players.
* May not contain ProtocolSupport players.
*/
@Override
public Set<UserConnection> getConnections() {
return Collections.unmodifiableSet(connections);
}
/**
* Returns if Via injected into this player connection.
*
* @param playerId player uuid
* @return true if the player is handled by Via
*/
@Override
public boolean isClientConnected(UUID playerId) {
return clients.containsKey(playerId);
}

View File

@ -0,0 +1,120 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.util.UUID;
public class ProtocolInfoImpl extends StoredObject implements ProtocolInfo {
private State state = State.HANDSHAKE;
private int protocolVersion = -1;
private int serverProtocolVersion = -1;
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
public ProtocolInfoImpl(UserConnection user) {
super(user);
}
@Override
public State getState() {
return state;
}
@Override
public void setState(State state) {
this.state = state;
}
@Override
public int getProtocolVersion() {
return protocolVersion;
}
@Override
public void setProtocolVersion(int protocolVersion) {
// Map snapshot versions to the higher/orderer release version
ProtocolVersion protocol = ProtocolVersion.getProtocol(protocolVersion);
this.protocolVersion = protocol.getVersion();
}
@Override
public int getServerProtocolVersion() {
return serverProtocolVersion;
}
@Override
public void setServerProtocolVersion(int serverProtocolVersion) {
ProtocolVersion protocol = ProtocolVersion.getProtocol(serverProtocolVersion);
this.serverProtocolVersion = protocol.getVersion();
}
@Override
public @MonotonicNonNull String getUsername() {
return username;
}
@Override
public void setUsername(String username) {
this.username = username;
}
@Override
public UUID getUuid() {
return uuid;
}
@Override
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
@Override
public ProtocolPipeline getPipeline() {
return pipeline;
}
@Override
public void setPipeline(ProtocolPipeline pipeline) {
this.pipeline = pipeline;
}
@Override
public String toString() {
return "ProtocolInfo{" +
"state=" + state +
", protocolVersion=" + protocolVersion +
", serverProtocolVersion=" + serverProtocolVersion +
", username='" + username + '\'' +
", uuid=" + uuid +
'}';
}
}

View File

@ -0,0 +1,385 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.connection;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.util.ChatColorUtil;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
public class UserConnectionImpl implements UserConnection {
private static final AtomicLong IDS = new AtomicLong();
private final long id = IDS.incrementAndGet();
private final Map<Class<?>, StoredObject> storedObjects = new ConcurrentHashMap<>();
private final PacketTracker packetTracker = new PacketTracker(this);
private final Set<UUID> passthroughTokens = Collections.newSetFromMap(CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.<UUID, Boolean>build().asMap());
private final Channel channel;
private final boolean clientSide;
private ProtocolInfo protocolInfo;
private boolean active = true;
private boolean pendingDisconnect;
/**
* Creates an UserConnection. When it's a client-side connection, some method behaviors are modified.
*
* @param channel netty channel.
* @param clientSide true if it's a client-side connection
*/
public UserConnectionImpl(@Nullable Channel channel, boolean clientSide) {
this.channel = channel;
this.clientSide = clientSide;
}
/**
* @see #UserConnectionImpl(Channel, boolean)
*/
public UserConnectionImpl(@Nullable Channel channel) {
this(channel, false);
}
@Override
public @Nullable <T extends StoredObject> T get(Class<T> objectClass) {
return (T) storedObjects.get(objectClass);
}
@Override
public boolean has(Class<? extends StoredObject> objectClass) {
return storedObjects.containsKey(objectClass);
}
@Override
public void put(StoredObject object) {
storedObjects.put(object.getClass(), object);
}
@Override
public void clearStoredObjects() {
storedObjects.clear();
}
@Override
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
Runnable act;
if (clientSide) {
// We'll just assume that Via decoder isn't wrapping the original decoder
act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
} else {
act = () -> channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(act);
} catch (Throwable e) {
packet.release(); // Couldn't schedule
e.printStackTrace();
}
}
}
@Override
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
if (clientSide) {
return sendRawPacketFutureClientSide(packet);
} else {
return sendRawPacketFutureServerSide(packet);
}
}
private ChannelFuture sendRawPacketFutureServerSide(final ByteBuf packet) {
return channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
}
private ChannelFuture sendRawPacketFutureClientSide(final ByteBuf packet) {
// Assume that decoder isn't wrapping
getChannel().pipeline().context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
return getChannel().newSucceededFuture();
}
@Override
public void sendRawPacket(ByteBuf packet) {
sendRawPacket(packet, false);
}
@Override
public PacketTracker getPacketTracker() {
return packetTracker;
}
@Override
public void disconnect(String reason) {
if (!channel.isOpen() || pendingDisconnect) return;
pendingDisconnect = true;
Via.getPlatform().runSync(() -> {
if (!Via.getPlatform().disconnect(this, ChatColorUtil.translateAlternateColorCodes(reason))) {
channel.close(); // =)
}
});
}
@Override
public void sendRawPacketToServer(final ByteBuf packet, boolean currentThread) {
if (clientSide) {
sendRawPacketToServerClientSide(packet, currentThread);
} else {
sendRawPacketToServerServerSide(packet, currentThread);
}
}
private void sendRawPacketToServerServerSide(final ByteBuf packet, boolean currentThread) {
final ByteBuf buf = packet.alloc().buffer();
try {
// We'll use passing through because there are some encoder wrappers
ChannelHandlerContext context = PipelineUtil
.getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline());
try {
Type.VAR_INT.writePrimitive(buf, PacketWrapper.PASSTHROUGH_ID);
Type.UUID.write(buf, generatePassthroughToken());
} catch (Exception shouldNotHappen) {
throw new RuntimeException(shouldNotHappen);
}
buf.writeBytes(packet);
Runnable act = () -> {
if (context != null) {
context.fireChannelRead(buf);
} else {
channel.pipeline().fireChannelRead(buf);
}
};
if (currentThread) {
act.run();
} else {
try {
channel.eventLoop().submit(act);
} catch (Throwable t) {
// Couldn't schedule
buf.release();
throw t;
}
}
} finally {
packet.release();
}
}
private void sendRawPacketToServerClientSide(final ByteBuf packet, boolean currentThread) {
Runnable act = () -> getChannel().pipeline()
.context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
if (currentThread) {
act.run();
} else {
try {
getChannel().eventLoop().submit(act);
} catch (Throwable e) {
e.printStackTrace();
packet.release(); // Couldn't schedule
}
}
}
@Override
public void sendRawPacketToServer(ByteBuf packet) {
sendRawPacketToServer(packet, false);
}
@Override
public boolean checkIncomingPacket() {
if (clientSide) {
return checkClientbound();
} else {
return checkServerbound();
}
}
private boolean checkClientbound() {
packetTracker.incrementSent();
return true;
}
private boolean checkServerbound() {
// Ignore if pending disconnect
if (pendingDisconnect) return false;
// Increment received + Check PPS
return !packetTracker.incrementReceived() || !packetTracker.exceedsMaxPPS();
}
@Override
public boolean checkOutgoingPacket() {
if (clientSide) {
return checkServerbound();
} else {
return checkClientbound();
}
}
@Override
public boolean shouldTransformPacket() {
return active;
}
@Override
public void transformOutgoing(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, clientSide ? Direction.INCOMING : Direction.OUTGOING, cancelSupplier);
}
@Override
public void transformIncoming(ByteBuf buf, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!buf.isReadable()) return;
transform(buf, clientSide ? Direction.OUTGOING : Direction.INCOMING, cancelSupplier);
}
private void transform(ByteBuf buf, Direction direction, Function<Throwable, Exception> cancelSupplier) throws Exception {
int id = Type.VAR_INT.readPrimitive(buf);
if (id == PacketWrapper.PASSTHROUGH_ID) {
if (!passthroughTokens.remove(Type.UUID.read(buf))) {
throw new IllegalArgumentException("Invalid token");
}
return;
}
PacketWrapper wrapper = PacketWrapper.create(id, buf, this);
try {
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(), wrapper);
} catch (CancelException ex) {
throw cancelSupplier.apply(ex);
}
ByteBuf transformed = buf.alloc().buffer();
try {
wrapper.writeToBuffer(transformed);
buf.clear().writeBytes(transformed);
} finally {
transformed.release();
}
}
@Override
public long getId() {
return id;
}
@Override
public @Nullable Channel getChannel() {
return channel;
}
@Override
public @Nullable ProtocolInfo getProtocolInfo() {
return protocolInfo;
}
@Override
public void setProtocolInfo(@Nullable ProtocolInfo protocolInfo) {
Preconditions.checkArgument(protocolInfo instanceof StoredObject, "ProtocolInfo has to extend StoredObject!");
this.protocolInfo = protocolInfo;
if (protocolInfo != null) {
storedObjects.put(ProtocolInfo.class, (StoredObject) protocolInfo);
} else {
storedObjects.remove(ProtocolInfo.class);
}
}
@Override
public Map<Class<?>, StoredObject> getStoredObjects() {
return storedObjects;
}
@Override
public boolean isActive() {
return active;
}
@Override
public void setActive(boolean active) {
this.active = active;
}
@Override
public boolean isPendingDisconnect() {
return pendingDisconnect;
}
@Override
public void setPendingDisconnect(boolean pendingDisconnect) {
this.pendingDisconnect = pendingDisconnect;
}
@Override
public boolean isClientSide() {
return clientSide;
}
@Override
public boolean shouldApplyBlockProtocol() {
return !clientSide; // Don't apply protocol blocking on client-side
}
@Override
public UUID generatePassthroughToken() {
UUID token = UUID.randomUUID();
passthroughTokens.add(token);
return token;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserConnectionImpl that = (UserConnectionImpl) o;
return id == that.id;
}
@Override
public int hashCode() {
return Long.hashCode(id);
}
}

View File

@ -21,18 +21,17 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.ProtocolPathKey;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl;
import com.viaversion.viaversion.protocols.base.BaseProtocol;
import com.viaversion.viaversion.protocols.base.BaseProtocol1_16;
import com.viaversion.viaversion.protocols.base.BaseProtocol1_7;
@ -64,6 +63,11 @@ import com.viaversion.viaversion.protocols.protocol1_9_1to1_9.Protocol1_9_1To1_9
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3To1_9_1_2;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_9_1.Protocol1_9To1_9_1;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
@ -288,8 +292,8 @@ public class ProtocolManagerImpl implements ProtocolManager {
}
@Override
public @Nullable Protocol getProtocol(Class<? extends Protocol> protocolClass) {
return protocols.get(protocolClass);
public @Nullable <T extends Protocol> T getProtocol(Class<T> protocolClass) {
return (T) protocols.get(protocolClass);
}
@Override
@ -414,6 +418,11 @@ public class ProtocolManagerImpl implements ProtocolManager {
}
}
@Override
public PacketWrapper createPacketWrapper(int packetId, ByteBuf buf, UserConnection connection) {
return new PacketWrapperImpl(packetId, buf, connection);
}
/**
* Called when the server is enabled, to register any non-registered listeners.
*/

View File

@ -17,8 +17,8 @@
*/
package com.viaversion.viaversion.protocol;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.base.Protocol;
public class ProtocolPathEntryImpl implements ProtocolPathEntry {
private final int outputProtocolVersion;

View File

@ -0,0 +1,193 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.protocol;
import com.google.common.base.Preconditions;
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.platform.ViaPlatform;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.connection.ProtocolInfoImpl;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements ProtocolPipeline {
/**
* Protocol list ordered from client to server transforation with the base protocols at the end.
*/
private List<Protocol> protocolList;
private UserConnection userConnection;
public ProtocolPipelineImpl(UserConnection userConnection) {
init(userConnection);
}
@Override
protected void registerPackets() {
protocolList = new CopyOnWriteArrayList<>();
// This is a pipeline so we register basic pipes
protocolList.add(Via.getManager().getProtocolManager().getBaseProtocol());
}
@Override
public void init(UserConnection userConnection) {
this.userConnection = userConnection;
ProtocolInfo protocolInfo = new ProtocolInfoImpl(userConnection);
protocolInfo.setPipeline(this);
userConnection.setProtocolInfo(protocolInfo);
/* Init through all our pipes */
for (Protocol protocol : protocolList) {
protocol.init(userConnection);
}
}
@Override
public void add(Protocol protocol) {
Preconditions.checkNotNull(protocolList, "Tried to add protocol too early");
protocolList.add(protocol);
protocol.init(userConnection);
if (!protocol.isBaseProtocol()) {
moveBaseProtocolsToTail();
}
}
@Override
public void add(List<Protocol> protocols) {
Preconditions.checkNotNull(protocolList, "Tried to add protocol too early");
protocolList.addAll(protocols);
for (Protocol protocol : protocols) {
protocol.init(userConnection);
}
moveBaseProtocolsToTail();
}
private void moveBaseProtocolsToTail() {
// Move base Protocols to the end, so the login packets can be modified by other protocols
List<Protocol> baseProtocols = null;
for (Protocol protocol : protocolList) {
if (protocol.isBaseProtocol()) {
if (baseProtocols == null) {
baseProtocols = new ArrayList<>();
}
baseProtocols.add(protocol);
}
}
if (baseProtocols != null) {
protocolList.removeAll(baseProtocols);
protocolList.addAll(baseProtocols);
}
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
int originalID = packetWrapper.getId();
// Apply protocols
packetWrapper.apply(direction, state, 0, protocolList, direction == Direction.OUTGOING);
super.transform(direction, state, packetWrapper);
if (Via.getManager().isDebug()) {
logPacket(direction, state, packetWrapper, originalID);
}
}
private void logPacket(Direction direction, State state, PacketWrapper packetWrapper, int originalID) {
// Debug packet
int clientProtocol = userConnection.getProtocolInfo().getProtocolVersion();
ViaPlatform platform = Via.getPlatform();
String actualUsername = packetWrapper.user().getProtocolInfo().getUsername();
String username = actualUsername != null ? actualUsername + " " : "";
platform.getLogger().log(Level.INFO, "{0}{1} {2}: {3} (0x{4}) -> {5} (0x{6}) [{7}] {8}",
new Object[]{
username,
direction,
state,
originalID,
Integer.toHexString(originalID),
packetWrapper.getId(),
Integer.toHexString(packetWrapper.getId()),
Integer.toString(clientProtocol),
packetWrapper
});
}
@Override
public boolean contains(Class<? extends Protocol> pipeClass) {
for (Protocol protocol : protocolList) {
if (protocol.getClass().equals(pipeClass)) return true;
}
return false;
}
@Override
public @Nullable <P extends Protocol> P getProtocol(Class<P> pipeClass) {
for (Protocol protocol : protocolList) {
if (protocol.getClass() == pipeClass) return (P) protocol;
}
return null;
}
@Override
public boolean filter(Object o, List list) throws Exception {
for (Protocol protocol : protocolList) {
if (protocol.isFiltered(o.getClass())) {
protocol.filterPacket(userConnection, o, list);
return true;
}
}
return false;
}
@Override
public List<Protocol> pipes() {
return protocolList;
}
@Override
public void cleanPipes() {
pipes().clear();
registerPackets();
}
}

View File

@ -0,0 +1,443 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.protocol.packet;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.ValueCreator;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.TypeConverter;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.NoSuchElementException;
public class PacketWrapperImpl implements PacketWrapper {
private static final Protocol[] PROTOCOL_ARRAY = new Protocol[0];
private final ByteBuf inputBuffer;
private final UserConnection userConnection;
private boolean send = true;
private int id = -1;
private final Deque<Pair<Type, Object>> readableObjects = new ArrayDeque<>();
private final List<Pair<Type, Object>> packetValues = new ArrayList<>();
public PacketWrapperImpl(int packetID, ByteBuf inputBuffer, UserConnection userConnection) {
this.id = packetID;
this.inputBuffer = inputBuffer;
this.userConnection = userConnection;
}
@Override
public <T> T get(Type<T> type, int index) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return (T) packetValue.getValue();
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId()).set("Data", packetValues);
}
@Override
public boolean is(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
@Override
public boolean isReadable(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : readableObjects) {
if (packetValue.getKey().getBaseClass() == type.getBaseClass()) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
@Override
public <T> void set(Type<T> type, int index, T value) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
packetValue.setValue(value);
return;
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId());
}
@Override
public <T> T read(Type<T> type) throws Exception {
if (type == Type.NOTHING) return null;
if (readableObjects.isEmpty()) {
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
try {
return type.read(inputBuffer);
} catch (Exception e) {
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
} else {
Pair<Type, Object> read = readableObjects.poll();
Type rtype = read.getKey();
if (rtype.equals(type)
|| (type.getBaseClass().equals(rtype.getBaseClass())
&& type.getOutputClass().equals(rtype.getOutputClass()))) {
return (T) read.getValue();
} else {
if (rtype == Type.NOTHING) {
return read(type); // retry
} else {
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
}
}
}
@Override
public <T> void write(Type<T> type, T value) {
if (value != null) {
if (!type.getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (type instanceof TypeConverter) {
value = (T) ((TypeConverter) type).from(value);
} else {
Via.getPlatform().getLogger().warning("Possible type mismatch: " + value.getClass().getName() + " -> " + type.getOutputClass());
}
}
}
packetValues.add(new Pair<>(type, value));
}
@Override
public <T> T passthrough(Type<T> type) throws Exception {
T value = read(type);
write(type, value);
return value;
}
@Override
public void passthroughAll() throws Exception {
// Copy previous objects
packetValues.addAll(readableObjects);
readableObjects.clear();
// If the buffer has readable bytes, copy them.
if (inputBuffer.isReadable()) {
passthrough(Type.REMAINING_BYTES);
}
}
@Override
public void writeToBuffer(ByteBuf buffer) throws Exception {
if (id != -1) {
Type.VAR_INT.writePrimitive(buffer, id);
}
if (!readableObjects.isEmpty()) {
packetValues.addAll(readableObjects);
readableObjects.clear();
}
int index = 0;
for (Pair<Type, Object> packetValue : packetValues) {
try {
Object value = packetValue.getValue();
if (value != null) {
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (packetValue.getKey() instanceof TypeConverter) {
value = ((TypeConverter) packetValue.getKey()).from(value);
} else {
Via.getPlatform().getLogger().warning("Possible type mismatch: " + value.getClass().getName() + " -> " + packetValue.getKey().getOutputClass());
}
}
}
packetValue.getKey().write(buffer, value);
} catch (Exception e) {
throw new InformativeException(e).set("Index", index).set("Type", packetValue.getKey().getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
index++;
}
writeRemaining(buffer);
}
@Override
public void clearInputBuffer() {
if (inputBuffer != null) {
inputBuffer.clear();
}
readableObjects.clear(); // :(
}
@Override
public void clearPacket() {
clearInputBuffer();
packetValues.clear();
}
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer);
}
}
@Override
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
send(packetProtocol, skipCurrentPipeline, false);
}
@Override
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception {
if (!isCancelled()) {
try {
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.OUTGOING);
user().sendRawPacket(output, currentThread);
} catch (Exception e) {
if (!PipelineUtil.containsCause(e, CancelException.class)) {
throw e;
}
}
}
}
/**
* 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
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, Direction direction) throws Exception {
// Apply current pipeline - for outgoing protocol, the collection will be reversed in the apply method
Protocol[] protocols = user().getProtocolInfo().getPipeline().pipes().toArray(PROTOCOL_ARRAY);
boolean reverse = direction == Direction.OUTGOING;
int index = -1;
for (int i = 0; i < protocols.length; i++) {
if (protocols[i].getClass() == packetProtocol) {
index = i;
break;
}
}
if (index == -1) {
// The given protocol is not in the pipeline
throw new NoSuchElementException(packetProtocol.getCanonicalName());
}
if (skipCurrentPipeline) {
index = reverse ? index - 1 : index + 1;
}
// Reset reader before we start
resetReader();
// Apply other protocols
apply(direction, user().getProtocolInfo().getState(), index, protocols, reverse);
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
return output.retain();
} finally {
output.release();
}
}
@Override
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
if (!isCancelled()) {
ByteBuf output = constructPacket(packetProtocol, true, Direction.OUTGOING);
return user().sendRawPacketFuture(output);
}
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
}
@Override
@Deprecated
public void send() throws Exception {
if (!isCancelled()) {
// Send
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
user().sendRawPacket(output.retain());
} finally {
output.release();
}
}
}
@Override
public PacketWrapperImpl create(PacketType packetType) {
return new PacketWrapperImpl(packetType.ordinal(), null, user());
}
@Override
public PacketWrapperImpl create(int packetID) {
return new PacketWrapperImpl(packetID, null, user());
}
@Override
public PacketWrapperImpl create(int packetID, ValueCreator init) throws Exception {
PacketWrapperImpl wrapper = create(packetID);
init.write(wrapper);
return wrapper;
}
@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
}
@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 {
// Reset the reader after every transformation for the packetWrapper, so it can be recycled across packets
if (reverse) {
for (int i = index; i >= 0; i--) {
pipeline[i].transform(direction, state, this);
resetReader();
}
} else {
for (int i = index; i < pipeline.length; i++) {
pipeline[i].transform(direction, state, this);
resetReader();
}
}
return this;
}
@Override
public void cancel() {
this.send = false;
}
@Override
public boolean isCancelled() {
return !this.send;
}
@Override
public UserConnection user() {
return this.userConnection;
}
@Override
public void resetReader() {
// Move all packet values to the readable for next packet.
for (int i = packetValues.size() - 1; i >= 0; i--) {
this.readableObjects.addFirst(this.packetValues.get(i));
}
this.packetValues.clear();
}
@Override
@Deprecated
public void sendToServer() throws Exception {
if (!isCancelled()) {
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
user().sendRawPacketToServer(output.retain(), true);
} finally {
output.release();
}
}
}
@Override
public void sendToServer(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception {
if (!isCancelled()) {
try {
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.INCOMING);
user().sendRawPacketToServer(output, currentThread);
} catch (Exception e) {
if (!PipelineUtil.containsCause(e, CancelException.class)) {
throw e;
}
}
}
}
@Override
public int getId() {
return id;
}
@Override
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "PacketWrapper{" +
"packetValues=" + packetValues +
", readableObjects=" + readableObjects +
", id=" + id +
'}';
}
}

View File

@ -17,26 +17,26 @@
*/
package com.viaversion.viaversion.protocols.base;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
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.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.protocol.SimpleProtocol;
import com.viaversion.viaversion.api.protocol.version.VersionProvider;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.protocol.base.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.protocol.version.VersionProvider;
import com.viaversion.viaversion.api.type.Type;
import java.util.ArrayList;
import java.util.List;
public class BaseProtocol extends SimpleProtocol {
public class BaseProtocol extends AbstractSimpleProtocol {
@Override
protected void registerPackets() {

View File

@ -30,7 +30,7 @@ import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.protocol.ServerProtocolVersionSingleton;
import com.viaversion.viaversion.api.protocol.SimpleProtocol;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
@ -43,7 +43,7 @@ import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class BaseProtocol1_7 extends SimpleProtocol {
public class BaseProtocol1_7 extends AbstractSimpleProtocol {
@Override
protected void registerPackets() {
@ -185,7 +185,7 @@ public class BaseProtocol1_7 extends SimpleProtocol {
if (!wrapper.user().getChannel().isOpen()) return;
if (!wrapper.user().shouldApplyBlockProtocol()) return;
PacketWrapper disconnectPacket = new PacketWrapper(0x00, null, wrapper.user()); // Disconnect Packet
PacketWrapper disconnectPacket = PacketWrapper.create(0x00, null, wrapper.user()); // Disconnect Packet
Protocol1_9To1_8.FIX_JSON.write(disconnectPacket, ChatColorUtil.translateAlternateColorCodes(Via.getConfig().getBlockedDisconnectMsg()));
wrapper.cancel(); // cancel current

View File

@ -129,7 +129,7 @@ public class MetadataRewriter1_11To1_10 extends MetadataRewriter {
tracker.addHologram(entityId);
try {
// Send movement
PacketWrapper wrapper = new PacketWrapper(0x25, null, connection);
PacketWrapper wrapper = PacketWrapper.create(0x25, null, connection);
wrapper.write(Type.VAR_INT, entityId);
wrapper.write(Type.SHORT, (short) 0);
wrapper.write(Type.SHORT, (short) (128D * (-Via.getConfig().getHologramYOffset() * 32D)));

View File

@ -62,7 +62,7 @@ public class ConnectionData {
if (handler == null) continue;
int newBlockState = handler.connect(user, pos, blockState);
PacketWrapper blockUpdatePacket = new PacketWrapper(0x0B, null, user);
PacketWrapper blockUpdatePacket = PacketWrapper.create(0x0B, null, user);
blockUpdatePacket.write(Type.POSITION, pos);
blockUpdatePacket.write(Type.VAR_INT, newBlockState);
try {
@ -135,7 +135,7 @@ public class ConnectionData {
}
if (!updates.isEmpty()) {
PacketWrapper wrapper = new PacketWrapper(0x0F, null, user);
PacketWrapper wrapper = PacketWrapper.create(0x0F, null, user);
wrapper.write(Type.INT, chunkX + chunkDeltaX);
wrapper.write(Type.INT, chunkZ + chunkDeltaZ);
wrapper.write(Type.BLOCK_CHANGE_RECORD_ARRAY, updates.toArray(EMPTY_RECORDS));

View File

@ -81,7 +81,7 @@ public class BlockEntityProvider implements Provider {
}
private void sendBlockChange(UserConnection user, Position position, int blockId) throws Exception {
PacketWrapper wrapper = new PacketWrapper(0x0B, null, user);
PacketWrapper wrapper = PacketWrapper.create(0x0B, null, user);
wrapper.write(Type.POSITION, position);
wrapper.write(Type.VAR_INT, blockId);

View File

@ -35,7 +35,7 @@ public class TabCompleteTracker extends StoredObject {
public void sendPacketToServer() {
if (lastTabComplete == null || timeToSend > System.currentTimeMillis()) return;
PacketWrapper wrapper = new PacketWrapper(0x01, null, getUser());
PacketWrapper wrapper = PacketWrapper.create(0x01, null, getUser());
wrapper.write(Type.STRING, lastTabComplete);
wrapper.write(Type.BOOLEAN, false);
wrapper.write(Type.OPTIONAL_POSITION, null);

View File

@ -130,7 +130,7 @@ public class MetadataRewriter1_14To1_13_2 extends MetadataRewriter {
armorItem = new Item(protocol.getMappingData().getNewItemId(729), (byte) 1, (short) 0, null);
}
PacketWrapper equipmentPacket = new PacketWrapper(0x46, null, connection);
PacketWrapper equipmentPacket = PacketWrapper.create(0x46, null, connection);
equipmentPacket.write(Type.VAR_INT, entityId);
equipmentPacket.write(Type.VAR_INT, 4);
equipmentPacket.write(Type.FLAT_VAR_INT_ITEM, armorItem);

View File

@ -94,7 +94,7 @@ public class EntityTracker1_14 extends EntityTracker {
public void onExternalJoinGame(int playerEntityId) {
super.onExternalJoinGame(playerEntityId);
PacketWrapper setViewDistance = new PacketWrapper(0x41, null, getUser());
PacketWrapper setViewDistance = PacketWrapper.create(0x41, null, getUser());
setViewDistance.write(Type.VAR_INT, WorldPackets.SERVERSIDE_VIEW_DISTANCE);
try {
setViewDistance.send(Protocol1_14To1_13_2.class, true, true);

View File

@ -75,7 +75,7 @@ public class BlockEntity {
}
private static void updateBlockEntity(Position pos, short id, CompoundTag tag, UserConnection connection) throws Exception {
PacketWrapper wrapper = new PacketWrapper(0x09, null, connection);
PacketWrapper wrapper = PacketWrapper.create(0x09, null, connection);
wrapper.write(Type.POSITION, pos);
wrapper.write(Type.UNSIGNED_BYTE, id);
wrapper.write(Type.NBT, tag);

View File

@ -154,7 +154,7 @@ public class Protocol1_9To1_8 extends Protocol<ClientboundPackets1_8, Clientboun
}
@Override
protected void filterPacket(UserConnection info, Object packet, List output) throws Exception {
public void filterPacket(UserConnection info, Object packet, List output) throws Exception {
output.addAll(info.get(ClientChunks.class).transformMapChunkBulk(packet));
}

View File

@ -306,7 +306,7 @@ public class SpawnPackets {
public void handle(PacketWrapper wrapper) throws Exception {
short item = wrapper.read(Type.SHORT);
if (item != 0) {
PacketWrapper packet = new PacketWrapper(0x3C, null, wrapper.user());
PacketWrapper packet = PacketWrapper.create(0x3C, null, wrapper.user());
packet.write(Type.VAR_INT, wrapper.get(Type.VAR_INT, 0));
packet.write(Type.VAR_INT, 0);
packet.write(Type.ITEM, new Item(item, (byte) 1, (short) 0, null));

View File

@ -176,7 +176,7 @@ public class WorldPackets {
try {
output.setId(-1); // -1 for no writing of id
output.writeToBuffer(buffer);
PacketWrapper chunkPacket = new PacketWrapper(0x21, buffer, wrapper.user());
PacketWrapper chunkPacket = PacketWrapper.create(0x21, buffer, wrapper.user());
chunkPacket.send(Protocol1_9To1_8.class, false, true);
} finally {
buffer.release();
@ -414,7 +414,7 @@ public class WorldPackets {
Optional<CompoundTag> tag = provider.get(wrapper.user(), pos);
// Send the Update Block Entity packet if present
if (tag.isPresent()) {
PacketWrapper updateBlockEntity = new PacketWrapper(0x09, null, wrapper.user());
PacketWrapper updateBlockEntity = PacketWrapper.create(0x09, null, wrapper.user());
updateBlockEntity.write(Type.POSITION, pos);
updateBlockEntity.write(Type.UNSIGNED_BYTE, (short) 2);

View File

@ -55,7 +55,7 @@ public class BulkChunkTranslatorProvider implements Provider {
meta.setData(wrapper.read(customByteType));
// Construct chunk packet
PacketWrapper chunkPacket = new PacketWrapper(0x21, null, wrapper.user());
PacketWrapper chunkPacket = PacketWrapper.create(0x21, null, wrapper.user());
chunkPacket.write(Type.INT, meta.getX());
chunkPacket.write(Type.INT, meta.getZ());
chunkPacket.write(Type.BOOLEAN, true); // Always ground-up

View File

@ -57,7 +57,7 @@ public class CommandBlockProvider implements Provider {
public void sendPermission(UserConnection user) throws Exception {
if (!isEnabled())
return;
PacketWrapper wrapper = new PacketWrapper(0x1B, null, user); // Entity status
PacketWrapper wrapper = PacketWrapper.create(0x1B, null, user); // Entity status
wrapper.write(Type.INT, user.get(EntityTracker1_9.class).getProvidedEntityId()); // Entity ID
wrapper.write(Type.BYTE, (byte) 26); // Hardcoded op permission level

View File

@ -88,7 +88,7 @@ public class EntityTracker1_9 extends EntityTracker {
}
public void setSecondHand(int entityID, Item item) {
PacketWrapper wrapper = new PacketWrapper(0x3C, null, getUser());
PacketWrapper wrapper = PacketWrapper.create(0x3C, null, getUser());
wrapper.write(Type.VAR_INT, entityID);
wrapper.write(Type.VAR_INT, 1); // slot
wrapper.write(Type.ITEM, this.itemInSecondHand = item);
@ -232,7 +232,7 @@ public class EntityTracker1_9 extends EntityTracker {
knownHolograms.add(entityId);
try {
// Send movement
PacketWrapper wrapper = new PacketWrapper(0x25, null, getUser());
PacketWrapper wrapper = PacketWrapper.create(0x25, null, getUser());
wrapper.write(Type.VAR_INT, entityId);
wrapper.write(Type.SHORT, (short) 0);
wrapper.write(Type.SHORT, (short) (128D * (Via.getConfig().getHologramYOffset() * 32D)));
@ -294,7 +294,7 @@ public class EntityTracker1_9 extends EntityTracker {
}
public void sendTeamPacket(boolean add, boolean now) {
PacketWrapper wrapper = new PacketWrapper(0x41, null, getUser());
PacketWrapper wrapper = PacketWrapper.create(0x41, null, getUser());
wrapper.write(Type.STRING, "viaversion"); // Use viaversion as name
if (add) {
// add
@ -334,7 +334,7 @@ public class EntityTracker1_9 extends EntityTracker {
public void sendMetadataBuffer(int entityId) {
List<Metadata> metadataList = metadataBuffer.get(entityId);
if (metadataList != null) {
PacketWrapper wrapper = new PacketWrapper(0x39, null, getUser());
PacketWrapper wrapper = PacketWrapper.create(0x39, null, getUser());
wrapper.write(Type.VAR_INT, entityId);
wrapper.write(Types1_9.METADATA_LIST, metadataList);
getUser().getProtocolInfo().getPipeline().getProtocol(Protocol1_9To1_8.class).get(MetadataRewriter1_9To1_8.class)

View File

@ -60,7 +60,7 @@ public class Sponge4ArmorListener extends ViaListener {
armor += calculate(player.getLeggings());
armor += calculate(player.getBoots());
PacketWrapper wrapper = new PacketWrapper(0x4B, null, getUserConnection(player.getUniqueId()));
PacketWrapper wrapper = PacketWrapper.create(0x4B, null, getUserConnection(player.getUniqueId()));
try {
wrapper.write(Type.VAR_INT, getEntityId(player)); // Player ID
wrapper.write(Type.INT, 1); // only 1 property

View File

@ -17,6 +17,8 @@
*/
package com.viaversion.viaversion.sponge.handlers;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
@ -24,7 +26,6 @@ import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import java.lang.reflect.Method;
@ -49,9 +50,9 @@ public class SpongeChannelInitializer extends ChannelInitializer<Channel> {
// Ensure ViaVersion is loaded
if (Via.getAPI().getServerVersion().isKnown()
&& channel instanceof SocketChannel) { // channel can be LocalChannel on internal server
UserConnection info = new UserConnection((SocketChannel) channel);
UserConnection info = new UserConnectionImpl((SocketChannel) channel);
// init protocol
new ProtocolPipeline(info);
new ProtocolPipelineImpl(info);
// Add originals
this.method.invoke(this.original, channel);
// Add our transformers

View File

@ -64,7 +64,7 @@ public class DeathListener extends ViaSpongeListener {
Via.getPlatform().runSync(new Runnable() {
@Override
public void run() {
PacketWrapper wrapper = new PacketWrapper(0x2C, null, getUserConnection(p.getUniqueId()));
PacketWrapper wrapper = PacketWrapper.create(0x2C, null, getUserConnection(p.getUniqueId()));
try {
int entityId = getEntityId(p);
wrapper.write(Type.VAR_INT, 2); // Event - Entity dead

View File

@ -58,7 +58,7 @@ public class Sponge5ArmorListener extends ViaSpongeListener {
armor += calculate(player.getLeggings());
armor += calculate(player.getBoots());
PacketWrapper wrapper = new PacketWrapper(0x4B, null, getUserConnection(player.getUniqueId()));
PacketWrapper wrapper = PacketWrapper.create(0x4B, null, getUserConnection(player.getUniqueId()));
try {
wrapper.write(Type.VAR_INT, getEntityId(player)); // Player ID
wrapper.write(Type.INT, 1); // only 1 property

View File

@ -17,10 +17,11 @@
*/
package com.viaversion.viaversion.velocity.handlers;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import java.lang.reflect.Method;
@ -47,8 +48,8 @@ public class VelocityChannelInitializer extends ChannelInitializer<Channel> {
protected void initChannel(Channel channel) throws Exception {
initChannel.invoke(original, channel);
UserConnection user = new UserConnection(channel, clientSide);
new ProtocolPipeline(user);
UserConnection user = new UserConnectionImpl(channel, clientSide);
new ProtocolPipelineImpl(user);
// We need to add a separated handler because Velocity uses pipeline().get(MINECRAFT_DECODER)
channel.pipeline().addBefore("minecraft-encoder", "via-encoder", new VelocityEncodeHandler(user));

View File

@ -38,7 +38,7 @@ public class VelocityMovementTransmitter extends MovementTransmitterProvider {
public void sendPlayer(UserConnection userConnection) {
if (userConnection.getProtocolInfo().getState() == State.PLAY) {
PacketWrapper wrapper = new PacketWrapper(0x03, null, userConnection);
PacketWrapper wrapper = PacketWrapper.create(0x03, null, userConnection);
wrapper.write(Type.BOOLEAN, userConnection.get(MovementTracker.class).isGround());
try {
wrapper.sendToServer(Protocol1_9To1_8.class);