Updating commands and Packet API

- Added wrapper for PacketHandshakeInSetProtocol
- Took ProtocolAPI from Atlas to implement ViaVersion and ProtocolSupport version checking hooks.
- Added hook into "Login" style packets.
- Wth this new hook, we get player version numbers and store them by Channel now.
- Packets are now initialized in the same join listener as where APlayer is generated in JoinListener class.
- Removed Listener extension from HandlerAbstract, ModernHandler, LegacvHandler

NOTE: Protocol version grabbing needs implemented for LegacyHandler (1.7.10 version)
This commit is contained in:
Dawson
2022-08-11 12:27:16 -04:00
parent 0c3066103c
commit 234744e98d
17 changed files with 308 additions and 72 deletions
@@ -18,7 +18,7 @@ import java.util.Comparator;
import java.util.stream.Collectors;
@Init(priority = Priority.LOW)
@CommandAlias("anticheat")
@CommandAlias("anticheat|ac")
@CommandPermission("anticheat.command")
public class AnticheatCommand extends BaseCommand {
@@ -74,4 +74,25 @@ public class AnticheatCommand extends BaseCommand {
pl.spigot().sendMessage(Messages.ALERTS_ON);
}
}
@Subcommand("playerinfo|info|pi")
@Description("Get player's information")
@Syntax("[player]")
@CommandCompletion("@players")
@CommandPermission("anticheat.command.info")
public void onCommand(CommandSender sender, @Single Player target) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().getPlayer(target.getUniqueId()).orElse(null);
if(player == null) {
sender.spigot().sendMessage(Messages.NULL_APLAYER);
return;
}
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
sender.sendMessage(Color.translate("&6&lPing&8: &f" + player.getLagInfo().getTransPing() * 50 + "ms"));
sender.sendMessage(Color.translate("&6&lVersion&8: &f" + player.getPlayerVersion().name()));
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
});
}
}
@@ -12,6 +12,8 @@ import dev.brighten.ac.data.obj.NormalAction;
import dev.brighten.ac.handler.EntityLocationHandler;
import dev.brighten.ac.handler.PotionHandler;
import dev.brighten.ac.handler.keepalive.KeepAlive;
import dev.brighten.ac.handler.protocolsupport.ProtocolAPI;
import dev.brighten.ac.messages.Messages;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.utils.Tuple;
@@ -51,7 +53,7 @@ public class APlayer {
private int playerTick;
@Getter
//TODO Actually grab real player version once finished implementing version grabber from Atlas
private ProtocolVersion playerVersion = ProtocolVersion.V1_8_9;
private ProtocolVersion playerVersion = ProtocolVersion.UNKNOWN;
@Getter
private Object playerConnection;
@@ -73,8 +75,10 @@ public class APlayer {
}
private void load() {
for (CheckStatic check : Anticheat.INSTANCE.getCheckManager().getCheckClasses()) {
checks.add(check.playerInit(this));
synchronized (checks) {
for (CheckStatic check : Anticheat.INSTANCE.getCheckManager().getCheckClasses()) {
checks.add(check.playerInit(this));
}
}
this.movement = new MovementHandler(this);
this.potionHandler = new PotionHandler(this);
@@ -82,6 +86,15 @@ public class APlayer {
this.info = new GeneralInformation();
this.lagInfo = new LagInformation();
this.blockInformation = new BlockInformation(this);
Anticheat.INSTANCE.getScheduler().execute(() ->
playerVersion = ProtocolVersion.getVersion(ProtocolAPI.INSTANCE.getPlayerVersion(getBukkitPlayer())));
if(getBukkitPlayer().hasPermission("anticheat.command.alerts")
|| getBukkitPlayer().hasPermission("anticheat.alerts")) {
Check.alertsEnabled.add(getUuid());
getBukkitPlayer().spigot().sendMessage(Messages.ALERTS_ON);
}
}
protected void unload() {
@@ -111,8 +124,8 @@ public class APlayer {
.get(new Tuple<String, Class<?>>(check.getCheckData().name(), packet.getClass()));
if(methods != null) {
for (WrappedMethod method :
for (WrappedMethod method :
methods) {
method.invoke(check, packet);
}
@@ -34,7 +34,7 @@ public class PlayerRegistry {
public void unregister(UUID uuid) {
synchronized (aplayerMap) {
Optional.of(aplayerMap.remove(uuid.hashCode())).ifPresent(APlayer::unload);
Optional.ofNullable(aplayerMap.remove(uuid.hashCode())).ifPresent(APlayer::unload);
}
}
@@ -0,0 +1,7 @@
package dev.brighten.ac.handler.protocolsupport;
import org.bukkit.entity.Player;
public interface Protocol {
int getPlayerVersion(Player player);
}
@@ -0,0 +1,29 @@
package dev.brighten.ac.handler.protocolsupport;
import dev.brighten.ac.handler.protocolsupport.impl.NoAPI;
import dev.brighten.ac.handler.protocolsupport.impl.ProtocolSupport;
import dev.brighten.ac.handler.protocolsupport.impl.ViaVersionAPI;
import dev.brighten.ac.utils.Init;
import dev.brighten.ac.utils.Instance;
import org.bukkit.Bukkit;
import java.util.HashMap;
import java.util.Map;
@Init
public class ProtocolAPI {
public Map<String, Integer> protocolVersionByIP = new HashMap<>();
public static Protocol INSTANCE;
@Instance
public static ProtocolAPI classInstance;
public ProtocolAPI() {
if(Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
INSTANCE = new ViaVersionAPI();
} else if(Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport")) {
INSTANCE = new ProtocolSupport();
} else INSTANCE = new NoAPI();
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import org.bukkit.entity.Player;
public class NoAPI implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return HandlerAbstract.getHandler().getProtocolVersion(player);
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import org.bukkit.entity.Player;
import protocolsupport.api.ProtocolSupportAPI;
public class ProtocolSupport implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return ProtocolSupportAPI.getProtocolVersion(player).getId();
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.api.Via;
public class ViaVersionAPI implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return Via.getAPI().getPlayerVersion(player.getUniqueId());
}
}
@@ -77,6 +77,8 @@ public class JoinListener implements Listener {
System.out.println("Generating for " + event.getPlayer().getName());
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().generate(event.getPlayer());
HandlerAbstract.getHandler().add(event.getPlayer());
player.callEvent(event);
}
@@ -1,6 +1,5 @@
package dev.brighten.ac.packet.handler;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.reflections.Reflections;
@@ -9,9 +8,8 @@ import dev.brighten.ac.utils.reflections.types.WrappedField;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
public abstract class HandlerAbstract implements Listener {
public abstract class HandlerAbstract{
static WrappedClass classNetworkManager = Reflections.getNMSClass("NetworkManager");
static WrappedField
fieldNetworkManager = Reflections.getNMSClass("PlayerConnection").getFieldByName("networkManager"),
@@ -27,8 +25,6 @@ public abstract class HandlerAbstract implements Listener {
handler = new ModernHandler();
} else handler = new LegacyHandler();
Bukkit.getPluginManager().registerEvents(handler, Anticheat.INSTANCE);
Bukkit.getOnlinePlayers().forEach(handler::add);
}
@@ -39,4 +35,6 @@ public abstract class HandlerAbstract implements Listener {
public abstract void sendPacket(Player player, Object packet);
public abstract void sendPacket(APlayer player, Object packet);
public abstract int getProtocolVersion(Player player);
}
@@ -15,29 +15,19 @@ import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class LegacyHandler extends HandlerAbstract implements Listener {
public class LegacyHandler extends HandlerAbstract {
private final Map<String, Channel> channelCache = new HashMap<>();
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
private static WrappedField fieldChannel = new WrappedClass(NetworkManager.class).getFieldByType(Channel.class, 0);
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent event) {
if(!uninjectedChannels.contains(getChannel(event.getPlayer())))
HandlerAbstract.getHandler().add(event.getPlayer());
}
@Override
public void add(Player player) {
try {
@@ -79,6 +69,11 @@ public class LegacyHandler extends HandlerAbstract implements Listener {
sendPacket(player.getBukkitPlayer(), packet);
}
@Override
public int getProtocolVersion(Player player) {
return -1;
}
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
@@ -4,63 +4,102 @@ import com.google.common.collect.MapMaker;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.packet.wrapper.in.WPacketHandshakingInSetProtocol;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import lombok.AllArgsConstructor;
import io.netty.channel.*;
import net.minecraft.server.v1_8_R3.PacketLoginInStart;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
public class ModernHandler extends HandlerAbstract implements Listener {
public class ModernHandler extends HandlerAbstract {
private final Map<String, Channel> channelCache = new HashMap<>();
private Map<Channel, Integer> protocolLookup = new MapMaker().weakKeys().makeMap();
private final Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent event) {
if(!uninjectedChannels.contains(getChannel(event.getPlayer())))
HandlerAbstract.getHandler().add(event.getPlayer());
private ChannelInboundHandlerAdapter serverChannelHandler;
private ChannelInitializer<Channel> beginInitProtocol;
private ChannelInitializer<Channel> endInitProtocol;
public ModernHandler() {
endInitProtocol = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
try {
// Stop injecting channels
if (!Anticheat.INSTANCE.isEnabled()) {
channel.eventLoop().submit(() -> injectChannel(channel));
}
} catch (Exception e) {
Anticheat.INSTANCE.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
}
}
};
// This is executed before Minecraft's channel handler
beginInitProtocol = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(endInitProtocol);
}
};
serverChannelHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = (Channel) msg;
channel.pipeline().addFirst(beginInitProtocol);
ctx.fireChannelRead(msg);
}
};
}
@Override
public void add(Player player) {
try {
System.out.println("Adding " + player.getName() + " to packets");
Channel channel = getChannel(player);
PacketHandler handler = (PacketHandler) channel.pipeline().get(handlerName);
if(handler == null) {
handler = new PacketHandler(player);
ChannelHandlerContext context = channel.pipeline().context("packet_handler");
if(context != null) {
channel.pipeline().addBefore("packet_handler", handlerName, handler);
uninjectedChannels.remove(channel);
} else uninjectedChannels.add(channel);
}
injectChannel(channel).player = player;
} catch(IllegalArgumentException e) {
System.out.println("Error");
e.printStackTrace();
}
}
private PacketHandler injectChannel(Channel channel) {
PacketHandler handler = (PacketHandler) channel.pipeline().get(handlerName);
if(handler != null) {
channel.pipeline().remove(handlerName);
}
PacketHandler newHandler = new PacketHandler();
channel.pipeline().addBefore("packet_handler", handlerName, newHandler);
return newHandler;
}
@Override
public void remove(Player player) {
Channel channel = getChannel(player);
if (Anticheat.INSTANCE.isEnabled()) {
uninjectedChannels.add(channel);
}
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
if(channel != null)
channel.eventLoop().execute(() -> {
if(channel.pipeline().get(handlerName) != null) {
channel.pipeline().remove(handlerName);
}
});
}
@Override
@@ -73,15 +112,19 @@ public class ModernHandler extends HandlerAbstract implements Listener {
sendPacket(player.getBukkitPlayer(), packet);
}
@Override
public int getProtocolVersion(Player player) {
return protocolLookup.getOrDefault(getChannel(player), -1);
}
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> MinecraftReflection.getChannel(player));
}
}
@AllArgsConstructor
private static class PacketHandler extends ChannelDuplexHandler {
private Player player;
private final class PacketHandler extends ChannelDuplexHandler {
protected Player player;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
@@ -92,17 +135,33 @@ public class ModernHandler extends HandlerAbstract implements Listener {
PacketType type = PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN);
try {
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg,
type);
if(type == PacketType.LOGIN_START) {
PacketLoginInStart packet = (PacketLoginInStart) msg;
if(allowed) {
channelCache.put(packet.a().getName(), ctx.channel());
} else if(type == PacketType.LOGIN_HANDSHAKE) {
WPacketHandshakingInSetProtocol packet = (WPacketHandshakingInSetProtocol) PacketType.processType(type, msg);
System.out.println("Received handshake");
if(packet.getProtocol() == WPacketHandshakingInSetProtocol.EnumProtocol.LOGIN) {
System.out.println("Setting protocol version number " + packet.getVersionNumber());
protocolLookup.put(ctx.channel(), packet.getVersionNumber());
}
}
if(player != null) {
try {
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg,
type);
if (allowed) {
super.channelRead(ctx, msg);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
super.channelRead(ctx, msg);
}
} catch(Throwable throwable) {
throwable.printStackTrace();
super.channelRead(ctx, msg);
}
} else super.channelRead(ctx, msg);
}
@Override
@@ -111,17 +170,19 @@ public class ModernHandler extends HandlerAbstract implements Listener {
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
try {
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN));
if(player != null) {
try {
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN));
if (allowed) {
if (allowed) {
super.write(ctx, msg, promise);
}
} catch(Throwable throwable) {
throwable.printStackTrace();
super.write(ctx, msg, promise);
}
} catch(Throwable throwable) {
throwable.printStackTrace();
super.write(ctx, msg, promise);
}
} else super.write(ctx, msg, promise);
}
}
}
@@ -28,4 +28,6 @@ public interface PacketConverter {
WPacketPlayOutEntity processOutEntity(Object object);
WPacketPlayOutEntityTeleport processEntityTeleport(Object object);
WPacketHandshakingInSetProtocol processHandshakingProtocol(Object object);
}
@@ -63,7 +63,10 @@ public enum PacketType {
SET_SLOT("PacketPlayOutSetSlot"),
EXPLOSION("PacketPlayOutExplosion"),
ATTACH("PacketPlayOutAttachEntity"),
LOGIN_HANDSHAKE("PacketHandshakingInSetProtocol"),
STATUS_PING("PacketStatusInPing"),
STATUS_START("PacketStatusInStart"),
LOGIN_START("PacketLoginInStart"),
UNKNOWN();
PacketType(String... packetIds) {
@@ -122,6 +125,8 @@ public enum PacketType {
return convert.processOutEntity(object);
case ENTITY_TELEPORT:
return convert.processEntityTeleport(object);
case LOGIN_HANDSHAKE:
return convert.processHandshakingProtocol(object);
default:
return object;
}
@@ -219,4 +219,23 @@ public class Processor_18 implements PacketConverter {
.onGround(serial.readBoolean())
.build();
}
@Override
public WPacketHandshakingInSetProtocol processHandshakingProtocol(Object object) {
PacketDataSerializer serial = new PacketDataSerializer(Unpooled.buffer());
PacketHandshakingInSetProtocol packet = (PacketHandshakingInSetProtocol) object;
try {
packet.b(serial);
} catch (IOException e) {
throw new RuntimeException(e);
}
return WPacketHandshakingInSetProtocol.builder()
.versionNumber(serial.e())
.hostname(serial.c(32767))
.port(serial.readUnsignedShort())
.protocol(WPacketHandshakingInSetProtocol.EnumProtocol.valueOf(EnumProtocol.a(serial.e()).name()))
.build();
}
}
@@ -0,0 +1,33 @@
package dev.brighten.ac.packet.wrapper.in;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.packet.wrapper.WPacket;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class WPacketHandshakingInSetProtocol implements WPacket {
private int versionNumber, port;
private String hostname;
private EnumProtocol protocol;
@Override
public PacketType getPacketType() {
return PacketType.LOGIN_HANDSHAKE;
}
public enum EnumProtocol {
HANDSHAKING(-1),
PLAY(0),
STATUS(1),
LOGIN(2),
UNKNOWN(-69); //Not an actual vanilla object.
int id;
EnumProtocol(int id) {
this.id = id;
}
}
}