Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-12-27 00:22:51 +01:00
Create some fancy interfaces
Dieser Commit ist enthalten in:
Ursprung
49d386063d
Commit
30d122e7fa
@ -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> {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.protocol;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.base.Protocol;
|
||||
|
||||
public interface ProtocolPathEntry {
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)));
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren