Compare commits

...
12 Commits
Author SHA1 Message Date
retrooper 643fa1ad18 updated credits to fit latest commit 2020-06-22 22:43:50 +02:00
retrooper 5c65874749 remove plugin.yml 2020-06-22 22:36:27 +02:00
purplexdev 5a4db46427 1.3.4.1 UNTESTED!
Completely removed TinyProtocol, please test it, I will be able to fix any bugs tomorrow!
2020-06-22 22:31:14 +02:00
purplexdev 305d376128 1.3.4 RELEASE, This is probably one of my most important releases, this is the last release using TinyProtocol!
Changelog:
* Clientbound Abilities wrapper added and is sendable to clients

* Clientbound Health update wrapper added and is also sendable to clients

Info:
TinyProtocol helped us listen to some login and handshake packets!
But it also came with a lot of bugs and problems to deal with, even today there are still some bugs and many people using this API have complained about it.

I will now stop using TinyProtocol.

WHAT WILL THIS MEAN?

There will be lots of pro's and con's.

Pros:
* No more issues with reloading
* Smaller file size

Cons:
* Version lookup will now only be possible with dependencies like ViaVersion.
* Soft depend on via version as mentioned above. Soft depend meaning that if you want to lookup versions, ViaVersion api is required to be provided.

* All login wrappers and the login event will be removed!

