Cut off 1.7.10 injector. We still support 1.7.10 protocol, just not 1.7.10 server software!!!

Attempted reload support again. (Tested on 1.8.8)

PacketSide#getOpposite added
This commit is contained in:
retrooper committed 2022-04-07 22:59:38 +02:00
1 parent d966013b69
commit 58f6cf5411
29 files changed
+123 -1352

No files matched your search

@@ -79,7 +79,7 @@ public abstract class ProtocolPacketEvent<T> extends PacketEvent implements Play
this.packetType = PacketType.getById(packetSide, user.getConnectionState(),
version, packetID);
if (this.packetType == null) {
throw new PacketProcessException("Failed to map the Packet ID " + packetID + " to a PacketType constant. Packet Side: " + packetSide + ", Connection state: " + user.getConnectionState() + ", Server version: " + serverVersion.getReleaseName());
throw new PacketProcessException("Failed to map the Packet ID " + packetID + " to a PacketType constant. Bound: " + packetSide.getOpposite() + ", Connection state: " + user.getConnectionState() + ", Server version: " + serverVersion.getReleaseName());
}
this.connectionState = user.getConnectionState();
}
@@ -19,5 +19,14 @@
package com.github.retrooper.packetevents.protocol;
public enum PacketSide {
CLIENT, SERVER
CLIENT, SERVER;
public PacketSide getOpposite() {
if (this == CLIENT) {
return SERVER;
}
else {
return CLIENT;
}
}
}
@@ -31,6 +31,7 @@ import java.util.IdentityHashMap;
import java.util.Map;
public final class PacketType {
private static boolean PREPARED = false;
//TODO UPDATE Update packet type mappings (clientbound pt. 1)
private static final VersionMapper CLIENTBOUND_PLAY_VERSION_MAPPER = new VersionMapper(
ClientVersion.V_1_7_10,
@@ -63,6 +64,16 @@ public final class PacketType {
ClientVersion.V_1_16_2,
ClientVersion.V_1_17);
public static void prepare() {
PacketType.Play.Client.load();
PacketType.Play.Server.load();
PREPARED = true;
}
public static boolean isPrepared() {
return PREPARED;
}
public static PacketTypeCommon getById(PacketSide side, ConnectionState state, ClientVersion version, int packetID) {
switch (state) {
case HANDSHAKING:
@@ -305,6 +316,9 @@ public final class PacketType {
@Nullable
public static PacketTypeCommon getById(ClientVersion version, int packetId) {
if (!PREPARED) {
PacketType.prepare();
}
int index = SERVERBOUND_PLAY_VERSION_MAPPER.getIndex(version);
Map<Integer, PacketTypeCommon> packetIdMap = PACKET_TYPE_ID_MAP.computeIfAbsent((byte)index, k -> new HashMap<>());
return packetIdMap.get(packetId);
@@ -324,6 +338,7 @@ public final class PacketType {
}
public static void load() {
INDEX = 0;
loadPacketIds(ServerboundPacketType_1_7_10.values());
loadPacketIds(ServerboundPacketType_1_8.values());
loadPacketIds(ServerboundPacketType_1_9.values());
@@ -339,6 +354,9 @@ public final class PacketType {
}
public int getId(ClientVersion version) {
if (!PREPARED) {
PacketType.prepare();
}
int index = SERVERBOUND_PLAY_VERSION_MAPPER.getIndex(version);
return ids[index];
}
@@ -467,12 +485,18 @@ public final class PacketType {
private final int[] ids = new int[CLIENTBOUND_PLAY_VERSION_MAPPER.getVersions().length];
public int getId(ClientVersion version) {
if (!PREPARED) {
PacketType.prepare();
}
int index = CLIENTBOUND_PLAY_VERSION_MAPPER.getIndex(version);
return ids[index];
}
@Nullable
public static PacketTypeCommon getById(ClientVersion version, int packetId) {
if (!PREPARED) {
PacketType.prepare();
}
int index = CLIENTBOUND_PLAY_VERSION_MAPPER.getIndex(version);
Map<Integer, PacketTypeCommon> map = PACKET_TYPE_ID_MAP.get((byte) index);
return map.get(packetId);
@@ -491,6 +515,7 @@ public final class PacketType {
}
public static void load() {
INDEX = 0;
loadPacketIds(ClientboundPacketType_1_7_10.values());
loadPacketIds(ClientboundPacketType_1_8.values());
loadPacketIds(ClientboundPacketType_1_9.values());
@@ -74,7 +74,7 @@ public class PacketEventsPlugin extends JavaPlugin {
case CHAT_MESSAGE:
System.out.println("Running 10 seconds later");
WrapperPlayClientChatMessage chatMessage = new WrapperPlayClientChatMessage(event);
if (chatMessage.getMessage().equalsIgnoreCase("mrmcyeet")) {
if (chatMessage.getMessage().equalsIgnoreCase("keyword")) {
System.out.println("pipe: " + ChannelHelper.pipelineHandlerNamesAsString(event.getChannel()));
event.setCancelled(true);
Particle particle = new Particle(ParticleTypes.ANGRY_VILLAGER);
@@ -23,7 +23,7 @@ import com.github.retrooper.packetevents.event.UserLoginEvent;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.PacketEventsImplHelper;
import io.github.retrooper.packetevents.injector.SpigotChannelInjector;
import io.github.retrooper.packetevents.injector.SpigotChannelInjectorLatest;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -35,7 +35,7 @@ public class InternalBukkitListener implements Listener {
@EventHandler(priority = EventPriority.HIGH)
public void onJoin(PlayerJoinEvent e) {
Player player = e.getPlayer();
SpigotChannelInjector injector = (SpigotChannelInjector) PacketEvents.getAPI().getInjector();
SpigotChannelInjectorLatest injector = (SpigotChannelInjectorLatest) PacketEvents.getAPI().getInjector();
//By accessing user with the player object, we ensure that a valid user is cached.
//In the spigot PlayerManager impl, if there was no user cached,
//we will create one with help of the Player object.
@@ -45,7 +45,6 @@ public class InternalBukkitListener implements Listener {
//if we failed to catch it.(shouldn't ever happen really)
//TODO With Via we might have to run this a tick AFTER join event (atleast when via & viabackwards & rewind are present)
ClientVersion version = PacketEvents.getAPI().getPlayerManager().getClientVersion(player);
injector.updatePlayer(user, player);
}
@@ -32,15 +32,15 @@ import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.settings.PacketEventsSettings;
import com.github.retrooper.packetevents.util.LogManager;
import io.github.retrooper.packetevents.bstats.Metrics;
import io.github.retrooper.packetevents.injector.SpigotChannelInjector;
import io.github.retrooper.packetevents.bukkit.InternalBukkitListener;
import io.github.retrooper.packetevents.injector.SpigotChannelInjectorLatest;
import io.github.retrooper.packetevents.manager.player.PlayerManagerImpl;
import io.github.retrooper.packetevents.manager.protocol.ProtocolManagerImpl;
import io.github.retrooper.packetevents.manager.server.ServerManagerImpl;
import io.github.retrooper.packetevents.bukkit.InternalBukkitListener;
import io.github.retrooper.packetevents.netty.NettyManagerImpl;
import io.github.retrooper.packetevents.util.BukkitLogManager;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.viaversion.CustomPipelineUtil;
import io.github.retrooper.packetevents.netty.NettyManagerImpl;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@@ -78,7 +78,7 @@ public class SpigotPacketEventsBuilder {
private final ServerManager serverManager = new ServerManagerImpl();
private final PlayerManager playerManager = new PlayerManagerImpl();
private final NettyManager nettyManager = new NettyManagerImpl();
private SpigotChannelInjector injector;
private final SpigotChannelInjectorLatest injector = new SpigotChannelInjectorLatest();
private final InternalBukkitListener internalBukkitListener = new InternalBukkitListener();
private final LogManager logManager = new BukkitLogManager();
private boolean loaded;
@@ -103,7 +103,6 @@ public class SpigotPacketEventsBuilder {
throw new IllegalStateException(ex);
}
injector = new SpigotChannelInjector();
//Server hasn't bound to the port yet.
lateBind = !injector.isServerBound();
//If late-bind is enabled, we will inject a bit later.
@@ -111,11 +110,10 @@ public class SpigotPacketEventsBuilder {
injector.inject();
}
loaded = true;
//Register internal packet listener (should be the first listener)
//This listener doesn't do any modifications to the packets, just reads data
getEventManager().registerListener(new InternalPacketListener());
loaded = true;
}
}
@@ -140,9 +138,9 @@ public class SpigotPacketEventsBuilder {
return getVersion().toString() + "-beta";//TODO Cut off "-beta" once 2.0 releases
}));
}
PacketType.Play.Client.load();
PacketType.Play.Server.load();
if (PacketType.isPrepared()) {
PacketType.prepare();
}
//TODO Test reloads
Runnable postInjectTask = () -> {
Bukkit.getPluginManager().registerEvents(internalBukkitListener, plugin);
@@ -1,29 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector;
public interface PacketCompressionManager {
void decompress(Object pipeline, Object buffer, Object output);
void compress(Object pipeline, Object buffer, Object output);
void recompress(Object ctx, Object buffer);
void relocateHandlers(Object pipeline, Object buffer, Object decompressed);
}
@@ -18,9 +18,7 @@
package io.github.retrooper.packetevents.injector;
import io.github.retrooper.packetevents.injector.legacy.compression.PacketCompressionLegacy;
import io.github.retrooper.packetevents.injector.latest.compression.PacketCompressionLatest;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.injector.compression.PacketCompressionHandler;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
@@ -34,38 +32,22 @@ public class PacketCompressionUtil {
public static int MAXIMUM = 2097152;
public static final byte[] COMPRESSED_DATA = new byte[8192];
public static PacketCompressionManager COMPRESSION_MANAGER = null;
private static void loadManager() {
if (COMPRESSION_MANAGER == null) {
if (SpigotReflectionUtil.USE_MODERN_NETTY_PACKAGE) {
COMPRESSION_MANAGER = new PacketCompressionLatest();
}
else {
COMPRESSION_MANAGER = new PacketCompressionLegacy();
}
}
}
public static PacketCompressionHandler COMPRESSION_MANAGER = new PacketCompressionHandler();
public static void decompress(Object pipeline, Object buffer, Object output) {
loadManager();
COMPRESSION_MANAGER.decompress(pipeline, buffer, output);
}
public static void compress(Object pipeline, Object buffer, Object output) {
loadManager();
COMPRESSION_MANAGER.compress(pipeline, buffer, output);
}
public static void recompress(Object ctx, Object buffer) {
loadManager();
COMPRESSION_MANAGER.recompress(ctx, buffer);
}
public static void relocateHandlers(Object pipeline, Object buffer, Object decompressed) {
loadManager();
COMPRESSION_MANAGER.relocateHandlers(pipeline, buffer, decompressed);
}
}
@@ -1,87 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.UserLoginEvent;
import com.github.retrooper.packetevents.injector.ChannelInjector;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import io.github.retrooper.packetevents.injector.latest.SpigotChannelInjectorLatest;
import io.github.retrooper.packetevents.injector.legacy.SpigotChannelInjectorLegacy;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
public class SpigotChannelInjector implements ChannelInjector {
private final ChannelInjector injector;
public SpigotChannelInjector() {
this.injector = SpigotReflectionUtil.USE_MODERN_NETTY_PACKAGE
? new SpigotChannelInjectorLatest() //1.8+ injector
: new SpigotChannelInjectorLegacy(); //1.7 injector
}
@Override
public boolean isServerBound() {
return injector.isServerBound();
}
@Override
public void inject() {
injector.inject();
}
@Override
public void uninject() {
injector.uninject();
}
@Override
public void updateUser(Object channel, User user) {
injector.updateUser(channel, user);
}
public void updatePlayer(User user, Object player) {
PacketEvents.getAPI().getEventManager().callEvent(new UserLoginEvent(user, player));
Object channel = user.getChannel();
if (channel == null) {
channel = PacketEvents.getAPI().getPlayerManager().getChannel(player);
}
setPlayer(channel, player);
}
@Override
public boolean hasPlayer(Object player) {
return injector.hasPlayer(player);
}
@Override
public User getUser(Object channel) {
return injector.getUser(channel);
}
@Override
public void setPlayer(Object channel, Object player) {
injector.setPlayer(channel, player);
}
@Override
public void changeConnectionState(Object channel, ConnectionState connectionState) {
injector.changeConnectionState(channel, connectionState);
}
}
@@ -16,18 +16,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest;
package io.github.retrooper.packetevents.injector;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.UserLoginEvent;
import com.github.retrooper.packetevents.injector.ChannelInjector;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.reflection.ClassUtil;
import com.github.retrooper.packetevents.util.reflection.ReflectionObject;
import io.github.retrooper.packetevents.injector.latest.connection.ServerChannelHandlerLatest;
import io.github.retrooper.packetevents.injector.latest.connection.ServerConnectionInitializerLatest;
import io.github.retrooper.packetevents.injector.latest.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.injector.latest.handlers.PacketEncoderLatest;
import io.github.retrooper.packetevents.injector.connection.ServerChannelHandlerLatest;
import io.github.retrooper.packetevents.injector.connection.ServerConnectionInitializerLatest;
import io.github.retrooper.packetevents.injector.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.injector.handlers.PacketEncoderLatest;
import io.github.retrooper.packetevents.util.InjectedList;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.protocolsupport.ProtocolSupportUtil;
@@ -50,6 +52,14 @@ public class SpigotChannelInjectorLatest implements ChannelInjector {
private List<Object> networkManagers;
private int connectionChannelsListIndex = -1;
public void updatePlayer(User user, Object player) {
PacketEvents.getAPI().getEventManager().callEvent(new UserLoginEvent(user, player));
Object channel = user.getChannel();
if (channel == null) {
channel = PacketEvents.getAPI().getPlayerManager().getChannel(player);
}
setPlayer(channel, player);
}
@Override
public boolean isServerBound() {
@@ -16,13 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.compression;
package io.github.retrooper.packetevents.injector.compression;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import io.github.retrooper.packetevents.injector.PacketCompressionManager;
import io.github.retrooper.packetevents.injector.PacketCompressionUtil;
import io.github.retrooper.packetevents.injector.latest.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.injector.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
@@ -31,8 +30,7 @@ import io.netty.channel.ChannelPipeline;
import java.util.zip.DataFormatException;
public class PacketCompressionLatest implements PacketCompressionManager {
@Override
public class PacketCompressionHandler {
public void decompress(Object rawPipeline, Object rawBuffer, Object rawOutput) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
@@ -70,7 +68,6 @@ public class PacketCompressionLatest implements PacketCompressionManager {
}
}
@Override
public void compress(Object rawPipeline, Object rawBuffer, Object rawOutput) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
@@ -101,7 +98,6 @@ public class PacketCompressionLatest implements PacketCompressionManager {
}
}
@Override
public void recompress(Object rawCtx, Object rawBuffer) {
ChannelHandlerContext ctx = (ChannelHandlerContext) rawCtx;
ByteBuf buffer = (ByteBuf) rawBuffer;
@@ -114,8 +110,6 @@ public class PacketCompressionLatest implements PacketCompressionManager {
}
}
@Override
public void relocateHandlers(Object rawPipeline, Object rawBuffer, Object rawDecompressed) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.connection;
package io.github.retrooper.packetevents.injector.connection;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import io.netty.channel.ChannelHandlerContext;
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.connection;
package io.github.retrooper.packetevents.injector.connection;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import io.netty.channel.Channel;
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.connection;
package io.github.retrooper.packetevents.injector.connection;
import com.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.connection;
package io.github.retrooper.packetevents.injector.connection;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.UserConnectEvent;
@@ -28,8 +28,9 @@ import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.util.reflection.ClassUtil;
import com.github.retrooper.packetevents.util.reflection.ReflectionObject;
import io.github.retrooper.packetevents.injector.latest.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.injector.latest.handlers.PacketEncoderLatest;
import io.github.retrooper.packetevents.injector.handlers.PacketDecoderLatest;
import io.github.retrooper.packetevents.injector.handlers.PacketEncoderLatest;
import io.github.retrooper.packetevents.util.protocolsupport.ProtocolSupportUtil;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
@@ -37,7 +38,6 @@ import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.NoSuchElementException;
@@ -48,7 +48,7 @@ public class ServerConnectionInitializerLatest {
private static void destroyHandlers(Channel channel) {
channel.pipeline().remove(PacketEvents.ENCODER_NAME);
ChannelHandler decoder = channel.pipeline().get(PacketEvents.DECODER_NAME);
if (decoder instanceof PacketDecoderLatest) {
if (decoder != null && ClassUtil.getClassSimpleName(decoder.getClass()).equals("PacketDecoderLatest")) {
channel.pipeline().remove(PacketEvents.DECODER_NAME);
} else if (ViaVersionUtil.isAvailable()) {
decoder = channel.pipeline().get("decoder");
@@ -59,8 +59,8 @@ public class ServerConnectionInitializerLatest {
if (injectedDecoder instanceof PacketDecoderLatest) {
PacketDecoderLatest peDecoder = (PacketDecoderLatest) injectedDecoder;
reflectMCDecoder.writeObject(0, peDecoder.mcDecoder);
} else if (ClassUtil.getClassSimpleName(injectedDecoder.getClass()).equals("PacketDecoderLatest")
|| ClassUtil.getClassSimpleName(injectedDecoder.getClass()).equals("PacketDecoderModern")) {
} else if (injectedDecoder != null && (ClassUtil.getClassSimpleName(injectedDecoder.getClass()).equals("PacketDecoderLatest")
|| ClassUtil.getClassSimpleName(injectedDecoder.getClass()).equals("PacketDecoderModern"))) {
//Some other packetevents instance already injected. Let us find our child decoder somewhere in here.
ReflectionObject reflectInjectedDecoder = new ReflectionObject(injectedDecoder);
List<Object> decoders = reflectInjectedDecoder.readList(0);
@@ -76,9 +76,11 @@ public class ServerConnectionInitializerLatest {
!(channel instanceof NioSocketChannel)) {
return;
}
boolean isReload = false;
if (channel.pipeline().get(PacketEvents.ENCODER_NAME) != null) {
//This is a reload...
destroyHandlers(channel);
isReload = true;
}
User user = new User(channel, connectionState, null, new UserProfile(null, null));
UserConnectEvent connectEvent = new UserConnectEvent(user);
@@ -89,7 +91,36 @@ public class ServerConnectionInitializerLatest {
}
ProtocolManager.USERS.put(channel, user);
try {
channel.pipeline().addAfter("splitter", PacketEvents.DECODER_NAME, new PacketDecoderLatest(user));
PacketDecoderLatest decoder = new PacketDecoderLatest(user);
if (!isReload) {
channel.pipeline().addAfter("splitter", PacketEvents.DECODER_NAME, decoder);
}
else {
decoder.handledCompression = true;
if (ViaVersionUtil.isAvailable()) {
//Inject our decoder into ViaVersion's decoder (because we have to)
ChannelHandler viaDecoder = channel.pipeline().get("decoder");
ReflectionObject reflectionObject = new ReflectionObject(viaDecoder);
ByteToMessageDecoder mcDecoder = reflectionObject.readObject(0, ByteToMessageDecoder.class);
String decoderClassName = ClassUtil.getClassSimpleName(mcDecoder.getClass());
if (decoderClassName.equals("PacketDecoderModern")
|| decoderClassName.equals("PacketDecoderLatest")) {
//We aren't the first packetevents instance to inject into ViaVersion's decoder
ReflectionObject reflectPacketDecoderModern = new ReflectionObject(mcDecoder);
List<ByteToMessageDecoder> decoders = reflectPacketDecoderModern.readList(0);
decoders.add(decoder);
} else {
//We are the first packetevents instance to inject into ViaVersion's decoder
decoder.mcDecoder = mcDecoder;
reflectionObject.write(ByteToMessageDecoder.class, 0, decoder);
}
} else if (ProtocolSupportUtil.isAvailable()) {
channel.pipeline().addAfter("ps_decoder_transformer", PacketEvents.DECODER_NAME, decoder);
}
else {
channel.pipeline().addAfter("decompress", PacketEvents.DECODER_NAME, decoder);
}
}
} catch (NoSuchElementException ex) {
String handlers = ChannelHelper.pipelineHandlerNamesAsString(channel);
throw new IllegalStateException("PacketEvents failed to add a decoder to the netty pipeline. Pipeline handlers: " + handlers, ex);
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.handlers;
package io.github.retrooper.packetevents.injector.handlers;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.exception.PacketProcessException;
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.latest.handlers;
package io.github.retrooper.packetevents.injector.handlers;
import com.github.retrooper.packetevents.exception.PacketProcessException;
import com.github.retrooper.packetevents.protocol.ConnectionState;
@@ -1,235 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.injector.ChannelInjector;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.reflection.ReflectionObject;
import io.github.retrooper.packetevents.injector.legacy.handlers.PacketDecoderLegacy;
import io.github.retrooper.packetevents.injector.legacy.handlers.PacketEncoderLegacy;
import io.github.retrooper.packetevents.injector.legacy.connection.ServerConnectionInitializerLegacy;
import io.github.retrooper.packetevents.injector.legacy.connection.ServerChannelHandlerLegacy;
import io.github.retrooper.packetevents.util.InjectedList;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.protocolsupport.ProtocolSupportUtil;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelFuture;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelPipeline;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SpigotChannelInjectorLegacy implements ChannelInjector {
//Channels that process connecting clients.
private final Set<Channel> injectedConnectionChannels = new HashSet<>();
private List<Object> networkManagers;
private int connectionChannelsListIndex = -1;
@Override
public boolean isServerBound() {
//We want to check if the server has been bound to the port already.
Object serverConnection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
if (serverConnection != null) {
ReflectionObject reflectServerConnection = new ReflectionObject(serverConnection);
//There should only be 2 lists.
for (int i = 0; i < 2; i++) {
List<?> list = reflectServerConnection.readList(i);
for (Object value : list) {
if (value instanceof ChannelFuture) {
connectionChannelsListIndex = i;
//Found the right list.
//It has connection channels, so the server has been bound.
return true;
}
}
}
}
return false;
}
@Override
public void inject() {
Object serverConnection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
if (serverConnection != null) {
ReflectionObject reflectServerConnection = new ReflectionObject(serverConnection);
List<ChannelFuture> connectionChannelFutures = reflectServerConnection.readList(connectionChannelsListIndex);
InjectedList<ChannelFuture> wrappedList = new InjectedList<>(connectionChannelFutures, future -> {
//Each time a channel future is added, we run this.
//This is automatically also ran for the elements already added before we wrapped the list.
Channel channel = future.channel();
//Inject into the server connection channel.
injectServerChannel(channel);
//Make sure to store it, so we can uninject later on.
injectedConnectionChannels.add(channel);
});
//Replace the list with our wrapped one.
reflectServerConnection.writeList(connectionChannelsListIndex, wrappedList);
//Player channels might have been registered already. Let us add our handlers. We are a little late though.
List<Object> networkManagers = SpigotReflectionUtil.getNetworkManagers();
for (Object networkManager : networkManagers) {
ReflectionObject networkManagerWrapper = new ReflectionObject(networkManager);
Channel channel = networkManagerWrapper.readObject(0, Channel.class);
if (channel == null) {
continue;
}
ServerConnectionInitializerLegacy.initChannel(channel, ConnectionState.PLAY);
}
}
}
@Override
public void uninject() {
//Uninject our connection handler from these connection channels.
for (Channel connectionChannel : injectedConnectionChannels) {
uninjectServerChannel(connectionChannel);
}
injectedConnectionChannels.clear();
Object serverConnection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
if (serverConnection != null) {
ReflectionObject reflectServerConnection = new ReflectionObject(serverConnection);
List<ChannelFuture> connectionChannelFutures = reflectServerConnection.readList(connectionChannelsListIndex);
if (connectionChannelFutures instanceof InjectedList) {
//Let us unwrap this. We no longer want to listen to connecting channels.
reflectServerConnection.writeList(connectionChannelsListIndex,
((InjectedList<ChannelFuture>) connectionChannelFutures).originalList());
}
}
}
private void injectServerChannel(Channel serverChannel) {
ChannelPipeline pipeline = serverChannel.pipeline();
ChannelHandler connectionHandler = pipeline.get(PacketEvents.CONNECTION_HANDLER_NAME);
if (connectionHandler != null) {
//Why does it already exist?
pipeline.remove(PacketEvents.CONNECTION_HANDLER_NAME);
}
//Make sure we handle connections after ProtocolSupport.
if (pipeline.get("SpigotNettyServerChannelHandler#0") != null) {
pipeline.addAfter("SpigotNettyServerChannelHandler#0", PacketEvents.CONNECTION_HANDLER_NAME, new ServerChannelHandlerLegacy());
}
//Make sure we handle connections after Geyser.
else if (pipeline.get("floodgate-init") != null) {
pipeline.addAfter("floodgate-init", PacketEvents.CONNECTION_HANDLER_NAME, new ServerChannelHandlerLegacy());
}
//Otherwise, we just don't care and make sure we are last.
else {
pipeline.addFirst(PacketEvents.CONNECTION_HANDLER_NAME, new ServerChannelHandlerLegacy());
}
if (networkManagers == null) {
networkManagers = SpigotReflectionUtil.getNetworkManagers();
}
//Make sure we handled all connected clients.
synchronized (networkManagers) {
for (Object networkManager : networkManagers) {
ReflectionObject networkManagerWrapper = new ReflectionObject(networkManager);
Channel channel = networkManagerWrapper.readObject(0, Channel.class);
if (channel.isOpen()) {
if (channel.localAddress().equals(serverChannel.localAddress())) {
channel.close();
}
}
}
}
}
private void uninjectServerChannel(Channel serverChannel) {
serverChannel.pipeline().remove(PacketEvents.CONNECTION_HANDLER_NAME);
}
@Override
public User getUser(Object channel) {
return getEncoder((Channel) channel).user;
}
@Override
public void changeConnectionState(Object ch, @Nullable ConnectionState connectionState) {
Channel channel = (Channel) ch;
PacketEncoderLegacy encoder = getEncoder(channel);
if (encoder != null) {
//Change connection state in encoder
encoder.user.setConnectionState(connectionState);
}
//On 1.7 this decoder is sharable, we can re-add it to the pipeline without cloning.
PacketDecoderLegacy decoder = getDecoder(channel);
if (decoder != null) {
//Change connection state in decoder
decoder.user.setConnectionState(connectionState);
if (connectionState == ConnectionState.PLAY) {
if (ProtocolSupportUtil.isAvailable()) {
channel.pipeline().remove(PacketEvents.DECODER_NAME);
channel.pipeline().addAfter("ps_decoder_transformer", PacketEvents.DECODER_NAME, decoder);
}
}
}
}
@Override
public void updateUser(Object channel, User user) {
PacketEncoderLegacy encoder = getEncoder((Channel) channel);
if (encoder != null) {
encoder.user = user;
}
PacketDecoderLegacy decoder = getDecoder((Channel) channel);
if (decoder != null) {
decoder.user = user;
}
}
@Override
public void setPlayer(Object channel, Object player) {
PacketEncoderLegacy encoder = getEncoder((Channel) channel);
if (encoder != null) {
encoder.player = (Player) player;
}
PacketDecoderLegacy decoder = getDecoder((Channel) channel);
if (decoder != null) {
decoder.player = (Player) player;
decoder.user.getProfile().setUUID(((Player) player).getUniqueId());
decoder.user.getProfile().setName(((Player) player).getName());
}
}
@Override
public boolean hasPlayer(Object player) {
Object channel = PacketEvents.getAPI().getPlayerManager().getChannel(player);
return channel != null && getEncoder((Channel) channel).player != null;
}
private PacketEncoderLegacy getEncoder(Channel channel) {
return (PacketEncoderLegacy) channel.pipeline().get(PacketEvents.ENCODER_NAME);
}
private PacketDecoderLegacy getDecoder(Channel channel) {
return (PacketDecoderLegacy) channel.pipeline().get(PacketEvents.DECODER_NAME);
}
}
@@ -1,134 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy.compression;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import io.github.retrooper.packetevents.injector.PacketCompressionManager;
import io.github.retrooper.packetevents.injector.PacketCompressionUtil;
import io.github.retrooper.packetevents.injector.legacy.handlers.PacketDecoderLegacy;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPipeline;
import java.util.zip.DataFormatException;
public class PacketCompressionLegacy implements PacketCompressionManager {
@Override
public void decompress(Object rawPipeline, Object rawBuffer, Object rawOutput) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
if (!buffer.isReadable()) return;
ByteBuf output = (ByteBuf) rawOutput;
ChannelHandler decompressionHandler = pipeline.get("decompress");
if (!SpigotReflectionUtil.BYTE_TO_MESSAGE_DECODER.isInstance(decompressionHandler)) {
//ViaRewind might have replaced the decompressor with an empty handler, so we can just skip the decompression process.
output.writeBytes(buffer);
return;
}
int dataLength = ByteBufHelper.readVarInt(buffer);
if (dataLength == 0) {
output.writeBytes(buffer.readBytes(buffer.readableBytes()));
} else {
if (dataLength < PacketCompressionUtil.THRESHOLD) {
throw new RuntimeException("Badly compressed packet - size of " + dataLength + " is below server threshold of " + PacketCompressionUtil.THRESHOLD);
}
if (dataLength > PacketCompressionUtil.MAXIMUM) {
throw new RuntimeException("Badly compressed packet - size of " + dataLength + " is larger than protocol maximum of " + PacketCompressionUtil.MAXIMUM);
}
byte[] compressedData = new byte[buffer.readableBytes()];
buffer.readBytes(compressedData);
PacketCompressionUtil.INFLATER.setInput(compressedData);
byte[] decompressedData = new byte[dataLength];
try {
PacketCompressionUtil.INFLATER.inflate(decompressedData);
} catch (DataFormatException e) {
e.printStackTrace();
}
output.writeBytes(decompressedData);
PacketCompressionUtil.INFLATER.reset();
}
}
@Override
public void compress(Object rawPipeline, Object rawBuffer, Object rawOutput) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
if (!buffer.isReadable()) return;
ByteBuf output = (ByteBuf) rawOutput;
ChannelHandler compressionHandler = pipeline.get("compress");
if (!SpigotReflectionUtil.MESSAGE_TO_BYTE_ENCODER.isInstance(compressionHandler)) {
//ViaRewind might have replaced the compressor with an empty handler, so we can just skip the compression process.
output.writeBytes(buffer);
return;
}
int dataLength = buffer.readableBytes();
if (dataLength < PacketCompressionUtil.THRESHOLD) {
ByteBufHelper.writeVarInt(output, 0);
output.writeBytes(buffer);
} else {
byte[] decompressedData = new byte[dataLength];
buffer.readBytes(decompressedData);
ByteBufHelper.writeVarInt(output, decompressedData.length);
PacketCompressionUtil.DEFLATER.setInput(decompressedData, 0, decompressedData.length);
PacketCompressionUtil.DEFLATER.finish();
while (!PacketCompressionUtil.DEFLATER.finished()) {
int deflateResult = PacketCompressionUtil.DEFLATER.deflate(PacketCompressionUtil.COMPRESSED_DATA);
output.writeBytes(PacketCompressionUtil.COMPRESSED_DATA, 0, deflateResult);
}
PacketCompressionUtil.DEFLATER.reset();
}
}
@Override
public void recompress(Object rawCtx, Object rawBuffer) {
ChannelHandlerContext ctx = (ChannelHandlerContext) rawCtx;
ByteBuf buffer = (ByteBuf) rawBuffer;
ByteBuf compressed = ctx.alloc().buffer();
compress(ctx.pipeline(), buffer, compressed);
try {
buffer.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
@Override
public void relocateHandlers(Object rawPipeline, Object rawBuffer, Object rawDecompressed) {
ChannelPipeline pipeline = (ChannelPipeline) rawPipeline;
ByteBuf buffer = (ByteBuf) rawBuffer;
ByteBuf decompressed = (ByteBuf) rawDecompressed;
try {
buffer.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
ChannelHandler encoder = pipeline.remove(PacketEvents.ENCODER_NAME);
pipeline.addAfter("compress", PacketEvents.ENCODER_NAME, encoder);
PacketDecoderLegacy decoder = (PacketDecoderLegacy) pipeline.remove(PacketEvents.DECODER_NAME);
pipeline.addAfter("decompress", PacketEvents.DECODER_NAME, new PacketDecoderLegacy(decoder));
}
}
@@ -1,40 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy.connection;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.util.io.netty.channel.ChannelInitializer;
public class ServerChannelHandlerLegacy extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = (Channel) msg;
channel.pipeline().addFirst(PacketEvents.SERVER_CHANNEL_HANDLER_NAME, new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
ServerConnectionInitializerLegacy.initChannel(channel, ConnectionState.HANDSHAKING);
}
});
super.channelRead(ctx, msg);
}
}
@@ -1,74 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy.connection;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.UserConnectEvent;
import com.github.retrooper.packetevents.event.UserDisconnectEvent;
import com.github.retrooper.packetevents.manager.protocol.ProtocolManager;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import io.github.retrooper.packetevents.injector.legacy.handlers.PacketDecoderLegacy;
import io.github.retrooper.packetevents.injector.legacy.handlers.PacketEncoderLegacy;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.socket.nio.NioSocketChannel;
import java.util.NoSuchElementException;
public class ServerConnectionInitializerLegacy {
public static void initChannel(Object ch, ConnectionState connectionState) {
Channel channel = (Channel) ch;
//1.7 has no EpollSocketChannel
if (!(channel instanceof NioSocketChannel)) {
return;
}
User user = new User(channel, connectionState, null, new UserProfile(null, null));
UserConnectEvent connectEvent = new UserConnectEvent(user);
PacketEvents.getAPI().getEventManager().callEvent(connectEvent);
if (connectEvent.isCancelled()) {
channel.unsafe().closeForcibly();
return;
}
ProtocolManager.USERS.put(channel, user);
try {
channel.pipeline().addAfter("splitter", PacketEvents.DECODER_NAME, new PacketDecoderLegacy(user));
} catch (NoSuchElementException ex) {
String handlers = ChannelHelper.pipelineHandlerNamesAsString(channel);
throw new IllegalStateException("PacketEvents failed to add a decoder to the netty pipeline. Pipeline handlers: " + handlers, ex);
}
//No need to account for ViaVersion, as they don't support 1.7.10
channel.pipeline().addBefore("encoder", PacketEvents.ENCODER_NAME, new PacketEncoderLegacy(user));
}
public static void destroyChannel(Object ch) {
Channel channel = (Channel) ch;
//1.7 has no EpollSocketChannel
if (!(channel instanceof NioSocketChannel)) {
return;
}
User user = ProtocolManager.USERS.get(channel);
UserDisconnectEvent disconnectEvent = new UserDisconnectEvent(user);
PacketEvents.getAPI().getEventManager().callEvent(disconnectEvent);
channel.pipeline().remove(PacketEvents.DECODER_NAME);
channel.pipeline().remove(PacketEvents.ENCODER_NAME);
ProtocolManager.USERS.remove(channel);
}
}
@@ -1,111 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy.handlers;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.exception.PacketProcessException;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.ExceptionUtil;
import com.github.retrooper.packetevents.util.PacketEventsImplHelper;
import io.github.retrooper.packetevents.injector.PacketCompressionUtil;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.handler.codec.MessageToMessageDecoder;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@ChannelHandler.Sharable
public class PacketDecoderLegacy extends MessageToMessageDecoder<ByteBuf> {
public volatile Player player;
public User user;
public boolean handledCompression;
private boolean skipDoubleTransform;
private final List<Runnable> postTasks = new ArrayList<>();
public PacketDecoderLegacy(User user) {
this.user = user;
}
public PacketDecoderLegacy(PacketDecoderLegacy decoder) {
this.player = decoder.player;
this.user = decoder.user;
this.handledCompression = decoder.handledCompression;
this.skipDoubleTransform = decoder.skipDoubleTransform;
postTasks.clear();
postTasks.addAll(decoder.postTasks);
}
public void read(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) throws Exception {
if (skipDoubleTransform) {
skipDoubleTransform = false;
out.add(input.retain());
}
ByteBuf outputBuffer = ctx.alloc().buffer().writeBytes(input);
try {
boolean doRecompression =
handleCompressionOrder(ctx, outputBuffer);
//TODO Use protocol translation dependencies on spigot!
PacketEventsImplHelper.handleServerBoundPacket(ctx.channel(), user, player, outputBuffer, false);
if (outputBuffer.isReadable()) {
if (doRecompression) {
PacketCompressionUtil.recompress(ctx, outputBuffer);
skipDoubleTransform = true;
}
out.add(outputBuffer.retain());
}
} finally {
outputBuffer.release();
}
}
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
read(ctx, byteBuf, out);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
//Check if the minecraft server will already print our exception for us.
if (ExceptionUtil.isException(cause, PacketProcessException.class) && !SpigotReflectionUtil.isMinecraftServerInstanceDebugging()
&& (user == null || user.getConnectionState() != ConnectionState.HANDSHAKING)) {
cause.printStackTrace();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buffer) {
if (handledCompression) return false;
int decoderIndex = ctx.pipeline().names().indexOf("decompress");
if (decoderIndex == -1) return false;
handledCompression = true;
if (decoderIndex > ctx.pipeline().names().indexOf(PacketEvents.DECODER_NAME)) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = ctx.alloc().buffer();
PacketCompressionUtil.decompress(ctx.pipeline(), buffer, decompressed);
PacketCompressionUtil.relocateHandlers(ctx.pipeline(), buffer, decompressed);
return true;
}
return false;
}
}
@@ -1,97 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.injector.legacy.handlers;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.event.UserDisconnectEvent;
import com.github.retrooper.packetevents.exception.PacketProcessException;
import com.github.retrooper.packetevents.manager.protocol.ProtocolManager;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.EventCreationUtil;
import com.github.retrooper.packetevents.util.ExceptionUtil;
import com.github.retrooper.packetevents.util.PacketEventsImplHelper;
import io.github.retrooper.packetevents.injector.PacketCompressionUtil;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
import org.bukkit.entity.Player;
@ChannelHandler.Sharable
public class PacketEncoderLegacy extends MessageToByteEncoder<ByteBuf> {
public volatile Player player;
public User user;
private boolean handledCompression;
public PacketEncoderLegacy(User user) {
this.user = user;
}
public void read(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
boolean doCompression = handleCompressionOrder(ctx, buffer);
PacketEventsImplHelper.handleClientBoundPacket(ctx.channel(), user, player, buffer, true);
if (doCompression && buffer.isReadable()) {
PacketCompressionUtil.recompress(ctx, buffer);
}
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
if (!in.isReadable()) return;
read(ctx, in);
out.writeBytes(in);
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buffer) {
if (handledCompression) return false;
int encoderIndex = ctx.pipeline().names().indexOf("compress");
if (encoderIndex == -1) return false;
handledCompression = true;
if (encoderIndex > ctx.pipeline().names().indexOf(PacketEvents.ENCODER_NAME)) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = ctx.alloc().buffer();
PacketCompressionUtil.decompress(ctx.pipeline(), buffer, decompressed);
PacketCompressionUtil.relocateHandlers(ctx.pipeline(), buffer, decompressed);
return true;
}
return false;
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
PacketEventsImplHelper.handleDisconnection(ctx.channel(), null);
super.close(ctx, promise);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
//Check if the minecraft server will already print our exception for us.
if (ExceptionUtil.isException(cause, PacketProcessException.class) && !SpigotReflectionUtil.isMinecraftServerInstanceDebugging()
&& (user == null || user.getConnectionState() != ConnectionState.HANDSHAKING)) {
cause.printStackTrace();
}
}
}
@@ -21,7 +21,6 @@ package io.github.retrooper.packetevents.manager.player;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.player.PlayerManager;
import com.github.retrooper.packetevents.manager.protocol.ProtocolManager;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
@@ -31,7 +30,6 @@ import com.github.retrooper.packetevents.util.reflection.Reflection;
import com.github.retrooper.packetevents.util.reflection.ReflectionObject;
import io.github.retrooper.packetevents.util.PlayerPingAccessorModern;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.SpigotVersionLookup_1_7;
import io.github.retrooper.packetevents.util.protocolsupport.ProtocolSupportUtil;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import org.bukkit.entity.Player;
@@ -69,14 +67,9 @@ public class PlayerManagerImpl implements PlayerManager {
PacketEvents.getAPI().getLogManager().debug("Requested ViaVersion for " + player.getName() + "'s protocol version. Protocol version: " + protocolVersion);
} else {
if (PacketEvents.getAPI().getServerManager().getVersion() == ServerVersion.V_1_7_10) {
protocolVersion = SpigotVersionLookup_1_7.getProtocolVersion(player);
PacketEvents.getAPI().getLogManager().debug("Requested Spigot 1.7.10 for " + player.getName() + "'s protocol version. Protocol version: " + protocolVersion);
} else {
//No protocol translation plugins available, the client must be the same version as the server.
protocolVersion = PacketEvents.getAPI().getServerManager().getVersion().getProtocolVersion();
PacketEvents.getAPI().getLogManager().debug("No protocol translation plugins are available. We will assume " + user.getAddress().getHostName() + "'s protocol version is the same as the server's protocol version. Protocol version: " + protocolVersion);
}
//No protocol translation plugins available, the client must be the same version as the server.
protocolVersion = PacketEvents.getAPI().getServerManager().getVersion().getProtocolVersion();
PacketEvents.getAPI().getLogManager().debug("No protocol translation plugins are available. We will assume " + user.getAddress().getHostName() + "'s protocol version is the same as the server's protocol version. Protocol version: " + protocolVersion);
}
ClientVersion version = ClientVersion.getById(protocolVersion);
user.setClientVersion(version);
@@ -142,6 +135,12 @@ public class PlayerManagerImpl implements PlayerManager {
profile.getTextureProperties().add(textureProperty);
}
}
if (profile.getName() == null) {
profile.setName(p.getName());
}
if (profile.getUUID() == null) {
profile.setUUID(p.getUniqueId());
}
return user;
}
}
@@ -27,44 +27,22 @@ import io.github.retrooper.packetevents.netty.buffer.*;
import io.github.retrooper.packetevents.netty.channel.*;
public class NettyManagerImpl implements NettyManager {
private static ByteBufOperator BYTE_BUF_OPERATOR;
private static ByteBufAllocationOperator BYTE_BUF_ALLOCATION_OPERATOR;
private static ChannelOperator CHANNEL_OPERATOR;
private static final ByteBufOperator BYTE_BUF_OPERATOR = new ByteBufOperatorModernImpl();
private static final ByteBufAllocationOperator BYTE_BUF_ALLOCATION_OPERATOR = new ByteBufAllocationOperatorModernImpl();
private static final ChannelOperator CHANNEL_OPERATOR = new ChannelOperatorModernImpl();
@Override
public ChannelOperator getChannelOperator() {
if (CHANNEL_OPERATOR == null) {
if (SpigotReflectionUtil.USE_MODERN_NETTY_PACKAGE) {
CHANNEL_OPERATOR = new ChannelOperatorModernImpl();
}
else {
CHANNEL_OPERATOR = new ChannelOperatorLegacyImpl();
}
}
return CHANNEL_OPERATOR;
}
@Override
public ByteBufOperator getByteBufOperator() {
if (BYTE_BUF_OPERATOR == null) {
if (SpigotReflectionUtil.USE_MODERN_NETTY_PACKAGE) {
BYTE_BUF_OPERATOR = new ByteBufOperatorModernImpl();
} else {
BYTE_BUF_OPERATOR = new ByteBufOperatorLegacyImpl();
}
}
return BYTE_BUF_OPERATOR;
}
@Override
public ByteBufAllocationOperator getByteBufAllocationOperator() {
if (BYTE_BUF_ALLOCATION_OPERATOR == null) {
if (SpigotReflectionUtil.USE_MODERN_NETTY_PACKAGE) {
BYTE_BUF_ALLOCATION_OPERATOR = new ByteBufAllocationOperatorModernImpl();
} else {
BYTE_BUF_ALLOCATION_OPERATOR = new ByteBufAllocationOperatorLegacyImpl();
}
}
return BYTE_BUF_ALLOCATION_OPERATOR;
}
}
@@ -1,49 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.netty.buffer;
import com.github.retrooper.packetevents.netty.buffer.ByteBufAllocationOperator;
import net.minecraft.util.io.netty.buffer.Unpooled;
public class ByteBufAllocationOperatorLegacyImpl implements ByteBufAllocationOperator {
@Override
public Object wrappedBuffer(byte[] bytes) {
return Unpooled.wrappedBuffer(bytes);
}
@Override
public Object copiedBuffer(byte[] bytes) {
return Unpooled.copiedBuffer(bytes);
}
@Override
public Object buffer() {
return Unpooled.buffer();
}
@Override
public Object directBuffer() {
return Unpooled.directBuffer();
}
@Override
public Object compositeBuffer() {
return Unpooled.compositeBuffer();
}
}
@@ -1,227 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.netty.buffer;
import com.github.retrooper.packetevents.netty.buffer.ByteBufOperator;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import java.nio.charset.Charset;
public class ByteBufOperatorLegacyImpl implements ByteBufOperator {
@Override
public int readerIndex(Object buffer) {
return ((ByteBuf) buffer).readerIndex();
}
@Override
public Object readerIndex(Object buffer, int readerIndex) {
return ((ByteBuf) buffer).readerIndex(readerIndex);
}
@Override
public int writerIndex(Object buffer) {
return ((ByteBuf) buffer).writerIndex();
}
@Override
public Object writerIndex(Object buffer, int writerIndex) {
return ((ByteBuf) buffer).writerIndex(writerIndex);
}
@Override
public int readableBytes(Object buffer) {
return ((ByteBuf) buffer).readableBytes();
}
@Override
public int writableBytes(Object buffer) {
return ((ByteBuf) buffer).writableBytes();
}
@Override
public Object clear(Object buffer) {
return ((ByteBuf) buffer).clear();
}
@Override
public byte readByte(Object buffer) {
return ((ByteBuf) buffer).readByte();
}
@Override
public short readShort(Object buffer) {
return ((ByteBuf) buffer).readShort();
}
@Override
public int readInt(Object buffer) {
return ((ByteBuf) buffer).readInt();
}
@Override
public long readUnsignedInt(Object buffer) {
return ((ByteBuf) buffer).readUnsignedInt();
}
@Override
public long readLong(Object buffer) {
return ((ByteBuf) buffer).readLong();
}
@Override
public void writeByte(Object buffer, int value) {
((ByteBuf) buffer).writeByte(value);
}
@Override
public void writeShort(Object buffer, int value) {
((ByteBuf) buffer).writeShort(value);
}
@Override
public void writeInt(Object buffer, int value) {
((ByteBuf) buffer).writeInt(value);
}
@Override
public void writeLong(Object buffer, long value) {
((ByteBuf) buffer).writeLong(value);
}
@Override
public Object getBytes(Object buffer, int index, byte[] destination) {
return ((ByteBuf)buffer).getBytes(index, destination);
}
@Override
public short getUnsignedByte(Object buffer, int index) {
return ((ByteBuf)buffer).getUnsignedByte(index);
}
@Override
public boolean isReadable(Object buffer) {
return ((ByteBuf)buffer).isReadable();
}
@Override
public Object copy(Object buffer) {
return ((ByteBuf) buffer).copy();
}
@Override
public Object duplicate(Object buffer) {
return ((ByteBuf) buffer).duplicate();
}
@Override
public boolean hasArray(Object buffer) {
return ((ByteBuf) buffer).hasArray();
}
@Override
public byte[] array(Object buffer) {
return ((ByteBuf) buffer).array();
}
@Override
public Object retain(Object buffer) {
return ((ByteBuf) buffer).retain();
}
@Override
public Object retainedDuplicate(Object buffer) {
return ((ByteBuf)buffer).duplicate().retain();
}
@Override
public Object readSlice(Object buffer, int length) {
return ((ByteBuf)buffer).readSlice(length);
}
@Override
public Object readBytes(Object buffer, byte[] destination, int destinationIndex, int length) {
return ((ByteBuf)buffer).readBytes(destination, destinationIndex, length);
}
@Override
public Object readBytes(Object buffer, int length) {
return ((ByteBuf) buffer).readBytes(length);
}
@Override
public void readBytes(Object buffer, byte[] bytes) {
((ByteBuf) buffer).readBytes(bytes);
}
@Override
public Object writeBytes(Object buffer, Object src) {
return ((ByteBuf)buffer).writeBytes((ByteBuf) src);
}
@Override
public Object writeBytes(Object buffer, byte[] bytes) {
return ((ByteBuf)buffer).writeBytes(bytes);
}
@Override
public Object writeBytes(Object buffer, byte[] bytes, int offset, int length) {
return ((ByteBuf)buffer).writeBytes(bytes, offset, length);
}
@Override
public boolean release(Object buffer) {
return ((ByteBuf) buffer).release();
}
@Override
public int refCnt(Object buffer) {
return ((ByteBuf) buffer).refCnt();
}
@Override
public Object skipBytes(Object buffer, int length) {
return ((ByteBuf) buffer).skipBytes(length);
}
@Override
public String toString(Object buffer, int index, int length, Charset charset) {
return ((ByteBuf) buffer).toString(index, length, charset);
}
@Override
public Object markReaderIndex(Object buffer) {
return ((ByteBuf)buffer).markReaderIndex();
}
@Override
public Object resetReaderIndex(Object buffer) {
return ((ByteBuf)buffer).resetReaderIndex();
}
@Override
public Object markWriterIndex(Object buffer) {
return ((ByteBuf)buffer).markWriterIndex();
}
@Override
public Object resetWriterIndex(Object buffer) {
return ((ByteBuf)buffer).resetWriterIndex();
}
}
@@ -1,126 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.netty.channel;
import com.github.retrooper.packetevents.netty.channel.ChannelOperator;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.handler.timeout.ReadTimeoutException;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.ClosedChannelException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ChannelOperatorLegacyImpl implements ChannelOperator {
private static Set<Class<? extends Throwable>> IGNORED_HANDLER_EXCEPTIONS = null;
@Override
public Set<Class<? extends Throwable>> getIgnoredHandlerExceptions() {
if (IGNORED_HANDLER_EXCEPTIONS == null) {
IGNORED_HANDLER_EXCEPTIONS = new HashSet<>();
IGNORED_HANDLER_EXCEPTIONS.add(ClosedChannelException.class);
IGNORED_HANDLER_EXCEPTIONS.add(ReadTimeoutException.class);
IGNORED_HANDLER_EXCEPTIONS.add(IOException.class);
//TODO NativeIoException, was thing a thing in 1.7? Only simiarly named class i found was IOExceptionWithCause
}
return IGNORED_HANDLER_EXCEPTIONS;
}
@Override
public SocketAddress remoteAddress(Object channel) {
return ((Channel) channel).remoteAddress();
}
@Override
public SocketAddress localAddress(Object channel) {
return ((Channel) channel).localAddress();
}
@Override
public boolean isOpen(Object channel) {
return ((Channel) channel).isOpen();
}
@Override
public Object close(Object channel) {
return ((Channel) channel).close();
}
@Override
public Object write(Object channel, Object buffer) {
return ((Channel) channel).write(buffer);
}
@Override
public Object flush(Object channel) {
return ((Channel) channel).flush();
}
@Override
public Object writeAndFlush(Object channel, Object buffer) {
return ((Channel) channel).writeAndFlush(buffer);
}
@Override
public Object fireChannelRead(Object channel, Object buffer) {
return ((Channel) channel).pipeline().fireChannelRead(buffer);
}
@Override
public Object writeInContext(Object channel, String ctx, Object buffer) {
return ((Channel) channel).pipeline().context(ctx).write(buffer);
}
@Override
public Object flushInContext(Object channel, String ctx) {
return ((Channel) channel).pipeline().context(ctx).flush();
}
@Override
public Object writeAndFlushInContext(Object channel, String ctx, Object buffer) {
return ((Channel) channel).pipeline().context(ctx).writeAndFlush(buffer);
}
@Override
public Object fireChannelReadInContext(Object channel, String ctx, Object buffer) {
return ((Channel) channel).pipeline().context(ctx).fireChannelRead(buffer);
}
@Override
public List<String> pipelineHandlerNames(Object channel) {
return ((Channel) channel).pipeline().names();
}
@Override
public Object getPipelineHandler(Object channel, String name) {
return ((Channel)channel).pipeline().get(name);
}
@Override
public Object getPipelineContext(Object channel, String name) {
return ((Channel)channel).pipeline().context(name);
}
@Override
public void runInEventLoop(Object channel, Runnable runnable) {
((Channel)channel).eventLoop().execute(runnable);
}
}
@@ -1,45 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2021 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.util;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class SpigotVersionLookup_1_7 {
private static Method getPlayerVersionMethod = null;
public static int getProtocolVersion(Player player) {
if (getPlayerVersionMethod == null) {
try {
getPlayerVersionMethod = SpigotReflectionUtil.NETWORK_MANAGER_CLASS.getMethod("getVersion");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
Object networkManager = SpigotReflectionUtil.getNetworkManager(player);
try {
return (int) getPlayerVersionMethod.invoke(networkManager);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
}