It is very important that PacketEvents is stable and people shouldn't be constantly running into issues!
I am releasing this version with TinyProtocol for the users that will miss and still want TinyProtocol.
2020-06-22 21:07:34 +02:00
purplexdev 4308936c1a * Bukkit move event added
* In and Out going transaction packet wrapper added (request of Hades developer)
2020-06-22 19:27:12 +02:00
purplexdev 87b0ac9295 * Updated the default value of PacketEvent#timestamp to the non deprecated method PacketEvents#highlyPreciseMillis() 2020-06-21 17:18:32 +02:00
purplexdev 3dee3b2a0e Bug fixes!
* Blockplace_1_9 wrapper error fixed
* Blockplace 1_7 wrapper is now being used
* PacketEvents#currentCalculatedMS() now deprecated, please use PacketEvents#highlyPreciseMillis() or System#currentTimeMillis() depending on what you need, please read the documentation of PacketEvents#highlyPreciseMillis().
2020-06-21 16:48:41 +02:00
purplexdev c2f0e30511 Updated pom.xml version to 1.3.3.1 2020-06-18 16:19:12 +02:00
purplexdev a824a70cc6 Lots of code cleaned up, appended some code to the MainExample.java, added an option to kick all online players when the PacketEvents.stop() function is called( JavaPlugin#onDisable() ) 2020-06-18 15:44:53 +02:00
purplexdev 07692f87f2 1.3.3 Release STABLE
* EventManager small optimization
* Imports optimized
* Own GameProfile class added
* New wrappers, this version is going to go on spigotmc!
2020-06-17 17:29:51 +02:00
purplexdev 7f94b254a2 LoginInEncryptionBegin & LoginOutEncryptionBegin wrapper added,
LoginStart & LoginStatusPing wrapper added,
Bug fixed with PostPlayerInjectEvent not being called,
Custom GameProfile class coded,
Main example class readded
2020-06-17 13:15:35 +02:00
purplexdev ceb3581c28 PostPlayerInjectEvent not called bug fix! 2020-06-17 11:04:16 +02:00
58 changed files with 1529 additions and 2253 deletions

No files matched your search

+3 -4
View File
@@ -2,12 +2,11 @@
A lightweight spigot packetlistener & packetsender coded by retrooper.
## Credit:
## Credit
PacketEvents uses a modified version of [TinyProtocol](http://github.com/dmulloy2/ProtocolLib/blob/master/TinyProtocol/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java) by [@DeprecatedLuke](https://github.com/DeprecatedLuke) to support 1.7.10 & 1.8.
I modified DeprecatedLuke's version of TinyProtocol and added support for 1.9+ versions.
PacketEvents took TinyProtocol's [Reflection](https://github.com/dmulloy2/ProtocolLib/blob/master/TinyProtocol/src/main/java/com/comphenix/tinyprotocol/Reflection.java) util class.
PacketEvents also took Atlas' Packet class.(all the packet names)
PacketEvents also took some parts of Atlas' [Packet](https://github.com/funkemunky/Atlas/blob/master/AtlasParent/Atlas/src/main/java/cc/funkemunky/api/tinyprotocol/api/Packet.java) class.
## About
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>io.github.retrooper</groupId>
<artifactId>packetevents</artifactId>
<version>1.3.2</version>
<version>1.3.4.1</version>
<packaging>jar</packaging>
<name>packetevents</name>
<properties>
@@ -1,50 +1,38 @@
package io.github.retrooper.packetevents;
import io.github.retrooper.packetevents.annotations.NotNull;
import io.github.retrooper.packetevents.annotations.Nullable;
import io.github.retrooper.packetevents.annotations.PacketHandler;
import io.github.retrooper.packetevents.enums.ClientVersion;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.annotations.PacketHandler;
import io.github.retrooper.packetevents.event.PacketListener;
import io.github.retrooper.packetevents.event.impl.PacketLoginEvent;
import io.github.retrooper.packetevents.event.impl.PlayerInjectEvent;
import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent;
import io.github.retrooper.packetevents.event.impl.ServerTickEvent;
import io.github.retrooper.packetevents.event.impl.*;
import io.github.retrooper.packetevents.event.manager.EventManager;
import io.github.retrooper.packetevents.handler.TinyProtocolHandler;
import io.github.retrooper.packetevents.handler.NettyPacketHandler;
import io.github.retrooper.packetevents.packet.Packet;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.packetwrappers.login.WrappedPacketLoginHandshake;
import io.github.retrooper.packetevents.utils.NMSUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import io.github.retrooper.packetevents.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
public final class PacketEvents implements PacketListener{
/*
* Login wrappers TODO
* PacketLoginInStart
* PacketLoginInEncryptionBegin
*/
private static boolean hasRegistered;
public final class PacketEvents implements PacketListener, Listener {
private static final ServerVersion version = ServerVersion.getVersion();
private static PacketEvents instance;
private static final EventManager eventManager = new EventManager();
private static boolean hasRegistered;
private static PacketEvents instance;
private static int currentTick;
private static BukkitTask serverTickTask;
private static final HashMap<Object, ClientVersion> clientVersionLookup = new HashMap<Object, ClientVersion>();
private static boolean kickOnRestart;
public static EventManager getEventManager() {
return eventManager;
@@ -61,14 +49,13 @@ public final class PacketEvents implements PacketListener{
//Register Bukkit and PacketListener
getEventManager().registerListener(getInstance());
//Initialize the TinyProtocolHandler
TinyProtocolHandler.initTinyProtocol(plugin);
Bukkit.getPluginManager().registerEvents(getInstance(), plugin);
//Start the server tick task
final Runnable tickRunnable = new Runnable() {
@Override
public void run() {
getEventManager().callEvent(new ServerTickEvent(currentTick++, PacketEvents.currentCalculatedMS()));
getEventManager().callEvent(new ServerTickEvent(currentTick++, highlyPreciseMillis()));
}
};
serverTickTask = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, tickRunnable, 0L, 1L);
@@ -77,13 +64,20 @@ public final class PacketEvents implements PacketListener{
}
/**
* Stop all tasks and unregisters all packetevent listeners
* Stop all tasks and unregisters all PacketEvents' listeners
*/
public static void stop() {
if (serverTickTask != null) {
serverTickTask.cancel();
}
getEventManager().unregisterAllListeners();
if (shouldKickOnStop()) {
for (final Player p : Bukkit.getOnlinePlayers()) {
if (p != null) {
p.kickPlayer("Please wait till the server finishes reloading/restarting.");
}
}
}
}
/**
@@ -120,7 +114,7 @@ public final class PacketEvents implements PacketListener{
/**
* Get the server's recent TPS values
* TPS stands for ticks per second.
* Learn more about ticks <a href="https://apexminecrafthosting.com/what-is-minecraft-tps/">"https://apexminecrafthosting.com/what-is-minecraft-tps/"</a>
* See to learn more about TPS<a href="https://apexminecrafthosting.com/what-is-minecraft-tps/">"https://apexminecrafthosting.com/what-is-minecraft-tps/"</a>
*
* @return recentTPS[]
*/
@@ -137,9 +131,9 @@ public final class PacketEvents implements PacketListener{
/**
* Get the server's current TPS
* TPS stands for ticks per second.
* Learn more about ticks <a href="https://apexminecrafthosting.com/what-is-minecraft-tps/">"https://apexminecrafthosting.com/what-is-minecraft-tps/"</a>
* See to learn more about TPS <a href="https://apexminecrafthosting.com/what-is-minecraft-tps/">"https://apexminecrafthosting.com/what-is-minecraft-tps/"</a>
*
* @return currentTPS / recentTPS[0]
* @return {@link PacketEvents#getRecentServerTPS()[0]}
*/
public static double getCurrentServerTPS() {
return getRecentServerTPS()[0];
@@ -152,57 +146,91 @@ public final class PacketEvents implements PacketListener{
* @param player
* @return ping
*/
public static int getPing(final Player player) {
public static int getPing(@NotNull final Player player) {
return NMSUtils.getPlayerPing(player);
}
/**
* Should I use this over System.currentTimeMillis()?
* <p>
* 1. System.currentTimeMillis() isn't supported on all machines
* 2. This is way more accurate than System.currentTimeMillis()
* 3. System.currentTimeMillis() can be up to 50ms off, depending on the operating system.
* When to use this(nano / 1 million) over {@link System#currentTimeMillis()}
* This is preciser than System.currentTimeMillis(), System.currentTimeMillis() can be up to 50ms off on some operating systems, but using this isn't cheap.
* Java documentation recommend using nano time if you are measuring elapsed time.
* In this function nanoTime is divided by 1 million giving us milliseconds.
* It is also important to mention that using this method doesn't guarantee thread safety.
*
* @return nanoTime / 1 million
*/
public static long highlyPreciseMillis() {
return System.nanoTime() / 1000000;
}
/**
* This is deprecated, use {@link #highlyPreciseMillis()} as they do the same thing, this is basically just a method rename.
*/
@Deprecated
public static long currentCalculatedMS() {
return System.nanoTime() / 1000000;
}
/**
* Get the player's version.
* Do not call this method in the PlayerInjectEvent, it is safe to call it in the PostPlayerInjectEvent.
* The EntityPlayer object is null at that time, resulting in the version lookup to fail.
*
* @param player
* / / @param player
* @return ClientVersion
*/
@Nullable
public static ClientVersion getClientVersion(final Player player) {
public static ClientVersion getClientVersion(@NotNull final Player player) {
final Object channel = TinyProtocolHandler.getPlayerChannel(player);
return clientVersionLookup.get(channel);
}
*/
/**
* Get the players' client version
* Do not call this method in the PlayerInjectEvent, it is safe to call it in the PostPlayerInjectEvent.
* The EntityPlayer object is null at that time, resulting in the version lookup to fail.
*
* @param channel
* @return ClientVersion
*/
@Nullable
public static ClientVersion getClientVersion(final Object channel) {
return clientVersionLookup.get(channel);
public static boolean shouldKickOnStop() {
return kickOnRestart;
}
@PacketHandler
public void onLogin(final PacketLoginEvent e) {
if (e.getPacketName().equals(Packet.Login.HANDSHAKE)) {
final WrappedPacketLoginHandshake handshake = new WrappedPacketLoginHandshake(e.getPacket());
final ClientVersion clientVersion = ClientVersion.fromProtocolVersion(handshake.getProtocolVersion());
clientVersionLookup.put(e.getNettyChannel(), clientVersion);
}
public static void setShouldKickOnRestart(final boolean val) {
kickOnRestart = val;
}
/**
* Version independent player injection
*
* @param player
*/
public static void injectPlayer(final Player player) {
NettyPacketHandler.injectPlayer(player);
}
/**
* Version independant player injection
*
* @param player
*/
public static void uninjectPlayer(final Player player) {
NettyPacketHandler.uninjectPlayer(player);
}
/**
* Returns whether we have injected the player
*
* @param player
* @return hasInjected
*/
public static boolean hasInjected(final Player player) {
return NettyPacketHandler.hasInjected(player);
}
/**
* Send a wrapped sendable packet to a player
*
* @param player
* @param sendable
*/
public static void sendPacket(final Player player, final Sendable sendable) {
NMSUtils.sendSendableWrapper(player, sendable);
}
/**
@@ -219,53 +247,30 @@ public final class PacketEvents implements PacketListener{
/**
* Called after the PlayerJoinEvent ONLY if the player has been injected!
*
* @param e
*/
@PacketHandler
public void onPostInject(final PostPlayerInjectEvent e) {
//It is safe to get his client version in here.
final ClientVersion clientVersion = PacketEvents.getClientVersion(e.getPlayer());
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
if (hasInjected(e.getPlayer())) {
PacketEvents.getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer()));
public void onJoin(final PlayerJoinEvent e) {
if (!hasInjected(e.getPlayer())) {
PlayerInjectEvent injectEvent = new PlayerInjectEvent(e.getPlayer());
PacketEvents.getEventManager().callEvent(injectEvent);
if(!injectEvent.isCancelled()) {
injectPlayer(e.getPlayer());
PacketEvents.getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer()));
}
}
}
/**
* Version independant player injection
* @param player
*/
public static void injectPlayer(final Player player) {
TinyProtocolHandler.inject(player);
}
/**
* Version independant player injection
* @param player
*/
public static void uninjectPlayer(final Player player) {
TinyProtocolHandler.uninject(player);
}
/**
* Returns whether we have injected the player
* @param player
* @return hasInjected
*/
public static boolean hasInjected(final Player player) {
return TinyProtocolHandler.hasInjected(player);
}
/**
* Send a wrapped sendable packet to a player
*
* @param player
* @param sendable
*/
public static void sendPacket(final Player player, final Sendable sendable) {
NMSUtils.sendSendableWrapper(player, sendable);
@EventHandler
public void onMove(final PlayerMoveEvent e) {
PacketEvents.getEventManager().callEvent(new BukkitMoveEvent(e));
}
}
@@ -1,9 +1,12 @@
package io.github.retrooper.packetevents.annotations;
import java.lang.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Is for sure not null.
* Is for sure not null.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@@ -6,7 +6,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Could be null.
* Could be null.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@@ -59,7 +59,7 @@ public enum ClientVersion {
default:
remainder = protocolVersion;
}
if(remainder > 578) {
if (remainder > 578) {
return HIGHER_THAN_V_1_15_2;
}
return INVALID;
@@ -78,18 +78,18 @@ public enum ClientVersion {
/**
* Returns if the client's version is more outdated than the argument passed version
*
* @param version
* @return
*/
public boolean isLowerThan(final ClientVersion version) {
if(version == this) return false;
if (version == this) return false;
byte len = (byte) values().length;
for (byte i = 0; i < len; i++) {
final ClientVersion v = values()[i];
if(v == this) {
if (v == this) {
return true;
}
else if(v == version) {
} else if (v == version) {
return false;
}
}
@@ -1,9 +1,7 @@
package io.github.retrooper.packetevents.enums;
public enum Direction {
DOWN, UP, NORTH, SOUTH, WEST, EAST
,NORTH_EAST,
DOWN, UP, NORTH, SOUTH, WEST, EAST, NORTH_EAST,
NORTH_WEST, SOUTH_EAST,
SOUTH_WEST, WEST_NORTH_WEST,
NORTH_NORTH_WEST, NORTH_NORTH_EAST,
@@ -8,9 +8,9 @@ public enum EntityAnimationType {
}
public final int getIndex() {
for(int i = 0; i < values().length; i++) {
for (int i = 0; i < values().length; i++) {
final EntityAnimationType curType = values()[0];
if(curType == this) {
if (curType == this) {
return i;
}
}
@@ -2,5 +2,5 @@ package io.github.retrooper.packetevents.enums;
public enum Hand {
MAIN_HAND,
OFF_HAND;
OFF_HAND
}
@@ -9,6 +9,8 @@ public enum ServerVersion {
ERROR;
private static final String nmsVersionSuffix = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
public static ServerVersion getVersion() {
ServerVersion[] vals = values();
ArrayUtils.reverse(vals);
@@ -21,8 +23,6 @@ public enum ServerVersion {
return ERROR;
}
private static final String nmsVersionSuffix = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
public static String getNmsSuffix() {
return nmsVersionSuffix;
}
@@ -53,14 +53,13 @@ public enum ServerVersion {
* @return
*/
public boolean isLowerThan(final ServerVersion version) {
if(version == this) return false;
if (version == this) return false;
byte len = (byte) values().length;
for (byte i = 0; i < len; i++) {
final ServerVersion v = values()[i];
if(v == this) {
if (v == this) {
return true;
}
else if(v == version) {
} else if (v == version) {
return false;
}
}
@@ -9,11 +9,12 @@ import io.github.retrooper.packetevents.enums.ServerVersion;
* recommended to avoid non thread safe bukkit operations
*/
public class PacketEvent {
protected long timestamp = PacketEvents.currentCalculatedMS();
protected static ServerVersion version = PacketEvents.getServerVersion();
protected long timestamp = PacketEvents.highlyPreciseMillis();
/**
* Timestamp of the packet
*
* @return
*/
public long getTimestamp() {
@@ -1,4 +1,4 @@
package io.github.retrooper.packetevents.event;
public interface PacketListener {
public interface PacketListener {
}
@@ -0,0 +1,35 @@
package io.github.retrooper.packetevents.event.impl;
import io.github.retrooper.packetevents.event.PacketEvent;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
public final class BukkitMoveEvent extends PacketEvent {
private final Player player;
private final Location to, from;
public BukkitMoveEvent(final Player player, final Location to, final Location from) {
this.player = player;
this.to = to;
this.from = from;
}
public BukkitMoveEvent(final PlayerMoveEvent e) {
this.player = e.getPlayer();
this.to = e.getTo();
this.from = e.getFrom();
}
public Player getPlayer() {
return player;
}
public Location getFrom() {
return from;
}
public Location getTo() {
return to;
}
}
@@ -1,26 +0,0 @@
package io.github.retrooper.packetevents.event.impl;
import io.github.retrooper.packetevents.event.PacketEvent;
public final class PacketLoginEvent extends PacketEvent {
private final Object channel;
private final String name;
private final Object packet;
public PacketLoginEvent(final Object channel, final String packetName, final Object packet) {
this.channel =channel;
this.name = packetName;
this.packet = packet;
}
public Object getNettyChannel() {
return channel;
}
public String getPacketName() {
return name;
}
public Object getPacket() {
return packet;
}
}
@@ -42,15 +42,6 @@ public final class PacketReceiveEvent extends PacketEvent {
return this.packet;
}
/**
* Cancel the packet
*
* @param cancelled
*/
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Returns if the packet has been cancelled
*
@@ -59,4 +50,13 @@ public final class PacketReceiveEvent extends PacketEvent {
public boolean isCancelled() {
return cancelled;
}
/**
* Cancel the packet
*
* @param cancelled
*/
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}
@@ -35,13 +35,12 @@ public final class PacketSendEvent extends PacketEvent {
return this.packet;
}
public boolean isCancelled() {
return cancelled;
}
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
public boolean isCancelled() {
return cancelled;
}
}
@@ -6,6 +6,7 @@ import org.bukkit.entity.Player;
public final class PlayerInjectEvent extends PacketEvent {
private final Player player;
private boolean cancelled;
public PlayerInjectEvent(final Player player) {
this.player = player;
}
@@ -7,7 +7,7 @@ public final class PlayerUninjectEvent extends PacketEvent {
private final Player player;
public PlayerUninjectEvent(final Player player) {
this.player =player;
this.player = player;
}
public Player getPlayer() {
@@ -3,6 +3,10 @@ package io.github.retrooper.packetevents.event.impl;
import io.github.retrooper.packetevents.event.PacketEvent;
import org.bukkit.entity.Player;
/**
* It is now safe to use PlayerInjectEvent
*/
@Deprecated
public final class PostPlayerInjectEvent extends PacketEvent {
private final Player player;
@@ -6,6 +6,7 @@ import io.github.retrooper.packetevents.event.PacketEvent;
public final class ServerTickEvent extends PacketEvent {
private final int tick;
private final double tps;
public ServerTickEvent(final int tick, final long timestamp) {
this.tick = tick;
this.timestamp = timestamp;
@@ -1,7 +1,7 @@
package io.github.retrooper.packetevents.event.manager;
import io.github.retrooper.packetevents.event.PacketEvent;
import io.github.retrooper.packetevents.annotations.PacketHandler;
import io.github.retrooper.packetevents.event.PacketEvent;
import io.github.retrooper.packetevents.event.PacketListener;
import java.lang.reflect.InvocationTargetException;
@@ -9,18 +9,22 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public final class EventManager {
private HashMap<PacketListener, List<Method>> registeredMethods = new HashMap<PacketListener, List<Method>>();
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private final HashMap<PacketListener, List<Method>> registeredMethods = new HashMap<PacketListener, List<Method>>();
public void callEvent(final PacketEvent e) {
for (final PacketListener listener : registeredMethods.keySet()) {
//Annotated methods
final List<Method> methods = registeredMethods.get(listener);
for (final Method method : methods) {
if (method.getParameterTypes()[0].equals(PacketEvent.class)
|| method.getParameterTypes()[0].getSimpleName().equals(e.getClass().getSimpleName())) {
final Class<?> parameterType = method.getParameterTypes()[0];
if (parameterType.equals(PacketEvent.class)
|| parameterType.isInstance(e)) {
try {
method.invoke(listener, e);
} catch (IllegalAccessException | InvocationTargetException ex) {
@@ -31,15 +35,20 @@ public final class EventManager {
}
}
public void registerListener(final PacketListener e) {
if (registeredMethods.containsKey(e)) {
return;
}
final List<Method> methods = new ArrayList<Method>();
for (final Method m : e.getClass().getMethods()) {
if (m.isAnnotationPresent(PacketHandler.class)) {
if (m.isAnnotationPresent(PacketHandler.class)
&& m.getParameterTypes().length == 1) {
methods.add(m);
}
}
registeredMethods.put(e, methods);
if (!methods.isEmpty()) {
registeredMethods.put(e, methods);
}
}
public void unregisterListener(final PacketListener e) {
@@ -50,7 +59,7 @@ public final class EventManager {
registeredMethods.clear();
}
public boolean hasRegistered(final PacketListener listener) {
public boolean isRegistered(final PacketListener listener) {
return registeredMethods.containsKey(listener);
}
}
@@ -0,0 +1,24 @@
package io.github.retrooper.packetevents.example;
import io.github.retrooper.packetevents.PacketEvents;
import org.bukkit.plugin.java.JavaPlugin;
public class MainExample extends JavaPlugin {
@Override
public void onEnable() {
PacketEvents.start(this);
PacketEvents.getServerTickTask().cancel();
//To fix bugs it is recommend to set this to TRUE
PacketEvents.setShouldKickOnRestart(true);
}
@Override
public void onDisable() {
PacketEvents.stop(); //it kicks when you call the stop() function
}
}
@@ -0,0 +1,67 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.event.impl.PacketReceiveEvent;
import io.github.retrooper.packetevents.event.impl.PacketSendEvent;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.UUID;
import java.util.concurrent.Future;
public class NettyPacketHandler {
public static final String handlerName = "PacketEventsListener";
private static final ServerVersion version = PacketEvents.getServerVersion();
public static final HashSet<UUID> injectedList = new HashSet<UUID>();
public static void injectPlayer(final Player player) {
if (!hasInjected(player)) {
if (version.isLowerThan(ServerVersion.v_1_8)) {
NettyPacketHandler_7.injectPlayer(player);
} else {
NettyPacketHandler_8.injectPlayer(player);
}
injectedList.add(player.getUniqueId());
}
}
public static Future<?> uninjectPlayer(final Player player) {
if (hasInjected(player)) {
injectedList.remove(player.getUniqueId());
if (version.isLowerThan(ServerVersion.v_1_8)) {
return NettyPacketHandler_7.uninjectPlayer(player);
} else {
return NettyPacketHandler_8.uninjectPlayer(player);
}
}
return null;
}
public static boolean hasInjected(final Player player) {
return injectedList.contains(player.getUniqueId());
}
public static Object read(final Player receiver, final Object packet) {
final String packetName = packet.getClass().getSimpleName();
final PacketSendEvent packetSendEvent = new PacketSendEvent(receiver, packetName, packet);
PacketEvents.getEventManager().callEvent(packetSendEvent);
if (!packetSendEvent.isCancelled()) {
return packet;
}
return null;
}
public static Object write(final Player sender, final Object packet) {
final String packetName = packet.getClass().getSimpleName();
final PacketReceiveEvent packetReceiveEvent = new PacketReceiveEvent(sender, packetName, packet);
PacketEvents.getEventManager().callEvent(packetReceiveEvent);
if (!packetReceiveEvent.isCancelled()) {
return packet;
}
return null;
}
}
@@ -0,0 +1,44 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.utils.NMSUtils;
import net.minecraft.util.io.netty.channel.*;
import org.bukkit.entity.Player;
import java.util.concurrent.Future;
final class NettyPacketHandler_7 {
public static void injectPlayer(final Player player) {
final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object packet = NettyPacketHandler.read(player, msg);
if(packet == null) {
return;
}
super.channelRead(ctx, msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object packet = NettyPacketHandler.write(player, msg);
if(packet == null) {
return;
}
super.write(ctx, msg, promise);
}
};
final ChannelPipeline pipeline = ((Channel) NMSUtils.getChannel(player)).pipeline();
pipeline.addBefore(NettyPacketHandler.handlerName, player.getName(), channelDuplexHandler);
}
public static Future<?> uninjectPlayer(final Player player) {
final Channel channel = (Channel) NMSUtils.getChannel(player);
return channel.eventLoop().submit(new Runnable() {
@Override
public void run() {
channel.pipeline().remove(player.getName());
}
});
}
}
@@ -0,0 +1,43 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.utils.NMSUtils;
import io.netty.channel.*;
import org.bukkit.entity.Player;
import java.util.concurrent.Future;
final class NettyPacketHandler_8 {
public static void injectPlayer(final Player player) {
final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object packet = NettyPacketHandler.read(player, msg);
if(packet == null) {
return;
}
super.channelRead(ctx, msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object packet = NettyPacketHandler.write(player, msg);
if(packet == null) {
return;
}
super.write(ctx, msg, promise);
}
};
final ChannelPipeline pipeline = ((Channel) NMSUtils.getChannel(player)).pipeline();
pipeline.addBefore(NettyPacketHandler.handlerName, player.getName(), channelDuplexHandler);
}
public static Future<?> uninjectPlayer(final Player player) {
final Channel channel = (Channel) NMSUtils.getChannel(player);
return channel.eventLoop().submit(new Runnable() {
@Override
public void run() {
channel.pipeline().remove(player.getName());
}
});
}
}
@@ -1,92 +0,0 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.tinyprotocol.Reflection;
import io.github.retrooper.packetevents.tinyprotocol.Reflection.FieldAccessor;
import io.github.retrooper.packetevents.utils.NMSUtils;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public final class TinyProtocolHandler {
private static final ServerVersion version = PacketEvents.getServerVersion();
private static Object tinyProtocol;
public static void initTinyProtocol(final Plugin plugin) {
if (version.isLowerThan(ServerVersion.v_1_8)) {
TinyProtocolHandler_1_7 tiny = new TinyProtocolHandler_1_7(plugin);
tiny.initTinyProtocol();
tinyProtocol = tiny;
} else {
TinyProtocolHandler_1_8 tiny = new TinyProtocolHandler_1_8(plugin);
tiny.initTinyProtocol();
tinyProtocol = tiny;
}
}
public static boolean hasInjected(final Player player) {
if (version.isLowerThan(ServerVersion.v_1_8)) {
TinyProtocolHandler_1_7 tiny = (TinyProtocolHandler_1_7) tinyProtocol;
assert tiny.tinyProtocol != null;
return tiny.tinyProtocol.hasInjected(player);
} else {
TinyProtocolHandler_1_8 tiny = (TinyProtocolHandler_1_8) tinyProtocol;
assert tiny.tinyProtocol != null;
return tiny.tinyProtocol.hasInjected(player);
}
}
public static void inject(final Player player) {
if (version.isLowerThan(ServerVersion.v_1_8)) {
TinyProtocolHandler_1_7 tiny = (TinyProtocolHandler_1_7) tinyProtocol;
assert tiny.tinyProtocol != null;
tiny.tinyProtocol.injectPlayer(player);
} else {
TinyProtocolHandler_1_8 tiny = (TinyProtocolHandler_1_8) tinyProtocol;
assert tiny.tinyProtocol != null;
tiny.tinyProtocol.injectPlayer(player);
}
}
public static void uninject(final Player player) {
if (version.isLowerThan(ServerVersion.v_1_8)) {
TinyProtocolHandler_1_7 tiny = (TinyProtocolHandler_1_7) tinyProtocol;
assert tiny.tinyProtocol != null;
tiny.tinyProtocol.uninjectPlayer(player);
} else {
TinyProtocolHandler_1_8 tiny = (TinyProtocolHandler_1_8) tinyProtocol;
assert tiny.tinyProtocol != null;
tiny.tinyProtocol.uninjectPlayer(player);
}
}
public static Object getPlayerChannel(final Player player) {
final Object entityPlayer = NMSUtils.getEntityPlayer(player);
final Object playerConnection = playerConnectionAccessor.get(entityPlayer);
final Object networkManager = networkManagerAccessor.get(playerConnection);
return nettyChannelAccessor.get(networkManager);
}
private static Class<?> networkManagerClass;
private static final FieldAccessor<?> playerConnectionAccessor, networkManagerAccessor, nettyChannelAccessor;
private static Class<?> channelClass;
static {
try {
networkManagerClass = NMSUtils.getNMSClass("NetworkManager");
channelClass = NMSUtils.getNettyClass("channel.Channel");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
playerConnectionAccessor = Reflection.getField(NMSUtils.entityPlayerClass, NMSUtils.playerConnectionClass, 0);
networkManagerAccessor = Reflection.getField(NMSUtils.playerConnectionClass, networkManagerClass, 0);
nettyChannelAccessor = Reflection.getField(networkManagerClass, channelClass, 0);
}
}
@@ -1,60 +0,0 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PacketLoginEvent;
import io.github.retrooper.packetevents.event.impl.PacketReceiveEvent;
import io.github.retrooper.packetevents.event.impl.PacketSendEvent;
import io.github.retrooper.packetevents.packet.Packet;
import io.github.retrooper.packetevents.tinyprotocol.TinyProtocol7;
import net.minecraft.util.io.netty.channel.Channel;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import io.github.retrooper.packetevents.annotations.Nullable;
public final class TinyProtocolHandler_1_7 {
private final Plugin plugin;
public TinyProtocolHandler_1_7(final Plugin plugin) {
this.plugin = plugin;
}
@Nullable
public TinyProtocol7 tinyProtocol;
public void initTinyProtocol() {
tinyProtocol = new TinyProtocol7(getPlugin()) {
@Override
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
final String packetName = packet.getClass().getSimpleName();
final PacketSendEvent packetSendEvent = new PacketSendEvent(receiver, packetName, packet);
PacketEvents.getEventManager().callEvent(packetSendEvent);
if (!packetSendEvent.isCancelled()) {
return packet;
}
return null;
}
@Override
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
final String packetName = packet.getClass().getSimpleName();
for(final String loginPacket : Packet.Login.LOGIN_PACKETS) {
if(packetName.equals(loginPacket)) {
PacketEvents.getEventManager().callEvent(new PacketLoginEvent(channel, packetName, packet));
return packet;
}
}
final PacketReceiveEvent packetReceiveEvent = new PacketReceiveEvent(sender, packetName, packet);
PacketEvents.getEventManager().callEvent(packetReceiveEvent);
if (!packetReceiveEvent.isCancelled()) {
return packet;
}
return null;
}
};
}
public Plugin getPlugin() {
return plugin;
}
}
@@ -1,85 +0,0 @@
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.event.impl.PacketLoginEvent;
import io.github.retrooper.packetevents.event.impl.PacketReceiveEvent;
import io.github.retrooper.packetevents.event.impl.PacketSendEvent;
import io.github.retrooper.packetevents.packet.Packet;
import io.github.retrooper.packetevents.tinyprotocol.TinyProtocol8;
import io.netty.channel.Channel;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import io.github.retrooper.packetevents.annotations.Nullable;
public final class TinyProtocolHandler_1_8 {
private static final ServerVersion version = PacketEvents.getServerVersion();
private final Plugin plugin;
public TinyProtocolHandler_1_8(final Plugin plugin) {
this.plugin = plugin;
}
@Nullable
public TinyProtocol8 tinyProtocol;
public void initTinyProtocol() {
tinyProtocol = new TinyProtocol8(getPlugin()) {
@Override
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
final String packetName = packet.getClass().getSimpleName();
final PacketSendEvent packetSendEvent = new PacketSendEvent(receiver, packetName, packet);
PacketEvents.getEventManager().callEvent(packetSendEvent);
if (!packetSendEvent.isCancelled()) {
return packet;
}
return null;
}
@Override
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
final String packetName = packet.getClass().getSimpleName();
for (final String loginPacket : Packet.Login.LOGIN_PACKETS) {
if (packetName.equals(loginPacket)) {
PacketEvents.getEventManager().callEvent(new PacketLoginEvent(channel, packetName, packet));
return packet;
}
}
final PacketReceiveEvent packetReceiveEvent = new PacketReceiveEvent(sender, packetName, packet);
PacketEvents.getEventManager().callEvent(packetReceiveEvent);
if (!packetReceiveEvent.isCancelled()) {
return packet;
}
return null;
}
};
}
public static String getNetworkManagersFieldName() {
//1.8
if (version.equals(ServerVersion.v_1_8)) {
return "g";
}
//1.8.3->1.12
else if (version.isLowerThan(ServerVersion.v_1_13)) {
return "h";
}
//1.13->1.14
else if (version.isLowerThan(ServerVersion.v_1_15)) {
return "g";
}
//1.15
else {
return "listeningChannels";
}
}
public Plugin getPlugin() {
return plugin;
}
}
@@ -0,0 +1,82 @@
package io.github.retrooper.packetevents.mojang;
import com.mojang.authlib.properties.PropertyMap;
import org.bukkit.craftbukkit.libs.org.apache.commons.lang3.StringUtils;
import org.bukkit.craftbukkit.libs.org.apache.commons.lang3.builder.ToStringBuilder;
import java.util.UUID;
public class GameProfile {
private final UUID id;
private final String name;
private final PropertyMap properties = new PropertyMap();
private boolean legacy;
public GameProfile(UUID id, String name) {
if (id == null && StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Name and ID cannot both be blank");
} else {
this.id = id;
this.name = name;
}
}
public UUID getId() {
return this.id;
}
public String getName() {
return this.name;
}
public PropertyMap getProperties() {
return this.properties;
}
public boolean isComplete() {
return this.id != null && StringUtils.isNotBlank(this.getName());
}
public boolean equalsMojangGameProfile(com.mojang.authlib.GameProfile profile) {
if (profile != null) {
return profile.getId() != null && getId() != null && profile.getId() == getId()
&& profile.getName() != null && getName() != null && profile.getName().equals(getName());
}
return false;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
GameProfile that = (GameProfile) o;
if (this.id != null) {
if (!this.id.equals(that.id)) {
return false;
}
} else if (that.id != null) {
return false;
}
if (this.name != null) {
return this.name.equals(that.name);
} else return that.name == null;
} else {
return false;
}
}
public int hashCode() {
int result = this.id != null ? this.id.hashCode() : 0;
result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
return result;
}
public String toString() {
return (new ToStringBuilder(this)).append("id", this.id).append("name", this.name).append("properties", this.properties).append("legacy", this.legacy).toString();
}
public boolean isLegacy() {
return this.legacy;
}
}
@@ -5,6 +5,58 @@ import io.github.retrooper.packetevents.utils.NMSUtils;
public abstract class Packet {
private static Class<?> packetPlayInFlying, packetPlayInPosition, packetPlayInPositionLook, packetPlayInLook;
static {
try {
packetPlayInFlying = NMSUtils.getNMSClass(Client.FLYING);
} catch (ClassNotFoundException e) {
//That is fine, they are on 1.9+ so we just initiate the rest of the variables
try {
packetPlayInPosition = NMSUtils.getNMSClass(Client.POSITION);
packetPlayInPositionLook = NMSUtils.getNMSClass(Client.POSITION_LOOK);
packetPlayInLook = NMSUtils.getNMSClass(Client.LOOK);
} catch (ClassNotFoundException e2) {
e2.printStackTrace();
}
}
}
/**
* Is this NMS packet an instanceof the "PacketPlayInFlying" packet in the 1.8 protocol?
* Supports 1.9->1.15.2 too!
*
* @param nmsPacket
* @return If the nms packet provided is an instanceof the "PacketPlayInFlying" packet in the 1.8 protocol!
*/
public static boolean isInstanceOfFlyingPacket(Object nmsPacket) {
//1.7->1.8.8
if (packetPlayInFlying != null) {
return packetPlayInFlying.isInstance(nmsPacket);
} else {
//1.9->1.15.2
return packetPlayInPosition.isInstance(nmsPacket)
|| packetPlayInPositionLook.isInstance(nmsPacket)
|| packetPlayInLook.isInstance(nmsPacket);
}
}
/**
* Use isInstanceOfFlyingPacket(nmsPacketObject) instead of this, as the flying packet is the only packet that has subclasses (in 1.8 atleast).
* Will be removed in newer versions of PacketEvents!
*
* @param fatherPacket
* @param childPacket
* @return isInstanceOf
*/
@Deprecated
public static boolean isInstanceOf(String fatherPacket, String childPacket) {
if (fatherPacket.equals(Client.FLYING) && childPacket.equals(Client.POSITION) || childPacket.equals(Client.POSITION_LOOK) || childPacket.equals(Client.LOOK)) {
return true;
}
return fatherPacket.equals(childPacket);
}
public static class Client {
private static final String c = "PacketPlayIn";
@@ -77,58 +129,10 @@ public abstract class Packet {
public static final String PING = "PacketStatusInPing";
public static final String START = "PacketStatusInStart";
public static final String SUCCESS = "PacketLoginOutSuccess";
public static final String ENCRYPTION_BEGIN_IN = "PacketLoginInEncryptionBegin";
public static final String ENCRYPTION_BEGIN_OUT = "PacketLoginOutEncryptionBegin";
public static final String[] LOGIN_PACKETS = new String[]{"PacketHandshakingInSetProtocol", "PacketLoginInStart", "PacketLoginInEncryptionBegin"};
}
/**
* Is this NMS packet an instanceof the "PacketPlayInFlying" packet in the 1.8 protocol?
* Supports 1.9->1.15.2 too!
* @param nmsPacket
* @return If the nms packet provided is an instanceof the "PacketPlayInFlying" packet in the 1.8 protocol!
*/
public static boolean isInstanceOfFlyingPacket(Object nmsPacket) {
//1.7->1.8.8
if (packetPlayInFlying != null) {
return packetPlayInFlying.isInstance(nmsPacket);
} else {
//1.9->1.15.2
return packetPlayInPosition.isInstance(nmsPacket)
|| packetPlayInPositionLook.isInstance(nmsPacket)
|| packetPlayInLook.isInstance(nmsPacket);
}
}
/**
* Use isInstanceOfFlyingPacket(nmsPacketObject) instead of this, as the flying packet is the only packet that has subclasses (in 1.8 atleast).
* Will be removed in newer versions of PacketEvents!
* @param fatherPacket
* @param childPacket
* @return isInstanceOf
*/
@Deprecated
public static boolean isInstanceOf(String fatherPacket, String childPacket) {
if (fatherPacket.equals(Client.FLYING) && childPacket.equals(Client.POSITION) || childPacket.equals(Client.POSITION_LOOK) || childPacket.equals(Client.LOOK)) {
return true;
}
return fatherPacket.equals(childPacket);
}
private static Class<?> packetPlayInFlying, packetPlayInPosition, packetPlayInPositionLook, packetPlayInLook;
static {
try {
packetPlayInFlying = NMSUtils.getNMSClass(Client.FLYING);
} catch (ClassNotFoundException e) {
//That is fine, they are on 1.9+ so we just initiate the rest of the variables
try {
packetPlayInPosition = NMSUtils.getNMSClass(Client.POSITION);
packetPlayInPositionLook = NMSUtils.getNMSClass(Client.POSITION_LOOK);
packetPlayInLook = NMSUtils.getNMSClass(Client.LOOK);
} catch (ClassNotFoundException e2) {
e2.printStackTrace();
}
}
}
}
@@ -1,5 +1,5 @@
package io.github.retrooper.packetevents.packetwrappers;
public interface Sendable {
public Object asNMSPacket();
Object asNMSPacket();
}
@@ -4,13 +4,13 @@ import io.github.retrooper.packetevents.enums.ServerVersion;
import org.bukkit.entity.Player;
public class WrappedPacket {
protected static ServerVersion version = ServerVersion.getVersion();
protected final Player player;
protected Object packet;
protected static ServerVersion version = ServerVersion.getVersion();
public WrappedPacket(final Object packet) {
this.player = null;
if(packet == null)return;
if (packet == null) return;
this.packet = packet;
try {
setup();
@@ -21,7 +21,7 @@ public class WrappedPacket {
public WrappedPacket(final Player player, final Object packet) {
this.player = player;
if(packet == null)return;
if (packet == null) return;
this.packet = packet;
try {
setup();
@@ -30,7 +30,7 @@ public class WrappedPacket {
}
}
protected void setup() {
protected void setup() {
}
@@ -6,6 +6,36 @@ import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Field;
public final class WrappedPacketInAbilities extends WrappedPacket {
private static Class<?> abilitiesClass;
private static final Field[] fields = new Field[6];
static {
try {
abilitiesClass = NMSUtils.getNMSClass("PacketPlayInAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
fields[0] = abilitiesClass.getDeclaredField("a");
fields[1] = abilitiesClass.getDeclaredField("b");
fields[2] = abilitiesClass.getDeclaredField("c");
fields[3] = abilitiesClass.getDeclaredField("d");
fields[4] = abilitiesClass.getDeclaredField("e");
fields[5] = abilitiesClass.getDeclaredField("f");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (Field f : fields) {
if (f != null) {
f.setAccessible(true);
}
}
}
private boolean isVulnerable;
private boolean isFlying;
private boolean allowFly;
@@ -39,6 +69,15 @@ public final class WrappedPacketInAbilities extends WrappedPacket {
return isFlying;
}
public boolean isFlightAllowed() {
return allowFly;
}
/**
* Use {@link WrappedPacketInAbilities#isFlightAllowed()}
* @return
*/
@Deprecated
public boolean canFly() {
return allowFly;
}
@@ -54,35 +93,4 @@ public final class WrappedPacketInAbilities extends WrappedPacket {
public float getWalkSpeed() {
return walkSpeed;
}
private static Class<?> abilitiesClass;
private static Field[] fields = new Field[6];
static {
try {
abilitiesClass = NMSUtils.getNMSClass("PacketPlayInAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
fields[0] = abilitiesClass.getDeclaredField("a");
fields[1] = abilitiesClass.getDeclaredField("b");
fields[2] = abilitiesClass.getDeclaredField("c");
fields[3] = abilitiesClass.getDeclaredField("d");
fields[4] = abilitiesClass.getDeclaredField("e");
fields[5] = abilitiesClass.getDeclaredField("f");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (Field f : fields) {
if (f != null) {
f.setAccessible(true);
}
}
}
}
@@ -4,86 +4,21 @@ import io.github.retrooper.packetevents.enums.Direction;
import io.github.retrooper.packetevents.enums.PlayerDigType;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.tinyprotocol.Reflection;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import java.lang.reflect.Field;
public final class WrappedPacketInBlockDig extends WrappedPacket {
private Vector3i blockPosition;
private Direction direction;
private PlayerDigType digType;
public WrappedPacketInBlockDig(Object packet) {
super(packet);
}
@Override
protected void setup() {
Direction enumDirection = null;
PlayerDigType enumDigType = null;
//1.7.10
try {
if (version == ServerVersion.v_1_7_10) {
final int x = fields[0].getInt(packet);
final int y = fields[1].getInt(packet);
final int z = fields[2].getInt(packet);
this.blockPosition = new Vector3i(x, y, z);
enumDirection = Direction.get((byte) face.getInt(packet));
enumDigType = PlayerDigType.get((byte) e.getInt(packet));
} else {
//1.8+
final Object blockPosObj = fields[0].get(packet);
final Object enumDirectionObj = fields[1].get(packet);
final Object digTypeObj = fields[2].get(packet);
final int x = blockPosXYZ[0].get(blockPosObj);
final int y = blockPosXYZ[1].get(blockPosObj);
final int z = blockPosXYZ[2].get(blockPosObj);
this.blockPosition = new Vector3i(x, y, z);
enumDirection = Direction.valueOf(((Enum) enumDirectionObj).name());
enumDigType = PlayerDigType.valueOf(((Enum) digTypeObj).name());
}
}
catch(IllegalAccessException e) {
e.printStackTrace();
}
this.direction = enumDirection;
this.digType = enumDigType;
}
public Vector3i getBlockPosition() {
return blockPosition;
}
public Direction getDirection() {
return direction;
}
public PlayerDigType getDigType() {
return digType;
}
//ALL
private static Class<?> blockDigClass;
//1.8
private static Field[] fields = new Field[3];
private static final Field[] fields = new Field[3];
private static Class<?> blockPositionClass;
private static Reflection.FieldAccessor<Integer>[] blockPosXYZ = new Reflection.FieldAccessor[3];
private static final Reflection.FieldAccessor<Integer>[] blockPosXYZ = new Reflection.FieldAccessor[3];
//1.7
private static Field face;
private static Field e;
static {
@@ -118,20 +53,77 @@ public final class WrappedPacketInBlockDig extends WrappedPacket {
e.printStackTrace();
}
for(Field f : fields) {
if(f != null) {
for (Field f : fields) {
if (f != null) {
f.setAccessible(true);
}
}
if(e != null) {
if (e != null) {
e.setAccessible(true);
}
if(face != null) {
if (face != null) {
face.setAccessible(true);
}
}
private Vector3i blockPosition;
private Direction direction;
private PlayerDigType digType;
public WrappedPacketInBlockDig(Object packet) {
super(packet);
}
@Override
protected void setup() {
Direction enumDirection = null;
PlayerDigType enumDigType = null;
//1.7.10
try {
if (version == ServerVersion.v_1_7_10) {
final int x = fields[0].getInt(packet);
final int y = fields[1].getInt(packet);
final int z = fields[2].getInt(packet);
this.blockPosition = new Vector3i(x, y, z);
enumDirection = Direction.get((byte) face.getInt(packet));
enumDigType = PlayerDigType.get((byte) e.getInt(packet));
} else {
//1.8+
final Object blockPosObj = fields[0].get(packet);
final Object enumDirectionObj = fields[1].get(packet);
final Object digTypeObj = fields[2].get(packet);
final int x = blockPosXYZ[0].get(blockPosObj);
final int y = blockPosXYZ[1].get(blockPosObj);
final int z = blockPosXYZ[2].get(blockPosObj);
this.blockPosition = new Vector3i(x, y, z);
enumDirection = Direction.valueOf(((Enum) enumDirectionObj).name());
enumDigType = PlayerDigType.valueOf(((Enum) digTypeObj).name());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
this.direction = enumDirection;
this.digType = enumDigType;
}
public Vector3i getBlockPosition() {
return blockPosition;
}
public Direction getDirection() {
return direction;
}
public PlayerDigType getDigType() {
return digType;
}
}
@@ -22,22 +22,28 @@ public final class WrappedPacketInBlockPlace extends WrappedPacket {
Vector3i position = null;
ItemStack itemStack = null;
try {
if (version.isHigherThan(ServerVersion.v_1_8_8)) {
final WrappedPacketInBlockPlace_1_9 updatedBlockPlace = new WrappedPacketInBlockPlace_1_9(getPlayer(), packet);
final Block block = updatedBlockPlace.getBlock();
position = new Vector3i(block.getX(), block.getY(), block.getZ());
itemStack = new ItemStack(block.getType());
} else {
final WrappedPacketInBlockPlace_1_8 legacyBlockPlace = new WrappedPacketInBlockPlace_1_8(packet);
position = legacyBlockPlace.getBlockPosition();
itemStack = legacyBlockPlace.getItemStack();
} }
catch(IllegalArgumentException e) {
if (version.isHigherThan(ServerVersion.v_1_8_8)) {
final WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(getPlayer(), packet);
final Block block = blockPlace_1_9.getBlock();
position = new Vector3i(block.getX(), block.getY(), block.getZ());
itemStack = new ItemStack(block.getType());
} else if(version.isHigherThan(ServerVersion.v_1_7_10)){
final WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
position = blockPlace_1_8.getBlockPosition();
itemStack = blockPlace_1_8.getItemStack();
}
else {
final WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(getPlayer(), packet);
position = blockPlace_1_7_10.getBlockPosition();
this.itemStack = blockPlace_1_7_10.getItemStack();
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
this.blockPosition = position;
this.itemStack = itemStack;
}
public Player getPlayer() {
return this.player;
}
@@ -53,7 +59,7 @@ public final class WrappedPacketInBlockPlace extends WrappedPacket {
static {
if (version.isHigherThan(ServerVersion.v_1_8_8)) {
WrappedPacketInBlockPlace_1_9.initStatic();
} else if(version.isLowerThan(ServerVersion.v_1_9) && version.isHigherThan(ServerVersion.v_1_7_10)){
} else if (version.isHigherThan(ServerVersion.v_1_7_10)) {
WrappedPacketInBlockPlace_1_8.initStatic();
}
}
@@ -10,6 +10,7 @@ final class WrappedPacketInBlockPlace_1_7_10 extends WrappedPacket {
private Vector3i blockPosition;
private ItemStack itemStack;
private int blockFace;
WrappedPacketInBlockPlace_1_7_10(final Player player, final Object packet) {
super(player, packet);
}
@@ -17,14 +18,14 @@ final class WrappedPacketInBlockPlace_1_7_10 extends WrappedPacket {
@Override
protected void setup() {
final PacketPlayInBlockPlace blockPlace = (PacketPlayInBlockPlace)packet;
final PacketPlayInBlockPlace blockPlace = (PacketPlayInBlockPlace) packet;
this.blockPosition = new Vector3i(blockPlace.c(), blockPlace.d(), blockPlace.e());
this.blockFace = blockPlace.d();
net.minecraft.server.v1_7_R4.ItemStack stack = blockPlace.getItemStack();
this.itemStack = org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asBukkitCopy(stack);
this.itemStack = org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asBukkitCopy(stack);
}
public Vector3i getBlockPosition() {
@@ -1,18 +1,40 @@
package io.github.retrooper.packetevents.packetwrappers.in.blockplace;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.tinyprotocol.Reflection;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.inventory.ItemStack;
final class WrappedPacketInBlockPlace_1_8 extends WrappedPacket {
private static final Reflection.MethodInvoker[] blockPositionXYZInvoker = new Reflection.MethodInvoker[3];
private static Class<?> blockPlaceClass, blockPositionClass;
private static Reflection.FieldAccessor<?> blockPositionAccessor, itemStackFieldAccessor;
private Vector3i blockPosition;
private ItemStack itemStack;
WrappedPacketInBlockPlace_1_8(final Object packet) {
super(packet);
}
protected static void initStatic() {
try {
blockPlaceClass = NMSUtils.getNMSClass("PacketPlayInBlockPlace");
blockPositionClass = NMSUtils.getNMSClass("BlockPosition");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
itemStackFieldAccessor = Reflection.getField(blockPlaceClass, NMSUtils.nmsItemStackClass, 0);
blockPositionAccessor = Reflection.getField(blockPlaceClass, blockPositionClass, 1);
blockPositionXYZInvoker[0] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getX");
blockPositionXYZInvoker[1] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getY");
blockPositionXYZInvoker[2] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getZ");
}
@Override
protected void setup() {
Object nmsBlockPos = blockPositionAccessor.get(packet);
@@ -33,27 +55,4 @@ final class WrappedPacketInBlockPlace_1_8 extends WrappedPacket {
public ItemStack getItemStack() {
return itemStack;
}
private static Class<?> blockPlaceClass, blockPositionClass;
private static Reflection.FieldAccessor<?> blockPositionAccessor, itemStackFieldAccessor;
private static final Reflection.MethodInvoker[] blockPositionXYZInvoker = new Reflection.MethodInvoker[3];
protected static void initStatic(){
try {
blockPlaceClass = NMSUtils.getNMSClass("PacketPlayInBlockPlace");
blockPositionClass = NMSUtils.getNMSClass("BlockPosition");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
itemStackFieldAccessor = Reflection.getField(blockPlaceClass, NMSUtils.nmsItemStackClass, 0);
blockPositionAccessor = Reflection.getField(blockPlaceClass, blockPositionClass, 1);
blockPositionXYZInvoker[0] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getX");
blockPositionXYZInvoker[1] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getY");
blockPositionXYZInvoker[2] = Reflection.getMethod(blockPositionClass.getSuperclass(), "getZ");
}
}
@@ -10,15 +10,24 @@ import org.bukkit.entity.Player;
import java.util.HashSet;
final class WrappedPacketInBlockPlace_1_9 extends WrappedPacket {
private static Class<?> blockPlaceClass_1_9;
private Block block;
public WrappedPacketInBlockPlace_1_9(final Player player, final Object packet) {
super(player, packet);
}
protected static void initStatic() {
try {
blockPlaceClass_1_9 = NMSUtils.getNMSClass("PacketPlayInUseItem");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
protected void setup() {
this.block = getPlayer().getTargetBlock((HashSet<Material>)null, 3);
this.block = getPlayer().getTargetBlock((HashSet<Material>) null, 3);
}
@@ -30,14 +39,5 @@ final class WrappedPacketInBlockPlace_1_9 extends WrappedPacket {
return block;
}
private static Class<?> blockPlaceClass_1_9;
protected static void initStatic() {
try {
blockPlaceClass_1_9 = NMSUtils.getNMSClass("PacketPlayInUseItem");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@@ -7,28 +7,7 @@ import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Field;
public final class WrappedPacketInChat extends WrappedPacket {
private String message;
public WrappedPacketInChat(Object packet) {
super(packet);
}
@Override
protected void setup() {
try {
final Object obj = field.get(packet);
this.message = obj.toString();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public String getMessage() {
return message;
}
private static Class<?> chatPacketClass;
private static Field field;
static {
@@ -48,10 +27,30 @@ public final class WrappedPacketInChat extends WrappedPacket {
field.setAccessible(true);
}
private String message;
public WrappedPacketInChat(Object packet) {
super(packet);
}
private static String getChatFieldName() {
if (version == ServerVersion.v_1_7_10) {
return "message";
}
return "a";
}
@Override
protected void setup() {
try {
final Object obj = field.get(packet);
this.message = obj.toString();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public String getMessage() {
return message;
}
}
@@ -1,15 +1,49 @@
package io.github.retrooper.packetevents.packetwrappers.in.entityaction;
import io.github.retrooper.packetevents.annotations.Nullable;
import io.github.retrooper.packetevents.enums.PlayerAction;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.utils.NMSUtils;
import org.bukkit.entity.Entity;
import io.github.retrooper.packetevents.annotations.Nullable;
import java.lang.reflect.Field;
public final class WrappedPacketInEntityAction extends WrappedPacket {
private static Class<?> entityActionClass;
@Nullable
private static Class<?> enumPlayerActionClass;
private static final Field[] fields = new Field[3];
@Nullable
private static Object[] enumPlayerActionConstants;
static {
try {
entityActionClass = NMSUtils.getNMSClass("PacketPlayInEntityAction");
if (version.isHigherThan(ServerVersion.v_1_7_10)) {
enumPlayerActionClass = NMSUtils.getNMSClass("PacketPlayInEntityAction$EnumPlayerAction");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//ints in 1.7.10, EnumPlayerAction in 1.8+
fields[0] = entityActionClass.getDeclaredField("a");
fields[1] = entityActionClass.getDeclaredField("animation");
fields[2] = entityActionClass.getDeclaredField("c");
if (version.isHigherThan(ServerVersion.v_1_7_10)) {
enumPlayerActionConstants = enumPlayerActionClass.getEnumConstants();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
private Entity entity;
private int entityId;
private PlayerAction action;
@@ -71,41 +105,4 @@ public final class WrappedPacketInEntityAction extends WrappedPacket {
public int getJumpBoost() {
return jumpBoost;
}
private static Class<?> entityActionClass;
@Nullable
private static Class<?> enumPlayerActionClass;
private static Field[] fields = new Field[3];
@Nullable
private static Object[] enumPlayerActionConstants;
static {
try {
entityActionClass = NMSUtils.getNMSClass("PacketPlayInEntityAction");
if (version.isHigherThan(ServerVersion.v_1_7_10)) {
enumPlayerActionClass = NMSUtils.getNMSClass("PacketPlayInEntityAction$EnumPlayerAction");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//ints in 1.7.10, EnumPlayerAction in 1.8+
fields[0] = entityActionClass.getDeclaredField("a");
fields[1] = entityActionClass.getDeclaredField("animation");
fields[2] = entityActionClass.getDeclaredField("c");
if (version.isHigherThan(ServerVersion.v_1_7_10)) {
enumPlayerActionConstants = enumPlayerActionClass.getEnumConstants();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
}
@@ -8,6 +8,38 @@ import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Field;
public class WrappedPacketInFlying extends WrappedPacket {
private static Class<?> flyingClass;
private static final Field[] fields = new Field[8]; //x, y, z, yaw pitch, onGround, isPositionPacket, isLookPacket
static {
try {
flyingClass = NMSUtils.getNMSClass("PacketPlayInFlying");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
fields[0] = flyingClass.getDeclaredField("x");
fields[1] = flyingClass.getDeclaredField("y");
fields[2] = flyingClass.getDeclaredField("z");
fields[3] = flyingClass.getDeclaredField("yaw");
fields[4] = flyingClass.getDeclaredField("pitch");
fields[5] = flyingClass.getDeclaredField(getOnGroundFieldName());
fields[6] = flyingClass.getDeclaredField("hasPos");
fields[7] = flyingClass.getDeclaredField("hasLook");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (Field f : fields) {
if (f != null) {
f.setAccessible(true);
}
}
}
private double x;
private double y;
private double z;
@@ -21,6 +53,13 @@ public class WrappedPacketInFlying extends WrappedPacket {
super(packet);
}
private static String getOnGroundFieldName() {
if (version == ServerVersion.v_1_7_10) {
return "g";
}
return "f";
}
@Override
protected void setup() {
try {
@@ -40,38 +79,6 @@ public class WrappedPacketInFlying extends WrappedPacket {
}
}
public static class WrappedPacketInPosition extends WrappedPacketInFlying {
public WrappedPacketInPosition(Object packet) {
super(packet);
}
@Override
public boolean isPosition() {
return true;
}
@Override
public boolean isLook() {
return false;
}
}
public static class WrappedPacketInPosition_Look extends WrappedPacketInFlying {
public WrappedPacketInPosition_Look(Object packet) {
super(packet);
}
@Override
public boolean isLook() {
return true;
}
@Override
public boolean isPosition() {
return false;
}
}
public final double getX() {
return x;
}
@@ -104,44 +111,36 @@ public class WrappedPacketInFlying extends WrappedPacket {
return isLook;
}
private static Class<?> flyingClass;
private static Field[] fields = new Field[8]; //x, y, z, yaw pitch, onGround, isPositionPacket, isLookPacket
static {
try {
flyingClass = NMSUtils.getNMSClass("PacketPlayInFlying");
} catch (ClassNotFoundException e) {
e.printStackTrace();
public static class WrappedPacketInPosition extends WrappedPacketInFlying {
public WrappedPacketInPosition(Object packet) {
super(packet);
}
try {
fields[0] = flyingClass.getDeclaredField("x");
fields[1] = flyingClass.getDeclaredField("y");
fields[2] = flyingClass.getDeclaredField("z");
fields[3] = flyingClass.getDeclaredField("yaw");
fields[4] = flyingClass.getDeclaredField("pitch");
fields[5] = flyingClass.getDeclaredField(getOnGroundFieldName());
fields[6] = flyingClass.getDeclaredField("hasPos");
fields[7] = flyingClass.getDeclaredField("hasLook");
} catch (NoSuchFieldException e) {
e.printStackTrace();
@Override
public boolean isPosition() {
return true;
}
for (Field f : fields) {
if (f != null) {
f.setAccessible(true);
}
@Override
public boolean isLook() {
return false;
}
}
private static String getOnGroundFieldName() {
if (version == ServerVersion.v_1_7_10) {
return "g";
public static class WrappedPacketInPosition_Look extends WrappedPacketInFlying {
public WrappedPacketInPosition_Look(Object packet) {
super(packet);
}
@Override
public boolean isLook() {
return true;
}
@Override
public boolean isPosition() {
return false;
}
return "f";
}
}
@@ -7,12 +7,31 @@ import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Field;
public final class WrappedPacketInKeepAlive extends WrappedPacket {
public WrappedPacketInKeepAlive(final Object packet) {
super(packet);
private static Class<?> keepAliveClass;
//1.7.10->1.12.2 has int field "a", rest as long field "a"
private static Field idField;
static {
try {
keepAliveClass = NMSUtils.getNMSClass("PacketPlayInKeepAlive");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
idField = keepAliveClass.getDeclaredField("a");
idField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
private long id;
public WrappedPacketInKeepAlive(final Object packet) {
super(packet);
}
@Override
protected void setup() {
try {
@@ -35,24 +54,4 @@ public final class WrappedPacketInKeepAlive extends WrappedPacket {
public long getId() {
return id;
}
private static Class<?> keepAliveClass;
//1.7.10->1.12.2 has int field "a", rest as long field "a"
private static Field idField;
static {
try {
keepAliveClass = NMSUtils.getNMSClass("PacketPlayInKeepAlive");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
idField = keepAliveClass.getDeclaredField("a");
idField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,51 @@
package io.github.retrooper.packetevents.packetwrappers.in.transaction;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
public final class WrappedPacketInTransaction extends WrappedPacket {
private int windowId;
private short actionNumber;
private boolean accepted;
public WrappedPacketInTransaction(final Object packet) {
super(packet);
}
@Override
protected void setup() {
this.windowId = windowIdAccessor.get(packet);
this.actionNumber = actionNumberAccessor.get(packet);
this.accepted = acceptedAccessor.get(packet);
}
public int getWindowId() {
return windowId;
}
public short getActionNumber() {
return actionNumber;
}
public boolean isAccepted() {
return accepted;
}
private static Class<?> packetClass;
private static final Reflection.FieldAccessor<Integer> windowIdAccessor;
private static final Reflection.FieldAccessor<Short> actionNumberAccessor;
private static final Reflection.FieldAccessor<Boolean> acceptedAccessor;
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayInTransaction");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
windowIdAccessor = Reflection.getField(packetClass, int.class, 0);
actionNumberAccessor = Reflection.getField(packetClass, short.class, 0);
acceptedAccessor= Reflection.getField(packetClass, boolean.class, 0);
}
}
@@ -8,46 +8,9 @@ import org.bukkit.entity.Entity;
import java.lang.reflect.Field;
public final class WrappedPacketInUseEntity extends WrappedPacket {
private int entityId;
private Entity entity;
private EntityUseAction action;
public WrappedPacketInUseEntity(final Object packet) {
super(packet);
}
@Override
protected void setup() {
try {
this.entityId = fields[0].getInt(packet);
this.entity = NMSUtils.getEntityById(this.entityId);
final Object useActionEnum = fields[1].get(packet);
this.action = EntityUseAction.valueOf(useActionEnum.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public Entity getEntity() {
return entity;
}
public int getEntityId() {
return entityId;
}
public EntityUseAction getAction() {
return action;
}
private static Class<?> useEntityClass;
private static Class<?> entityClass;
private static Field[] fields = new Field[2];
private static final Field[] fields = new Field[2];
static {
@@ -75,4 +38,36 @@ public final class WrappedPacketInUseEntity extends WrappedPacket {
}
}
private int entityId;
private Entity entity;
private EntityUseAction action;
public WrappedPacketInUseEntity(final Object packet) {
super(packet);
}
@Override
protected void setup() {
try {
this.entityId = fields[0].getInt(packet);
this.entity = NMSUtils.getEntityById(this.entityId);
final Object useActionEnum = fields[1].get(packet);
this.action = EntityUseAction.valueOf(useActionEnum.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public Entity getEntity() {
return entity;
}
public int getEntityId() {
return entityId;
}
public EntityUseAction getAction() {
return action;
}
}
@@ -1,55 +0,0 @@
package io.github.retrooper.packetevents.packetwrappers.login;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.tinyprotocol.Reflection;
import io.github.retrooper.packetevents.tinyprotocol.Reflection.FieldAccessor;
import io.github.retrooper.packetevents.utils.NMSUtils;
public final class WrappedPacketLoginHandshake extends WrappedPacket {
private int protocolVersion, port;
private String hostname;
public WrappedPacketLoginHandshake(final Object packet) {
super(packet);
}
@Override
protected void setup() {
final Object protocolVersionObj = fields[0].get(packet);
final Object hostNameObj = fields[1].get(packet);
final Object portObj = fields[2].get(packet);
this.protocolVersion = (int)protocolVersionObj;
this.hostname = hostNameObj.toString();
this.port = (int)portObj;
}
public int getProtocolVersion() {
return protocolVersion;
}
public String getHostname() {
return hostname;
}
public int getPort() {
return port;
}
private static Class<?> loginHandshakeClass;
private static FieldAccessor<?>[] fields = new FieldAccessor[3];
static {
try {
loginHandshakeClass = NMSUtils.getNMSClass("PacketHandshakingInSetProtocol");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
fields[0] = Reflection.getField(loginHandshakeClass, int.class, 0);
fields[1] = Reflection.getField(loginHandshakeClass, String.class, 0);
fields[2] = Reflection.getField(loginHandshakeClass, int.class, 1);
}
}
@@ -0,0 +1,52 @@
package io.github.retrooper.packetevents.packetwrappers.out.abilities;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public final class PlayerAbilitiesUtils {
public static Class<?> playerAbilitiesClass;
public static Constructor<?> playerAbilitiesConstructor;
private static final Reflection.FieldAccessor<Boolean>[] booleanFieldAccessors = new Reflection.FieldAccessor[4];
private static final Reflection.FieldAccessor<Float>[] floatFieldAccessors = new Reflection.FieldAccessor[2];
public static Object getPlayerAbilities(final boolean isVulnerable, final boolean isFlying, final boolean allowFlight, final boolean canBuildInstantly,
final float flySpeed, final float walkSpeed) throws IllegalAccessException, InvocationTargetException, InstantiationException {
final Object instance = playerAbilitiesConstructor.newInstance();
booleanFieldAccessors[0].set(instance, isVulnerable);
booleanFieldAccessors[1].set(instance, isFlying);
booleanFieldAccessors[2].set(instance, allowFlight);
booleanFieldAccessors[3].set(instance, canBuildInstantly);
floatFieldAccessors[0].set(instance, flySpeed);
floatFieldAccessors[1].set(instance, walkSpeed);
return instance;
}
static {
try {
playerAbilitiesClass = NMSUtils.getNMSClass("PlayerAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
playerAbilitiesConstructor = playerAbilitiesClass.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
for (byte i = 0; i < booleanFieldAccessors.length; i++) {
booleanFieldAccessors[i] = Reflection.getField(playerAbilitiesClass, boolean.class, i);
}
for (byte i = 0; i < floatFieldAccessors.length; i++) {
floatFieldAccessors[i] = Reflection.getField(playerAbilitiesClass, float.class, i);
}
}
}
@@ -0,0 +1,102 @@
package io.github.retrooper.packetevents.packetwrappers.out.abilities;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutAbilities extends WrappedPacket implements Sendable {
private boolean vulnerable, flying, allowFlight, instantBuild;
private float flySpeed, walkSpeed;
public WrappedPacketOutAbilities(final Object packet) {
super(packet);
}
public WrappedPacketOutAbilities(final boolean isVulnerable, final boolean isFlying, final boolean allowFlight, final boolean canBuildInstantly,
final float flySpeed, final float walkSpeed) {
super(null);
this.vulnerable = isVulnerable;
this.flying = isFlying;
this.allowFlight = allowFlight;
this.instantBuild = canBuildInstantly;
this.flySpeed = flySpeed;
this.walkSpeed = walkSpeed;
}
@Override
protected void setup() {
this.vulnerable = booleanFieldAccessors[0].get(packet);
this.flying = booleanFieldAccessors[1].get(packet);
this.allowFlight = booleanFieldAccessors[2].get(packet);
this.instantBuild = booleanFieldAccessors[3].get(packet);
this.flySpeed = floatFieldAccessors[0].get(packet);
this.walkSpeed = floatFieldAccessors[1].get(packet);
}
public boolean isVulnerable() {
return vulnerable;
}
public boolean isFlying() {
return flying;
}
public boolean isFlightAllowed() {
return allowFlight;
}
public boolean canInstantlyBuild() {
return instantBuild;
}
public float getFlySpeed() {
return flySpeed;
}
public float getWalkSpeed() {
return walkSpeed;
}
private static Class<?> packetClass;
private static Constructor<?> packetConstructor;
private static final Reflection.FieldAccessor<Boolean>[] booleanFieldAccessors = new Reflection.FieldAccessor[4];
private static final Reflection.FieldAccessor<Float>[] floatFieldAccessors = new Reflection.FieldAccessor[2];
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayOutAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
packetConstructor = packetClass.getConstructor(PlayerAbilitiesUtils.playerAbilitiesClass);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
for (byte i = 0; i < booleanFieldAccessors.length; i++) {
booleanFieldAccessors[i] = Reflection.getField(packetClass, boolean.class, i);
}
for (byte i = 0; i < floatFieldAccessors.length; i++) {
floatFieldAccessors[i] = Reflection.getField(packetClass, float.class, i);
}
}
@Override
public Object asNMSPacket() {
try {
return packetConstructor.newInstance(PlayerAbilitiesUtils.getPlayerAbilities(vulnerable, flying, allowFlight, instantBuild, flySpeed, walkSpeed));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
@@ -11,6 +11,36 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutAnimation extends WrappedPacket implements Sendable {
private static Class<?> animationClass, nmsEntityClass;
private static Constructor<?> animationConstructor;
private static final Field[] fields = new Field[2];
static {
try {
animationClass = NMSUtils.getNMSClass("PacketPlayOutAnimation");
nmsEntityClass = NMSUtils.getNMSClass("Entity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
animationConstructor = animationClass.getConstructor(nmsEntityClass, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
fields[0] = animationClass.getDeclaredField("a");
fields[1] = animationClass.getDeclaredField("b");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
private Entity entity;
private int entityID;
private EntityAnimationType type;
@@ -52,7 +82,6 @@ public final class WrappedPacketOutAnimation extends WrappedPacket implements Se
return type;
}
@Override
public Object asNMSPacket() {
final Object nmsEntity = NMSUtils.getNMSEntity(this.entity);
@@ -64,35 +93,4 @@ public final class WrappedPacketOutAnimation extends WrappedPacket implements Se
}
return null;
}
private static Class<?> animationClass, nmsEntityClass;
private static Constructor<?> animationConstructor;
private static Field[] fields = new Field[2];
static {
try {
animationClass = NMSUtils.getNMSClass("PacketPlayOutAnimation");
nmsEntityClass = NMSUtils.getNMSClass("Entity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
animationConstructor = animationClass.getConstructor(nmsEntityClass, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
fields[0] = animationClass.getDeclaredField("a");
fields[1] = animationClass.getDeclaredField("b");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
}
@@ -11,71 +11,9 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class WrappedPacketOutChat extends WrappedPacket implements Sendable {
private String message;
public WrappedPacketOutChat(final Object packet) {
super(packet);
}
public WrappedPacketOutChat(final String message) {
super(null);
this.message = "{\"text\": \"" + message + "\"}";
}
@Override
protected void setup() {
//all fields are IChatBaseComponents, and named 'a'
try {
Object iChatBaseObj = iChatBaseField.get(packet);
Object contentString = getStringOfIChatBase.invoke(iChatBaseObj);
this.message = contentString.toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public Object asNMSPacket() {
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(this.message));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static String toStringFromIChatBaseComponent(Object obj) {
try {
return getStringOfIChatBase.invoke(obj).toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static Object toIChatBaseComponent(String msg) {
try {
return serialize.invoke(null, msg);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public String getMessage() {
return this.message;
}
private static Constructor<?> chatClassConstructor;
private static Class<?> chatClass, iChatBaseComponentClass, chatSerializerClass;
private static Field iChatBaseField;
private static Method serialize, getStringOfIChatBase;
static {
@@ -84,15 +22,14 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
iChatBaseComponentClass = NMSUtils.getNMSClass("IChatBaseComponent");
//In 1.8.3+ the ChatSerializer class is declared in the IChatBaseComponent class, so we have to handle tahat
if(version.isHigherThan(ServerVersion.v_1_8)) {
for(final Class<?> cls : iChatBaseComponentClass.getDeclaredClasses()) {
if(cls.getSimpleName().equals("ChatSerializer")) {
if (version.isHigherThan(ServerVersion.v_1_8)) {
for (final Class<?> cls : iChatBaseComponentClass.getDeclaredClasses()) {
if (cls.getSimpleName().equals("ChatSerializer")) {
chatSerializerClass = cls;
break;
}
}
}
else {
} else {
chatSerializerClass = NMSUtils.getNMSClass("ChatSerializer");
}
} catch (ClassNotFoundException e) {
@@ -121,6 +58,35 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
}
private String message;
public WrappedPacketOutChat(final Object packet) {
super(packet);
}
public WrappedPacketOutChat(final String message) {
super(null);
this.message = "{\"text\": \"" + message + "\"}";
}
public static String toStringFromIChatBaseComponent(Object obj) {
try {
return getStringOfIChatBase.invoke(obj).toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static Object toIChatBaseComponent(String msg) {
try {
return serialize.invoke(null, msg);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static String getTextMethodName() {
//1.7.10
if (version.isLowerThan(ServerVersion.v_1_8)) {
@@ -129,4 +95,34 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
return "getText";
}
}
@Override
protected void setup() {
//all fields are IChatBaseComponents, and named 'a'
try {
Object iChatBaseObj = iChatBaseField.get(packet);
Object contentString = getStringOfIChatBase.invoke(iChatBaseObj);
this.message = contentString.toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public Object asNMSPacket() {
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(this.message));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public String getMessage() {
return this.message;
}
}
@@ -11,6 +11,58 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutEntityVelocity extends WrappedPacket implements Sendable {
private static Constructor<?> velocityConstructor, vec3dConstructor;
private static Class<?> velocityClass, vec3dClass;
private static final Field[] fields = new Field[4];
/**
* Velocity Constructor parameter types:
* 1.7.10->1.13.2 use int, double, double, double style,
* 1.14+ use int, Vec3D style
*/
static {
try {
velocityClass = NMSUtils.getNMSClass("PacketPlayOutEntityVelocity");
if (version.isHigherThan(ServerVersion.v_1_13_2)) {
vec3dClass = NMSUtils.getNMSClass("Vec3D");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
if (version.isHigherThan(ServerVersion.v_1_13_2)) {
velocityConstructor = velocityClass.getConstructor(int.class, vec3dClass);
//vec3d constructor
vec3dConstructor = vec3dClass.getConstructor(double.class, double.class, double.class);
} else {
velocityConstructor = velocityClass.getConstructor(int.class, double.class, double.class, double.class);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
//entity id
fields[0] = velocityClass.getDeclaredField("a");
//x
fields[1] = velocityClass.getDeclaredField("b");
//y
fields[2] = velocityClass.getDeclaredField("c");
//z
fields[3] = velocityClass.getDeclaredField("d");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
private Entity entity;
private int entityId;
private double velocityX, velocityY, velocityZ;
@@ -87,59 +139,4 @@ public final class WrappedPacketOutEntityVelocity extends WrappedPacket implemen
return null;
}
private static Constructor<?> velocityConstructor, vec3dConstructor;
private static Class<?> velocityClass, vec3dClass;
private static Field[] fields = new Field[4];
/**
* Velocity Constructor parameter types:
* 1.7.10->1.13.2 use int, double, double, double style,
* 1.14+ use int, Vec3D style
*/
static {
try {
velocityClass = NMSUtils.getNMSClass("PacketPlayOutEntityVelocity");
if (version.isHigherThan(ServerVersion.v_1_13_2)) {
vec3dClass = NMSUtils.getNMSClass("Vec3D");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
if (version.isHigherThan(ServerVersion.v_1_13_2)) {
velocityConstructor = velocityClass.getConstructor(int.class, vec3dClass);
//vec3d constructor
vec3dConstructor = vec3dClass.getConstructor(double.class, double.class, double.class);
} else {
velocityConstructor = velocityClass.getConstructor(int.class, double.class, double.class, double.class);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
//entity id
fields[0] = velocityClass.getDeclaredField("a");
//x
fields[1] = velocityClass.getDeclaredField("b");
//y
fields[2] = velocityClass.getDeclaredField("c");
//z
fields[3] = velocityClass.getDeclaredField("d");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
for (final Field f : fields) {
f.setAccessible(true);
}
}
}
@@ -3,13 +3,33 @@ package io.github.retrooper.packetevents.packetwrappers.out.kickdisconnect;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.packetwrappers.out.chat.WrappedPacketOutChat;
import io.github.retrooper.packetevents.tinyprotocol.Reflection;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutKickDisconnect extends WrappedPacket implements Sendable {
private static final Reflection.FieldAccessor<?> iChatBaseComponentAccessor;
private static Class<?> packetClass, iChatBaseComponentClass;
private static Constructor<?> kickDisconnectConstructor;
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayOutKickDisconnect");
iChatBaseComponentClass = NMSUtils.getNMSClass("IChatBaseComponent");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
kickDisconnectConstructor = packetClass.getConstructor(iChatBaseComponentClass);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
iChatBaseComponentAccessor = Reflection.getField(packetClass, iChatBaseComponentClass, 0);
}
private String kickMessage;
public WrappedPacketOutKickDisconnect(final Object packet) {
@@ -39,26 +59,4 @@ public final class WrappedPacketOutKickDisconnect extends WrappedPacket implemen
}
return null;
}
private static Class<?> packetClass, iChatBaseComponentClass;
private static Constructor<?> kickDisconnectConstructor;
private static final Reflection.FieldAccessor<?> iChatBaseComponentAccessor;
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayOutKickDisconnect");
iChatBaseComponentClass = NMSUtils.getNMSClass("IChatBaseComponent");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
kickDisconnectConstructor = packetClass.getConstructor(iChatBaseComponentClass);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
iChatBaseComponentAccessor = Reflection.getField(packetClass, iChatBaseComponentClass, 0);
}
}
@@ -0,0 +1,80 @@
package io.github.retrooper.packetevents.packetwrappers.out.transaction;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class WrappedPacketOutTransaction extends WrappedPacket implements Sendable {
private int windowId;
private short actionNumber;
private boolean accepted;
public WrappedPacketOutTransaction(final Object packet) {
super(packet);
}
public WrappedPacketOutTransaction(final int windowId, final short actionNumber, final boolean accepted) {
super(null);
this.windowId = windowId;
this.actionNumber = actionNumber;
this.accepted = accepted;
}
@Override
protected void setup() {
this.windowId = windowIdAccessor.get(packet);
this.actionNumber = actionNumberAccessor.get(packet);
this.accepted = acceptedAccessor.get(packet);
}
public int getWindowId() {
return windowId;
}
public short getActionNumber() {
return actionNumber;
}
public boolean isAccepted() {
return accepted;
}
@Override
public Object asNMSPacket() {
try {
return packetConstructor.newInstance(windowId, actionNumber, accepted);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static Class<?> packetClass;
private static Constructor<?> packetConstructor;
private static final Reflection.FieldAccessor<Integer> windowIdAccessor;
private static final Reflection.FieldAccessor<Short> actionNumberAccessor;
private static final Reflection.FieldAccessor<Boolean> acceptedAccessor;
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayOutTransaction");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
packetConstructor = packetClass.getConstructor(int.class, short.class, boolean.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
windowIdAccessor = Reflection.getField(packetClass, int.class, 0);
actionNumberAccessor = Reflection.getField(packetClass, short.class, 0);
acceptedAccessor= Reflection.getField(packetClass, boolean.class, 0);
}
}
@@ -0,0 +1,85 @@
package io.github.retrooper.packetevents.packetwrappers.out.updatehealth;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.packetwrappers.api.WrappedPacket;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutUpdateHealth extends WrappedPacket implements Sendable {
private float health, foodSaturation;
private int food;
public WrappedPacketOutUpdateHealth(final Object packet) {
super(packet);
}
/**
* See https://wiki.vg/Protocol#Update_Health
* @param health
* @param food
* @param foodSaturation
*/
public WrappedPacketOutUpdateHealth(final float health, final int food, final float foodSaturation) {
super(null);
this.health= health;
this.food = food;
this.foodSaturation = foodSaturation;
}
@Override
protected void setup() {
this.health = healthAccessor.get(packet);
this.foodSaturation = foodSaturationAccessor.get(packet);
this.food = foodAccessor.get(packet);
}
public float getHealth() {
return health;
}
public float getFoodSaturation() {
return foodSaturation;
}
public int getFood() {
return food;
}
@Override
public Object asNMSPacket() {
try {
return packetConstructor.newInstance(health, food, foodSaturation);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static Class<?> packetClass;
private static Constructor<?> packetConstructor;
private static final Reflection.FieldAccessor<Float> healthAccessor, foodSaturationAccessor;
private static final Reflection.FieldAccessor<Integer> foodAccessor;
static {
try {
packetClass = NMSUtils.getNMSClass("PacketPlayOutUpdateHealth");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
packetConstructor = packetClass.getConstructor(float.class, int.class, float.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
healthAccessor = Reflection.getField(packetClass, float.class, 0);
foodSaturationAccessor = Reflection.getField(packetClass, float.class, 1);
foodAccessor = Reflection.getField(packetClass, int.class, 0);
}
}
@@ -1,4 +1,4 @@
package io.github.retrooper.packetevents.tinyprotocol;
package io.github.retrooper.packetevents.reflectionutils;
import org.bukkit.Bukkit;
@@ -15,72 +15,12 @@ import java.util.regex.Pattern;
* @author Kristian
*/
public final class Reflection {
/**
* An interface for invoking a specific constructor.
*/
public interface ConstructorInvoker {
/**
* Invoke a constructor for a specific class.
*
* @param arguments - the arguments to pass to the constructor.
* @return The constructed object.
*/
Object invoke(Object... arguments);
}
/**
* An interface for invoking a specific method.
*/
public interface MethodInvoker {
/**
* Invoke a method on a specific target object.
*
* @param target - the target object, or NULL for a static method.
* @param arguments - the arguments to pass to the method.
* @return The return value, or NULL if is void.
*/
Object invoke(Object target, Object... arguments);
}
/**
* An interface for retrieving the field content.
*
* @param <T> - field type.
*/
public interface FieldAccessor<T> {
/**
* Retrieve the content of a field.
*
* @param target - the target object, or NULL for a static field.
* @return The value of the field.
*/
T get(Object target);
/**
* Set the content of a field.
*
* @param target - the target object, or NULL for a static field.
* @param value - the new value of the field.
*/
void set(Object target, Object value);
/**
* Determine if the given object has this field.
*
* @param target - the object to test.
* @return TRUE if it does, FALSE otherwise.
*/
boolean hasField(Object target);
}
// Deduce the net.minecraft.server.v* package
private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
private static final String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
private static final String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
private static final String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
// Variable replacement
private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
private static final Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
private Reflection() {
// Seal class
}
@@ -88,8 +28,8 @@ public final class Reflection {
/**
* Retrieve a field accessor for a specific field type and name.
*
* @param target - the target type.
* @param name - the name of the field, or NULL to ignore.
* @param target - the target type.
* @param name - the name of the field, or NULL to ignore.
* @param fieldType - a compatible field type.
* @return The field accessor.
*/
@@ -101,7 +41,7 @@ public final class Reflection {
* Retrieve a field accessor for a specific field type and name.
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param name - the name of the field, or NULL to ignore.
* @param name - the name of the field, or NULL to ignore.
* @param fieldType - a compatible field type.
* @return The field accessor.
*/
@@ -112,9 +52,9 @@ public final class Reflection {
/**
* Retrieve a field accessor for a specific field type and name.
*
* @param target - the target type.
* @param target - the target type.
* @param fieldType - a compatible field type.
* @param index - the number of compatible fields to skip.
* @param index - the number of compatible fields to skip.
* @return The field accessor.
*/
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
@@ -126,7 +66,7 @@ public final class Reflection {
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param fieldType - a compatible field type.
* @param index - the number of compatible fields to skip.
* @param index - the number of compatible fields to skip.
* @return The field accessor.
*/
public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
@@ -180,9 +120,9 @@ public final class Reflection {
/**
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param methodName - the method name, or NULL to skip.
* @param params - the expected parameters.
* @param params - the expected parameters.
* @return An object that invokes this specific method.
* @throws IllegalStateException If we cannot find this method.
*/
@@ -193,9 +133,9 @@ public final class Reflection {
/**
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
* @param params - the expected parameters.
* @param params - the expected parameters.
* @return An object that invokes this specific method.
* @throws IllegalStateException If we cannot find this method.
*/
@@ -206,10 +146,10 @@ public final class Reflection {
/**
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
* @param returnType - the expected return type, or NULL to ignore.
* @param params - the expected parameters.
* @param params - the expected parameters.
* @return An object that invokes this specific method.
* @throws IllegalStateException If we cannot find this method.
*/
@@ -246,7 +186,7 @@ public final class Reflection {
* Search for the first publically and privately defined constructor of the given name and parameter count.
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param params - the expected parameters.
* @param params - the expected parameters.
* @return An object that invokes this constructor.
* @throws IllegalStateException If we cannot find this method.
*/
@@ -257,7 +197,7 @@ public final class Reflection {
/**
* Search for the first publically and privately defined constructor of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param clazz - a class to start with.
* @param params - the expected parameters.
* @return An object that invokes this constructor.
* @throws IllegalStateException If we cannot find this method.
@@ -291,12 +231,12 @@ public final class Reflection {
* This is useful when looking up fields by a NMS or OBC type.
* <p>
*
* @see {@link #getClass()} for more information.
* @param lookupName - the class name with variables.
* @return The class.
* @see {@link #getClass()} for more information.
*/
public static Class<Object> getUntypedClass(String lookupName) {
@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings({"rawtypes", "unchecked"})
Class<Object> clazz = (Class) getClass(lookupName);
return clazz;
}
@@ -400,4 +340,62 @@ public final class Reflection {
matcher.appendTail(output);
return output.toString();
}
/**
* An interface for invoking a specific constructor.
*/
public interface ConstructorInvoker {
/**
* Invoke a constructor for a specific class.
*
* @param arguments - the arguments to pass to the constructor.
* @return The constructed object.
*/
Object invoke(Object... arguments);
}
/**
* An interface for invoking a specific method.
*/
public interface MethodInvoker {
/**
* Invoke a method on a specific target object.
*
* @param target - the target object, or NULL for a static method.
* @param arguments - the arguments to pass to the method.
* @return The return value, or NULL if is void.
*/
Object invoke(Object target, Object... arguments);
}
/**
* An interface for retrieving the field content.
*
* @param <T> - field type.
*/
public interface FieldAccessor<T> {
/**
* Retrieve the content of a field.
*
* @param target - the target object, or NULL for a static field.
* @return The value of the field.
*/
T get(Object target);
/**
* Set the content of a field.
*
* @param target - the target object, or NULL for a static field.
* @param value - the new value of the field.
*/
void set(Object target, Object value);
/**
* Determine if the given object has this field.
*
* @param target - the object to test.
* @return TRUE if it does, FALSE otherwise.
*/
boolean hasField(Object target);
}
}
@@ -1,544 +0,0 @@
/*
* This file is part of Hawk Anticheat.
* Copyright (C) 2018 Hawk Development Team
*
* 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 <https://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.tinyprotocol;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PacketSendEvent;
import io.github.retrooper.packetevents.event.impl.PlayerInjectEvent;
import io.github.retrooper.packetevents.event.impl.PlayerUninjectEvent;
import io.github.retrooper.packetevents.tinyprotocol.Reflection.FieldAccessor;
import io.github.retrooper.packetevents.tinyprotocol.Reflection.MethodInvoker;
import net.minecraft.util.com.google.common.collect.Lists;
import net.minecraft.util.com.google.common.collect.MapMaker;
import net.minecraft.util.com.mojang.authlib.GameProfile;
import net.minecraft.util.io.netty.channel.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
/**
* Represents a very tiny alternative to ProtocolLib.
* <p>
* It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
*
* @author Kristian
*/
//BROKEN: NPEs when player joins while server is starting, and using PlayerJoinEvent instead will cause NPE for protocol version lookup.
//Protocol version number lookup method from DeprecatedLuke's version of TinyProtocol
public abstract class TinyProtocol7 {
private static final AtomicInteger ID = new AtomicInteger(0);
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
// Speedup channel/protocol lookup
private final Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
private Listener listener;
// Channels that have already been removed
private final Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
// List of network markers
private List<Object> networkManagers;
// Injected channel handlers
private List<Channel> serverChannels = Lists.newArrayList();
private ChannelInboundHandlerAdapter serverChannelHandler;
private ChannelInitializer<Channel> beginInitProtocol;
private ChannelInitializer<Channel> endInitProtocol;
// Current handler name
private String handlerName;
protected volatile boolean closed;
protected Plugin plugin;
/**
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
* <p>
* You can construct multiple instances per plugin.
*
* @param plugin - the plugin.
*/
public TinyProtocol7(final Plugin plugin) {
this.plugin = plugin;
// Compute handler name
this.handlerName = getHandlerName();
// Prepare existing players
registerBukkitEvents();
try {
registerChannelHandler();
registerPlayers(plugin);
} catch (IllegalArgumentException ex) {
// Damn you, late bind
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
new BukkitRunnable() {
@Override
public void run() {
registerChannelHandler();
registerPlayers(plugin);
plugin.getLogger().info("[TinyProtocol] Late bind injection successful.");
}
}.runTask(plugin);
}
}
private void createServerChannelHandler() {
// Handle connected channels
endInitProtocol = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
try {
// This can take a while, so we need to stop the main thread from interfering
synchronized (networkManagers) {
// Stop injecting channels
if (!closed) {
channel.eventLoop().submit(() -> injectChannelInternal(channel));
}
}
} catch (Exception e) {
plugin.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;
// Prepare to initialize ths channel
channel.pipeline().addFirst(beginInitProtocol);
ctx.fireChannelRead(msg);
}
};
}
/**
* Register bukkit events.
*/
private void registerBukkitEvents() {
listener = new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
public final void onPlayerLogin(PlayerLoginEvent e) {
if (closed)
return;
Channel channel = getChannel(e.getPlayer());
// Don't inject players that have been explicitly uninjected
if (!uninjectedChannels.contains(channel)) {
injectPlayer(e.getPlayer());
}
}
@EventHandler
public final void onPluginDisable(PluginDisableEvent e) {
if (e.getPlugin().equals(plugin)) {
close();
}
}
};
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
}
@SuppressWarnings("unchecked")
private void registerChannelHandler() {
Object mcServer = getMinecraftServer.get(Bukkit.getServer());
Object serverConnection = getServerConnection.get(mcServer);
boolean looking = true;
// We need to synchronize against this list
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
createServerChannelHandler();
// Find the correct list, or implicitly throw an exception
for (int i = 0; looking; i++) {
List<Object> list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection);
for (Object item : list) {
if (!(item instanceof ChannelFuture))
break;
// Channel future that contains the server connection
Channel serverChannel = ((ChannelFuture) item).channel();
serverChannels.add(serverChannel);
serverChannel.pipeline().addFirst(serverChannelHandler);
looking = false;
}
}
}
private void unregisterChannelHandler() {
if (serverChannelHandler == null)
return;
for (Channel serverChannel : serverChannels) {
final ChannelPipeline pipeline = serverChannel.pipeline();
// Remove channel handler
serverChannel.eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
pipeline.remove(serverChannelHandler);
} catch (NoSuchElementException e) {
// That's fine
}
}
});
}
}
private void registerPlayers(Plugin plugin) {
for (Player player : plugin.getServer().getOnlinePlayers()) {
injectPlayer(player);
}
}
/**
* Invoked when the server is starting to send a packet to a player.
* <p>
* Note that this is not executed on the main thread.
*
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
final String packetName = packet.getClass().getSimpleName();
final PacketSendEvent packetSendEvent = new PacketSendEvent(receiver, packetName, packet);
PacketEvents.getEventManager().callEvent(packetSendEvent);
if (!packetSendEvent.isCancelled()) {
return packet;
}
return null;
}
/**
* Invoked when the server has received a packet from a given player.
* <p>
* Use {@link Channel#remoteAddress()} to get the remote address of the client.
*
* @param sender - the player that sent the packet, NULL for early login/status packets.
* @param channel - channel that received the packet. Never NULL.
* @param packet - the packet being received.
* @return The packet to recieve instead, or NULL to cancel.
*/
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
return packet;
}
/**
* Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the destination player.
* @param packet - the packet to send.
*/
public void sendPacket(Player player, Object packet) {
sendPacket(getChannel(player), packet);
}
/**
* Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet to send.
*/
public void sendPacket(Channel channel, Object packet) {
channel.pipeline().writeAndFlush(packet);
}
/**
* Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Player player, Object packet) {
receivePacket(getChannel(player), packet);
}
/**
* Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Channel channel, Object packet) {
channel.pipeline().context("encoder").fireChannelRead(packet);
}
/**
* Changed from "tiny-" to "packetevents"
* @return handlerName
*/
protected String getHandlerName() {
return "packetevents-" + plugin.getName() + "-" + ID.incrementAndGet();
}
/**
* Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets.
* <p>
* This will automatically be called when a player has logged in.
*
* @param player - the player to inject.
*/
public void injectPlayer(Player player) {
final PlayerInjectEvent playerInjectEvent = new PlayerInjectEvent(player);
PacketEvents.getEventManager().callEvent(playerInjectEvent);
if (!playerInjectEvent.isCancelled()) {
injectChannelInternal(getChannel(player)).player = player;
}
}
/**
* Add a custom channel handler to the given channel.
*
* @param channel - the channel to inject.
* @return The intercepted channel, or NULL if it has already been injected.
*/
public void injectChannel(Channel channel) {
injectChannelInternal(channel);
}
/**
* Add a custom channel handler to the given channel.
*
* @param channel - the channel to inject.
* @return The packet interceptor.
*/
private PacketInterceptor injectChannelInternal(Channel channel) {
try {
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName);
// Inject our packet interceptor
if (interceptor == null) {
interceptor = new PacketInterceptor();
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
uninjectedChannels.remove(channel);
}
return interceptor;
} catch (IllegalArgumentException e) {
// Try again
return (PacketInterceptor) channel.pipeline().get(handlerName);
}
}
/**
* Retrieve the Netty channel associated with a player. This is cached.
*
* @param player - the player.
* @return The Netty channel.
*/
public Channel getChannel(Player player) {
Channel channel = channelLookup.get(player.getName());
// Lookup channel again
if (channel == null) {
Object connection = getConnection.get(getPlayerHandle.invoke(player));
Object manager = getManager.get(connection);
channelLookup.put(player.getName(), channel = getChannel.get(manager));
}
return channel;
}
/**
* Uninject a specific player.
*
* @param player - the injected player.
*/
public void uninjectPlayer(Player player) {
uninjectChannel(getChannel(player));
PacketEvents.getEventManager().callEvent(new PlayerUninjectEvent(player));
}
/**
* Uninject a specific channel.
* <p>
* This will also disable the automatic channel injection that occurs when a player has properly logged in.
*
* @param channel - the injected channel.
*/
public void uninjectChannel(final Channel channel) {
// No need to guard against this if we're closing
if (!closed) {
uninjectedChannels.add(channel);
}
// See ChannelInjector in ProtocolLib, line 590
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
channel.pipeline().remove(handlerName);
}
});
}
/**
* Determine if the given player has been injected by TinyProtocol.
*
* @param player - the player.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean hasInjected(Player player) {
return hasInjected(getChannel(player));
}
/**
* Determine if the given channel has been injected by TinyProtocol.
*
* @param channel - the channel.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean hasInjected(Channel channel) {
return channel.pipeline().get(handlerName) != null;
}
/**
* Cease listening for packets. This is called automatically when your plugin is disabled.
*/
public final void close() {
if (!closed) {
closed = true;
// Remove our handlers
for (Player player : plugin.getServer().getOnlinePlayers()) {
uninjectPlayer(player);
}
// Clean up Bukkit
HandlerList.unregisterAll(listener);
unregisterChannelHandler();
}
}
/**
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
*
* @author Kristian
*/
private final class PacketInterceptor extends ChannelDuplexHandler {
// Updated by the login event
public volatile Player player;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// Intercept channel
final Channel channel = ctx.channel();
handleLoginStart(channel, msg);
try {
msg = onPacketInAsync(player, channel, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
}
if (msg != null) {
super.channelRead(ctx, msg);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
try {
msg = onPacketOutAsync(player, ctx.channel(), msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
}
if (msg != null) {
super.write(ctx, msg, promise);
}
}
private void handleLoginStart(Channel channel, Object packet) {
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
GameProfile profile = getGameProfile.get(packet);
channelLookup.put(profile.getName(), channel);
}
}
}
}
@@ -1,550 +0,0 @@
/*
* This file is part of Hawk Anticheat.
* Copyright (C) 2018 Hawk Development Team
*
* 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 <https://www.gnu.org/licenses/>.
*/
package io.github.retrooper.packetevents.tinyprotocol;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.mojang.authlib.GameProfile;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PlayerInjectEvent;
import io.github.retrooper.packetevents.event.impl.PlayerUninjectEvent;
import io.github.retrooper.packetevents.handler.TinyProtocolHandler_1_8;
import io.github.retrooper.packetevents.tinyprotocol.Reflection.FieldAccessor;
import io.netty.channel.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
/**
* Represents a very tiny alternative to ProtocolLib.
* <p>
* It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
*
* @author Kristian
*/
//BROKEN: NPEs when player joins while server is starting, and using PlayerJoinEvent instead will cause NPE for protocol version lookup.
//Protocol version number lookup method from DeprecatedLuke's version of TinyProtocol
public abstract class TinyProtocol8 {
private static final AtomicInteger ID = new AtomicInteger(0);
// Used in order to lookup a channel
private static final Reflection.MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
// Speedup channel/protocol lookup
private final Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
private Listener listener;
// Channels that have already been removed
private final Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
// List of network markers
private List<Object> networkManagers;
// Injected channel handlers
private final List<Channel> serverChannels = Lists.newArrayList();
private ChannelInboundHandlerAdapter serverChannelHandler;
private ChannelInitializer<Channel> beginInitProtocol;
private ChannelInitializer<Channel> endInitProtocol;
// Current handler name
private String handlerName;
protected volatile boolean closed;
protected Plugin plugin;
/**
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
* <p>
* You can construct multiple instances per plugin.
*
* @param plugin - the plugin.
*/
public TinyProtocol8(final Plugin plugin) {
this.plugin = plugin;
// Compute handler name
this.handlerName = getHandlerName();
// Prepare existing players
registerBukkitEvents();
try {
registerChannelHandler();
registerPlayers(plugin);
} catch (IllegalArgumentException | NoSuchFieldException | IllegalAccessException ex) {
// Damn you, late bind
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
new BukkitRunnable() {
@Override
public void run() {
try {
registerChannelHandler();
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
registerPlayers(plugin);
plugin.getLogger().info("[TinyProtocol] Late bind injection successful.");
}
}.runTask(plugin);
}
}
private void createServerChannelHandler() {
// Handle connected channels
endInitProtocol = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
try {
// This can take a while, so we need to stop the main thread from interfering
synchronized (networkManagers) {
// Stop injecting channels
if (!closed) {
channel.eventLoop().submit(() -> injectChannelInternal(channel));
}
}
} catch (Exception e) {
plugin.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;
// Prepare to initialize ths channel
channel.pipeline().addFirst(beginInitProtocol);
ctx.fireChannelRead(msg);
}
};
}
/**
* Register bukkit events.
*/
private void registerBukkitEvents() {
listener = new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
public final void onPlayerLogin(PlayerLoginEvent e) {
if (closed)
return;
Channel channel = getChannel(e.getPlayer());
// Don't inject players that have been explicitly uninjected
if (!uninjectedChannels.contains(channel)) {
injectPlayer(e.getPlayer());
}
}
@EventHandler
public final void onPluginDisable(PluginDisableEvent e) {
if (e.getPlugin().equals(plugin)) {
close();
}
}
};
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
}
/**
* Function modified by Retrooper
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@SuppressWarnings("unchecked")
private void registerChannelHandler() throws NoSuchFieldException, IllegalAccessException {
Object mcServer = getMinecraftServer.get(Bukkit.getServer());
Object serverConnection = getServerConnection.get(mcServer);
boolean looking = true;
/*
* I am doing this so we can support 1.8->1.15.2, the previous method TinyProtocol used wasn't supported on some versions
* like 1.15.2
*/
final Field networkManagersField = serverConnection.getClass().getDeclaredField(TinyProtocolHandler_1_8.getNetworkManagersFieldName());
networkManagersField.setAccessible(true);
networkManagers = (List<Object>) networkManagersField.get(serverConnection);
createServerChannelHandler();
// Find the correct list, or implicitly throw an exception
for (int i = 0; looking; i++) {
List<Object> list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection);
for (Object item : list) {
if (!(item instanceof ChannelFuture))
break;
// Channel future that contains the server connection
Channel serverChannel = ((ChannelFuture) item).channel();
serverChannels.add(serverChannel);
serverChannel.pipeline().addFirst(serverChannelHandler);
looking = false;
}
}
}
private void unregisterChannelHandler() {
if (serverChannelHandler == null)
return;
for (Channel serverChannel : serverChannels) {
final ChannelPipeline pipeline = serverChannel.pipeline();
// Remove channel handler
serverChannel.eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
pipeline.remove(serverChannelHandler);
} catch (NoSuchElementException e) {
// That's fine
}
}
});
}
}
private void registerPlayers(Plugin plugin) {
for (Player player : plugin.getServer().getOnlinePlayers()) {
injectPlayer(player);
}
}
/**
* Invoked when the server is starting to send a packet to a player.
* <p>
* Note that this is not executed on the main thread.
*
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
return packet;
}
/**
* Invoked when the server has received a packet from a given player.
* <p>
* Use {@link Channel#remoteAddress()} to get the remote address of the client.
*
* @param sender - the player that sent the packet, NULL for early login/status packets.
* @param channel - channel that received the packet. Never NULL.
* @param packet - the packet being received.
* @return The packet to recieve instead, or NULL to cancel.
*/
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
return packet;
}
/**
* Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the destination player.
* @param packet - the packet to send.
*/
public void sendPacket(Player player, Object packet) {
sendPacket(getChannel(player), packet);
}
/**
* Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet to send.
*/
public void sendPacket(Channel channel, Object packet) {
channel.pipeline().writeAndFlush(packet);
}
/**
* Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Player player, Object packet) {
receivePacket(getChannel(player), packet);
}
/**
* Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Channel channel, Object packet) {
channel.pipeline().context("encoder").fireChannelRead(packet);
}
/**
* Changed from "tiny-" to "packetevents"
*
* @return handlerName
*/
protected String getHandlerName() {
return "packetevents-" + plugin.getName() + "-" + ID.incrementAndGet();
}
/**
* Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets.
* <p>
* This will automatically be called when a player has logged in.
*
* @param player - the player to inject.
*/
public void injectPlayer(Player player) {
final PlayerInjectEvent playerInjectEvent = new PlayerInjectEvent(player);
PacketEvents.getEventManager().callEvent(playerInjectEvent);
if (!playerInjectEvent.isCancelled()) {
injectChannelInternal(getChannel(player)).player = player;
}
}
/**
* Add a custom channel handler to the given channel.
*
* @param channel - the channel to inject.
* @return The intercepted channel, or NULL if it has already been injected.
*/
public void injectChannel(Channel channel) {
injectChannelInternal(channel);
}
/**
* Add a custom channel handler to the given channel.
*
* @param channel - the channel to inject.
* @return The packet interceptor.
*/
private PacketInterceptor injectChannelInternal(Channel channel) {
try {
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName);
// Inject our packet interceptor
if (interceptor == null) {
interceptor = new PacketInterceptor();
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
uninjectedChannels.remove(channel);
}
return interceptor;
} catch (IllegalArgumentException e) {
// Try again
return (PacketInterceptor) channel.pipeline().get(handlerName);
}
}
/**
* Retrieve the Netty channel associated with a player. This is cached.
*
* @param player - the player.
* @return The Netty channel.
*/
public Channel getChannel(Player player) {
Channel channel = channelLookup.get(player.getName());
// Lookup channel again
if (channel == null) {
Object connection = getConnection.get(getPlayerHandle.invoke(player));
Object manager = getManager.get(connection);
channelLookup.put(player.getName(), channel = getChannel.get(manager));
}
return channel;
}
/**
* Uninject a specific player.
*
* @param player - the injected player.
*/
public void uninjectPlayer(Player player) {
uninjectChannel(getChannel(player));
PacketEvents.getEventManager().callEvent(new PlayerUninjectEvent(player));
}
/**
* Uninject a specific channel.
* <p>
* This will also disable the automatic channel injection that occurs when a player has properly logged in.
*
* @param channel - the injected channel.
*/
public void uninjectChannel(final Channel channel) {
// No need to guard against this if we're closing
if (!closed) {
uninjectedChannels.add(channel);
}
// See ChannelInjector in ProtocolLib, line 590
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
channel.pipeline().remove(handlerName);
}
});
}
/**
* Determine if the given player has been injected by TinyProtocol.
*
* @param player - the player.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean hasInjected(Player player) {
return hasInjected(getChannel(player));
}
/**
* Determine if the given channel has been injected by TinyProtocol.
*
* @param channel - the channel.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean hasInjected(Channel channel) {
return channel.pipeline().get(handlerName) != null;
}
/**
* Cease listening for packets. This is called automatically when your plugin is disabled.
*/
public final void close() {
if (!closed) {
closed = true;
// Remove our handlers
for (Player player : plugin.getServer().getOnlinePlayers()) {
uninjectPlayer(player);
}
// Clean up Bukkit
HandlerList.unregisterAll(listener);
unregisterChannelHandler();
}
}
/**
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
*
* @author Kristian
*/
private final class PacketInterceptor extends ChannelDuplexHandler {
// Updated by the login event
public volatile Player player;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// Intercept channel
final Channel channel = ctx.channel();
handleLoginStart(channel, msg);
try {
msg = onPacketInAsync(player, channel, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
}
if (msg != null) {
super.channelRead(ctx, msg);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
try {
msg = onPacketOutAsync(player, ctx.channel(), msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
}
if (msg != null) {
super.write(ctx, msg, promise);
}
}
private void handleLoginStart(Channel channel, Object packet) {
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
GameProfile profile = getGameProfile.get(packet);
channelLookup.put(profile.getName(), channel);
}
}
}
}
@@ -1,14 +1,15 @@
package io.github.retrooper.packetevents.utils;
import io.github.retrooper.packetevents.annotations.Nullable;
import io.github.retrooper.packetevents.enums.ServerVersion;
import io.github.retrooper.packetevents.packetwrappers.Sendable;
import io.github.retrooper.packetevents.reflectionutils.Reflection;
import io.github.retrooper.packetevents.utils.nms_entityfinder.EntityFinderUtils;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.retrooper.packetevents.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -22,14 +23,18 @@ public final class NMSUtils {
private static final String obcDir = ServerVersion.getOBCDirectory();
public static Class<?> minecraftServerClass, craftWorldsClass,
packetClass, entityPlayerClass, playerConnectionClass, craftPlayerClass, serverConnectionClass, craftEntityClass, craftItemStack, nmsItemStackClass;
packetClass, entityPlayerClass, playerConnectionClass,
craftPlayerClass, serverConnectionClass, craftEntityClass,
craftItemStack, nmsItemStackClass, networkManagerClass, nettyChannelClass;
private static Method getServerMethod, getCraftWorldHandleMethod, getServerConnection, getCraftPlayerHandle, getCraftEntityHandle, sendPacketMethod, asBukkitCopy;
private static Field recentTPSField, entityPlayerPingField, playerConnectionField;
public static final Reflection.FieldAccessor<?> networkManagerFieldAccesssor, channelFieldAccessor;
static {
try {
nettyChannelClass = getNettyClass("channel.Channel");
minecraftServerClass = getNMSClass("MinecraftServer");
craftWorldsClass = getOBCClass("CraftWorld");
craftPlayerClass = getOBCClass("entity.CraftPlayer");
@@ -40,6 +45,7 @@ public final class NMSUtils {
craftEntityClass = getOBCClass("entity.CraftEntity");
craftItemStack = getOBCClass("inventory.CraftItemStack");
nmsItemStackClass = getNMSClass("ItemStack");
networkManagerClass = getNMSClass("NetworkManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
@@ -66,7 +72,8 @@ public final class NMSUtils {
e.printStackTrace();
}
networkManagerFieldAccesssor = Reflection.getField(playerConnectionClass, networkManagerClass, 0);
channelFieldAccessor = Reflection.getField(networkManagerClass, nettyChannelClass, 0);
}
public static Object getMinecraftServerInstance() throws InvocationTargetException, IllegalAccessException {
@@ -124,16 +131,32 @@ public final class NMSUtils {
}
public static Object getEntityPlayer(final Player player) {
final Object craftPlayer = getCraftPlayer(player);
Object entityPlayer = null;
try {
return getCraftPlayerHandle.invoke(craftPlayer);
return getCraftPlayerHandle.invoke(getCraftPlayer(player));
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static Object getPlayerConnection(final Player player) {
try {
return playerConnectionField.get( getEntityPlayer(player));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static Object getNetworkManager(final Player player) {
return networkManagerFieldAccesssor.get(getPlayerConnection(player));
}
public static Object getChannel(final Player player) {
return channelFieldAccessor.get(getNetworkManager(player));
}
public static int getPlayerPing(final Player player) {
Object entityPlayer = getEntityPlayer(player);
try {
@@ -13,58 +13,12 @@ import java.lang.reflect.Method;
public final class EntityFinderUtils {
@Nullable
private static ServerVersion version = PacketEvents.getServerVersion();
public static Entity getEntityById(final int id) {
for (final World world : Bukkit.getWorlds()) {
final Entity entity = getEntityByIdWithWorld(world, id);
if (entity != null) {
return entity;
}
}
return null;
}
@Nullable
public static Entity getEntityByIdWithWorld(final World world, final int id) {
if(world == null) {
return null;
}
Object craftWorld = craftWorldClass.cast(world);
Object worldServer = null;
try {
worldServer = craftWorldGetHandle.invoke(craftWorld);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Object nmsEntity = null;
try {
nmsEntity = getEntityByIdMethod.invoke(worldServer, id);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
if(nmsEntity == null) {
return null;
}
try {
return (Entity) getBukkitEntity.invoke(nmsEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static final ServerVersion version = PacketEvents.getServerVersion();
private static Class<?> worldServerClass;
private static Class<?> craftWorldClass;
private static Class<?> entityClass;
private static Method getEntityByIdMethod;
private static Method craftWorldGetHandle;
private static Method getBukkitEntity;
static {
@@ -85,6 +39,47 @@ public final class EntityFinderUtils {
}
}
public static Entity getEntityById(final int id) {
for (final World world : Bukkit.getWorlds()) {
final Entity entity = getEntityByIdWithWorld(world, id);
if (entity != null) {
return entity;
}
}
return null;
}
@Nullable
public static Entity getEntityByIdWithWorld(final World world, final int id) {
if (world == null) {
return null;
}
Object craftWorld = craftWorldClass.cast(world);
Object worldServer = null;
try {
worldServer = craftWorldGetHandle.invoke(craftWorld);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Object nmsEntity = null;
try {
nmsEntity = getEntityByIdMethod.invoke(worldServer, id);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
if (nmsEntity == null) {
return null;
}
try {
return (Entity) getBukkitEntity.invoke(nmsEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static String getEntityByNameMethodName() {
if (version.isHigherThan(ServerVersion.v_1_7_10) && version.isLowerThan(ServerVersion.v_1_9)) {
return "a";