Compare commits

...
93 Commits
Author SHA1 Message Date
retrooper 5e88678c52 PacketTypes are now constants and can be used in switch statements. packetevents-pre-8 update! 2021-03-06 13:32:07 +01:00
retrooper 60ad9f476d packetevents-pre-7 pre-release 2021-03-05 21:52:06 +01:00
retrooper 5b4579c67c packetevents-pre-7 2021-03-05 21:48:40 +01:00
retrooper d502de76dd PacketType IDs of other states no longer have duplicate values!
All IDs are initialized as you load PacketEvents using reflection, the IDs may change across java versions as the Java documentation clearly states that fields(in reflection) are not given in any particular order.
2021-03-05 21:32:43 +01:00
retrooper eb5e21eb1b Just to be safe 2021-03-05 19:31:38 +01:00
retrooper d747974950 Wrappers with #getEntity methods now also have a #getEntity(World) method, which can lead to faster entity look up time. People have rarely encountered concurrent modification exceptions when calling getEntity, I just return null in such cases. It is extremely rare, but the entity can be null! 2021-03-05 19:30:00 +01:00
retrooper ef8fe9161f PacketEvents async loading added 2021-03-01 12:13:51 +01:00
retrooper 50ede8bf24 WrappedPacketOutBlockBreakAnimation UNTESTED wrapper created,
WrappedPacketOutUpdateAttributes UNTESTED wrapper created
2021-02-28 17:50:52 +01:00
retrooper 81f90a955c 1.8-pre-6 2021-02-27 23:40:57 +01:00
retrooper 0252dbf1b8 * Project code cleanup
* Minor bug fixes
* Few setters renamed to be more logical
2021-02-27 23:34:54 +01:00
retrooper cd230ca35d Small changes to wrapper contributions. 2021-02-27 16:23:07 +01:00
retrooper 4852e4bfca ProtocolSupport support added 2021-02-27 16:17:10 +01:00
retrooper 0ed112b6fc Working on ProtocolSupport support... 2021-02-26 22:12:16 +01:00
retrooper 3db6390efb Merge pull request #106 from Tecnio/dev
Many wrappers added.
2021-02-26 22:08:31 +01:00
Tecnio 57aa65cb76 Many wrappers added. 2021-02-27 03:04:20 +03:00
retrooper 746be8a0cf WrappedPacketOutBlockChange now accepts Materials, WrappedPacketOutBlockChange#getMaterial, WrappedPacketOutBlockChange#setMaterial 2021-02-26 16:36:51 +01:00
retrooper 0ea484bdb1 PacketListenerDynamic#onPacketHandshakeReceiveEvent -> PacketListenerDynamic#onPacketHandshakeReceive 2021-02-26 12:38:05 +01:00
retrooper 072dbfbbb4 1.8-pre-5 2021-02-26 11:26:43 +01:00
retrooper 58faab9f54 WrappedPacketOutBlockChange finished,
WrappedPacketOutOpenWindow finished
2021-02-26 11:25:25 +01:00
retrooper 16d9a0c6a6 EarlyInjector finally finished, no longer those old "Failed to inject" problems,
Network manager not found issues on 1.7.10 fixed,

mSpigot support ;O(discouraged to use mSpigot though),
PacketType.Handshaking state created,

PacketType.Handshaking.Client.SET_PROTOCOL created (used to be PacketType.Login.Client.HANDSHAKE),
setData setters finished on custom payload wrappers
2021-02-25 22:55:41 +01:00
terrarier2111 c2f55aa048 Implemented ByteBufUtil#setBytes 2021-02-25 09:43:35 +01:00
retrooper bcb8d0e785 WrappedPacketInSettings & WrappedPacketOutPosition fixed with new setters 2021-02-25 07:53:55 +01:00
retrooper a881496e2b WrappedPacketInSettings#isChatColors -> WrappedPacketInSettings#isChatColored 2021-02-24 22:22:02 +01:00
retrooper e4913cb841 Made setters for almost all fields in all wrappers, some stuff are still marked as TODO. Some wrappers are incomplete. 2021-02-24 22:06:58 +01:00
retrooper 781278a2c5 Made setters for like half the client-bound play wrappers, half to do 2021-02-24 15:37:07 +01:00
retrooper f1bbcd61ed Fixed SupportedVersions annotation,
WrappedPacketHandshakingInHandshake renamed,
PacketType.Handshaking is now its own state
2021-02-24 11:37:35 +01:00
retrooper 42bdc1f1ca Setters created for all server-bound play wrappers 2021-02-24 09:36:09 +01:00
retrooper f826363d01 Many wrappers now have setter methods, not all finished though. Read all TODOs. 2021-02-23 22:49:34 +01:00
retrooper 0710f214c5 Merge pull request #104 from LIWKK/patch-24
Added player kick, when channel is null.
2021-02-23 17:28:04 +01:00
Johannes 3c4298dcea Update EarlyChannelInjector.java 2021-02-23 18:18:29 +02:00
Johannes 0aeff7cab8 Added player kick, when channel is null. 2021-02-23 18:02:22 +02:00
terrarier2111 be340a9812 Fixed ByteBufUtil#getBytes 2021-02-23 08:14:21 +01:00
retrooper 7122f1194c Created setters for a few login wrappers, TODO Make setters for all wrappers and document every wrapper 2021-02-22 22:14:11 +01:00
retrooper e8df865b3c Completed WrappedPacketOutSetSlot wrapper, TODO: document all wrappers nicely including this one. 2021-02-22 21:51:27 +01:00
retrooper 9e38883dcd Merge pull request #103 from AoElite/setslotbranch
Add WrappedPacketOutSetSlot
2021-02-22 21:38:58 +01:00
retrooper 1d955cac6e Fixed getNetworkManagers method in NMSUtils 2021-02-22 21:38:39 +01:00
terrarier2111 106b7c6d75 Improved OS lookup 2021-02-22 11:49:43 +01:00
retrooper e70609bbc2 Working on early channel injector, still has a bug... 2021-02-21 09:46:59 +01:00
AoElite e1e0735688 fix field names 2021-02-21 02:18:57 -05:00
AoElite 51a417cebd Add WrappedPacketOutSetSlot 2021-02-21 02:11:17 -05:00
retrooper c98c4e7d38 Forgot to replace server channel list with our list wrapper. Weird it isn't working still... 2021-02-20 23:12:18 +01:00
retrooper 042b55af62 Merge pull request #102 from LIWKK/patch-23
Remove NotNull annotation, player can be null in send event.
2021-02-20 23:09:48 +01:00
retrooper ab8885eb6e Merge pull request #101 from LIWKK/patch-22
Remove NotNull annotation, player can be null.
2021-02-20 23:09:43 +01:00
Johannes 069980d567 Remove NotNull annotation, player can be null in send event. 2021-02-20 23:59:52 +02:00
Johannes e6e3835846 Remove NotNull annotation, player can be null. 2021-02-20 23:59:15 +02:00
retrooper bdb83dfd86 Merge remote-tracking branch 'origin/dev' into dev 2021-02-20 22:56:57 +01:00
retrooper 628730d484 EarlyChannelInjector WIP, more progress, still has a few issues. 2021-02-20 22:56:43 +01:00
retrooper 7aefa35623 Merge pull request #100 from LIWKK/patch-21
Fix 1.7.10 loadup error
2021-02-20 20:05:01 +01:00
Johannes 8964a0ac5d Fix 1.7.10 loadup error 2021-02-20 14:05:03 +02:00
retrooper 128ff6c60b Entity metadata wrapper modified, but untested 2021-02-19 09:33:19 +01:00
retrooper 1bc2bbc905 Merge pull request #99 from DragonHunt3r/dev
Initial PacketPlayOutEntityMetaData support
2021-02-19 09:15:02 +01:00
DragonHunt3r 3f9977fd1c Merge branch 'dev' into dev 2021-02-19 09:14:45 +01:00
retrooper 6f5bc43179 BlockDig wrapper SWAP_HELD_ITEMS deprecated,
BlockPlace Hand enum fixed
2021-02-19 09:05:23 +01:00
retrooper 89cba54262 Attempt to fix bug in block place wrapper, removed protocollib from pom.xml 2021-02-17 21:22:56 +01:00
retrooper 081c2fece1 Cleanup done, this commit still has debugs, still working on fixing issues when joining too early SOMETIMES 2021-02-17 20:54:50 +01:00
retrooper 0b9f646650 Unfinished clean ups, 1.7.10 support attempted 2021-02-17 15:39:54 +01:00
retrooper 07ca8ac282 TODO: Tons of cleaning up, this commit is rushed lol 2021-02-17 08:16:30 +01:00
retrooper cb798d6fd0 New EarlyChannelInjector8, invomplete, many debugs, TODO cleanup, and re-add 1.7 support :) we now dont havevthat injection failure problem 2021-02-16 22:33:47 +01:00
DragonHunt3r 5b06f2005c Initial PacketPlayOutEntityMetaData support
Added WrappedPacketOutEntityMetadata and required classes
Current implementation is missing some features marked with `// TODO:`
2021-02-16 19:08:44 +01:00
retrooper c07960814c Merge remote-tracking branch 'origin/dev' into dev 2021-02-14 23:27:06 +01:00
retrooper a1b75243f3 player ping NPE fixed 2021-02-14 23:26:56 +01:00
retrooper 9f9ca79f90 attempt to fix concurrent modification exception 2021-02-13 22:18:31 +01:00
retrooper 241f5ed707 Merge pull request #98 from LIWKK/patch-20
[DEV] Fix EntityAction 1.7.10 bug
2021-02-13 22:14:59 +01:00
Johannes 276653975d [DEV] Fix EntityAction 1.7.10 bug 2021-02-13 11:12:48 +02:00
retrooper ff985e4eee Fixed a NPE when killing server in the event manager. 2021-02-13 07:57:36 +01:00
retrooper 65c28b4c90 1.8-pre-4! 2021-02-12 22:45:15 +01:00
retrooper e994c34689 Merge remote-tracking branch 'origin/dev' into dev 2021-02-12 21:44:37 +01:00
retrooper 0eed580e72 * Cleaner exceptions, exceptions thrown by PacketEvents should now be easier to read and will explain more about the problem. Can help you identify the issue much easier.
* WrapperUnsupportedUsageException created, called whenever you use a wrapper that isn't supported on the local server version.
2021-02-12 21:44:29 +01:00
retrooper 88ccbdbbe7 [Dev] Fixed method shenanigans by @True-cc
[Dev] Fixed method shenanigans
2021-02-12 19:39:06 +01:00
true-cc 4604981e7d Removed random load method 2021-02-12 10:36:26 -08:00
true-cc bf990c0a20 Fixed method shenanigans
constantly getting method -> storing method to var then using it later
2021-02-12 10:27:53 -08:00
retrooper cfebc47853 PacketListenerDynamic#filterXYZ added for each protocol state.
WrappedPacketOutChat fixed for different java versions than 8 HOPEFULLY
2021-02-12 13:39:35 +01:00
retrooper 4a72e70506 [Dev] [BugFix] Fixed a bug where java is idiotic in getDelcaredMethods not returning in any order by @True-cc
[Dev] [BugFix] Fixed a bug where java is idiotic in  getDelcaredMethods  not returning in any order by @True-cc
2021-02-11 08:53:23 +01:00
true-cc edc07a38ae Made all instances of getMethod store the method for later use. 2021-02-10 13:46:36 -08:00
true-cc a4daa60a7f Removed the WChat shit and added Reflection.getDeclaredMethodsInOrder 2021-02-10 13:16:44 -08:00
true-cc e3f7b6888c Added a todo x2 2021-02-10 10:32:15 -08:00
true-cc 62bb1a501b Added a todo 2021-02-10 10:31:35 -08:00
true-cc 92fab840c5 Started adding wrappers for chat stuff 2021-02-10 10:25:26 -08:00
retrooper 515a4a4f8c 1.8-pre-4 2021-02-09 21:13:45 +01:00
retrooper 7518b9335e PacketEventsSettings#injectionRescheduling toggleable,
dynamic event manager registering concurrent mod exceptions fixed, registering event listeners thread safe, unregister event listeners method removed,
2021-02-09 19:02:11 +01:00
retrooper 6221ff0b8b 1.8-PRE-3 pre-release 2021-02-07 21:02:17 +01:00
retrooper bacc1cd878 1.8-PRE-3 2021-02-07 20:41:37 +01:00
retrooper 5144bd6199 * Packet filtering in PacketListenerDynamic support (Good usage of this can improve performance of your plugin)
* PacketListenerDynamic#onPacketEventExternal added, onPacketEvent deleted(for performance improvements), onPacketEventExternal is meant for events that are not inbuilt in the PacketEvents API. PacketEvents lets you create your own custom events and call them.
2021-02-07 20:24:58 +01:00
retrooper a80770b8f7 * Support injecting multiple threads at the same on the same thread in the inject and eject executor service.(No thread switching for all players)
* WrappedPacketOutPosition#isRelative.... methods added
2021-02-07 14:55:40 +01:00
retrooper 1caca9fd96 Direction enum fixed 2021-02-06 13:56:23 +01:00
retrooper 59aa347e4e Updated BLOCK_PLACE wrapper by @Tecnio 2021-02-05 15:00:29 +01:00
Tecnio a22ce57287 Updated BLOCK_PLACE wrapper. 2021-02-05 16:51:21 +03:00
retrooper bac63c57e7 * Direction is now an enum again, cleaned up too...
* PacketHandlerInternal -> PacketProcessorInternal
2021-02-04 19:18:09 +01:00
retrooper 78171c583d Merge remote-tracking branch 'origin/dev' into dev 2021-02-04 13:21:29 +01:00
retrooper 3821c6e98a Untested WrappedPacketOutNamedSoundEffect added 2021-02-04 13:21:12 +01:00
terrarier2111 e0c71e7c86 Fixed ClientVersion#isPreRelease and improved its performance 2021-02-03 13:30:41 +01:00
terrarier2111 d9d054c0cd Cleaned up some stuff 2021-02-02 18:43:31 +01:00
retrooper 82304e4f45 * WrappedPacketInEntityAction cleanup and optimized to use much less memory with faster load times
* WrappedPacketOutChat(BaseComponent, UUID) added
* WrappedPacketOutChat support for some weird 1.7 spigots removing constructors

* Direction documentation corrected
2021-01-29 22:06:12 +01:00
166 changed files with 8165 additions and 3800 deletions

No files matched your search

+30 -26
View File
@@ -6,7 +6,7 @@
<groupId>io.github.retrooper</groupId>
<artifactId>packetevents</artifactId>
<version>1.8-PRE-2</version>
<version>1.8-pre-8</version>
<build>
<plugins>
<plugin>
@@ -36,20 +36,20 @@
<target>8</target>
</configuration>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
-->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
<resources>
<resource>
@@ -83,10 +83,6 @@
<id>viaversion-repo</id>
<url>https://repo.viaversion.com</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
<repository>
<id>sonotype</id>
<url>https://oss.sonatype.org/content/repositories/releases/</url>
@@ -101,6 +97,7 @@
<version>20.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.16.2</artifactId>
@@ -115,6 +112,13 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.13</artifactId>
<version>1.13</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.12.2</artifactId>
@@ -122,6 +126,13 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.9.4</artifactId>
<version>1.9.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.9</artifactId>
@@ -143,13 +154,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
@@ -28,9 +28,11 @@ import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent;
import io.github.retrooper.packetevents.event.manager.EventManager;
import io.github.retrooper.packetevents.event.manager.PEEventManager;
import io.github.retrooper.packetevents.exceptions.PacketEventsLoadFailureException;
import io.github.retrooper.packetevents.handler.PacketHandlerInternal;
import io.github.retrooper.packetevents.injector.GlobalChannelInjector;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.processor.PacketProcessorInternal;
import io.github.retrooper.packetevents.settings.PacketEventsSettings;
import io.github.retrooper.packetevents.updatechecker.UpdateChecker;
import io.github.retrooper.packetevents.utils.entityfinder.EntityFinderUtils;
@@ -44,9 +46,6 @@ import io.github.retrooper.packetevents.utils.server.ServerUtils;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.github.retrooper.packetevents.utils.version.PEVersion;
import io.github.retrooper.packetevents.utils.versionlookup.VersionLookupUtils;
import io.github.retrooper.packetevents.utils.versionlookup.protocollib.ProtocolLibVersionLookupUtils;
import io.github.retrooper.packetevents.utils.versionlookup.v_1_7_10.ProtocolVersionAccessor_v_1_7;
import io.github.retrooper.packetevents.utils.versionlookup.viaversion.ViaVersionLookupUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -57,39 +56,36 @@ import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.NotNull;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
public final class PacketEvents implements Listener, EventManager {
public static String handlerName;
//TODO finish unfinished wrappers
private static PacketEvents instance;
private final PEVersion version = new PEVersion(1, 7, 9, 1);
private static Plugin plugin;
private final PEVersion version = new PEVersion(1, 7, 9, 8);
private final EventManager eventManager = new PEEventManager();
private final PlayerUtils playerUtils = new PlayerUtils();
private final ServerUtils serverUtils = new ServerUtils();
private final UpdateChecker updateChecker = new UpdateChecker();
private static Plugin plugin;
public static String handlerName;
//Executor used for player injecting/ejecting.
public ExecutorService injectAndEjectExecutorService;//Initiated in init method
public PacketHandlerInternal packetHandlerInternal;
private boolean loading, loaded, initialized, initializing, terminating;
public final PacketProcessorInternal packetProcessorInternal = new PacketProcessorInternal();
public final GlobalChannelInjector injector = new GlobalChannelInjector();
private volatile boolean loading, loaded;
private final AtomicBoolean injectorReady = new AtomicBoolean(false);
private boolean initialized, initializing, terminating;
private PacketEventsSettings settings = new PacketEventsSettings();
private final ByteBufUtil byteBufUtil = NMSUtils.legacyNettyImportMode ? new ByteBufUtil_7() : new ByteBufUtil_8();
private ByteBufUtil byteBufUtil;
public static PacketEvents create(final Plugin plugin) {
if (!Bukkit.getServicesManager().isProvidedFor(PacketEvents.class)) {
instance = new PacketEvents();
Bukkit.getServicesManager().register(PacketEvents.class, instance,
plugin, ServicePriority.Normal);
PacketEvents.plugin = plugin;
return instance;
}
return instance = Bukkit.getServicesManager().load(PacketEvents.class);
@@ -112,23 +108,41 @@ public final class PacketEvents implements Listener, EventManager {
WrappedPacket.version = version;
NMSUtils.version = version;
EntityFinderUtils.version = version;
handlerName = "pe-" + plugin.getName();
try {
NMSUtils.load();
//Load all classes
PacketTypeClasses.load();
PacketType.load();
EntityFinderUtils.load();
} catch (Exception ex) {
loading = false;
throw new PacketEventsLoadFailureException(ex);
}
byteBufUtil = NMSUtils.legacyNettyImportMode ? new ByteBufUtil_7() : new ByteBufUtil_8();
if (!injectorReady.get()) {
injector.load();
injector.inject();
injectorReady.set(true);
}
loaded = true;
loading = false;
}
}
public void loadAsyncNewThread() {
new Thread(this::load).start();
}
public void loadAsync(ExecutorService executorService) {
executorService.execute(this::load);
}
public void loadSettings(PacketEventsSettings settings) {
this.settings = settings;
}
@@ -142,44 +156,28 @@ public final class PacketEvents implements Listener, EventManager {
if (!initialized && !initializing) {
initializing = true;
settings = packetEventsSettings;
if (settings.getInjectAndEjectThreadCount() < 1) {
settings.injectAndEjectThreadCount(1);
}
settings.lock();
int injectAndEjectThreadCount = settings.getInjectAndEjectThreadCount();
injectAndEjectExecutorService = Executors.newFixedThreadPool(injectAndEjectThreadCount, new ThreadFactory() {
if (settings.shouldCheckForUpdates()) {
handleUpdateCheck();
}
private final AtomicInteger id = new AtomicInteger();
@Override
public Thread newThread(@NotNull Runnable r) {
return new Thread(r, "PacketEvents-inject-eject #" + id.getAndIncrement());
}
});
//Register Bukkit listener
plugin = pl;
//We may not continue until the injector has been initialized!
while (!injectorReady.get()) {
;
}
Bukkit.getPluginManager().registerEvents(this, plugin);
handlerName = "pe-" + plugin.getName();
packetHandlerInternal = new PacketHandlerInternal();
for (final Player p : Bukkit.getOnlinePlayers()) {
try {
getPlayerUtils().injectPlayer(p);
} catch (Exception ex) {
p.kickPlayer(getSettings().getInjectionFailureMessage());
//IT IS NOT RECOMMENDED TO RELOAD!
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(p, false));
}
catch (Exception ex) {
p.kickPlayer("Please rejoin!");
}
}
if (settings.shouldCheckForUpdates()) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
UpdateChecker.UpdateCheckerStatus status = updateChecker.checkForUpdate();
}
}, "PacketEvents-update-check-thread");
thread.start();
}
initialized = true;
initializing = false;
}
@@ -188,12 +186,12 @@ public final class PacketEvents implements Listener, EventManager {
public void terminate() {
if (initialized && !terminating) {
for (Player player : Bukkit.getOnlinePlayers()) {
packetHandlerInternal.ejectPlayer(player);
injector.ejectPlayer(player);
}
packetHandlerInternal.cleanup();
injector.eject();
getEventManager().unregisterAllListeners();
injectAndEjectExecutorService.shutdownNow();
initialized = false;
terminating = false;
}
@@ -201,6 +199,7 @@ public final class PacketEvents implements Listener, EventManager {
/**
* Use {@link #terminate()}. This is deprecated
*
* @deprecated "Stop" might be misleading and "terminate" sounds better I guess...
*/
@Deprecated
@@ -271,71 +270,90 @@ public final class PacketEvents implements Listener, EventManager {
}
@EventHandler(priority = EventPriority.LOWEST)
public void onLogin(final PlayerLoginEvent e) {
if (getSettings().shouldInjectEarly()) {
try {
packetHandlerInternal.injectPlayer(e.getPlayer());
} catch (Exception ex) {
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, PacketEvents.get().getSettings().getInjectionFailureMessage());
}
public void onLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
if (!getSettings().shouldUseCompatibilityInjector()) {
injector.injectPlayer(player);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onJoin(final PlayerJoinEvent e) {
final InetSocketAddress address = e.getPlayer().getAddress();
boolean viaAvailable = ViaVersionLookupUtils.isAvailable();
PacketEvents.get().getPlayerUtils().loginTime.put(e.getPlayer().getUniqueId(), System.currentTimeMillis());
if (viaAvailable) {
PacketEvents.get().getPlayerUtils().clientVersionsMap.put(e.getPlayer().getAddress(), ClientVersion.TEMP_UNRESOLVED);
Bukkit.getScheduler().runTaskLaterAsynchronously(getPlugin(), new Runnable() {
@Override
public void run() {
int protocolVersion = VersionLookupUtils.getProtocolVersion(e.getPlayer());
ClientVersion version = ClientVersion.getClientVersion(protocolVersion);
PacketEvents.get().getPlayerUtils().clientVersionsMap.put(address, version);
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer(), true));
}
}, 1L);
} else if (getServerUtils().getVersion() == ServerVersion.v_1_7_10) {
ClientVersion version = ClientVersion.getClientVersion(ProtocolVersionAccessor_v_1_7.getProtocolVersion(e.getPlayer()));
if (version == ClientVersion.UNRESOLVED) {
version = getPlayerUtils().tempClientVersionMap.get(address);
if (version == null) {
version = ClientVersion.UNRESOLVED;
}
}
getPlayerUtils().clientVersionsMap.put(address, version);
public void onJoin(PlayerJoinEvent e) {
Player player = e.getPlayer();
InetSocketAddress address = player.getAddress();
boolean shouldInject = getSettings().shouldUseCompatibilityInjector() || !(injector.hasInjected(e.getPlayer()));
//Inject now if we are using the compatibility-injector or inject if the early injector failed to inject them.
if (shouldInject) {
injector.injectPlayer(player);
}
if (!getSettings().shouldInjectEarly()) {
try {
packetHandlerInternal.injectPlayer(e.getPlayer());
//Injection was successful as no exception was thrown...
if (!viaAvailable) {
if (ProtocolLibVersionLookupUtils.isAvailable()) {
ClientVersion version = ClientVersion.getClientVersion(ProtocolLibVersionLookupUtils.getProtocolVersion(e.getPlayer()));
PacketEvents.get().getPlayerUtils().clientVersionsMap.put(address, version);
}
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer(), false));
boolean dependencyAvailable = VersionLookupUtils.isDependencyAvailable();
PacketEvents.get().getPlayerUtils().loginTime.put(player.getUniqueId(), System.currentTimeMillis());
//A supported dependency is available, we need to first ask the dependency for the client version.
if (dependencyAvailable) {
Bukkit.getScheduler().runTaskLaterAsynchronously(getPlugin(), () -> {
try {
int protocolVersion = VersionLookupUtils.getProtocolVersion(player);
ClientVersion version = ClientVersion.getClientVersion(protocolVersion);
PacketEvents.get().getPlayerUtils().clientVersionsMap.put(address, version);
} catch (Exception ignored) {
}
} catch (Exception ex) {
e.getPlayer().kickPlayer(getSettings().getInjectionFailureMessage());
}
} else {
if (!viaAvailable) {
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer(), false));
}
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(player, true));
}, 1L);
}
else {
//Dependency isn't available, we can already call the post player inject event.
PacketEvents.get().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer(), false));
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onQuit(final PlayerQuitEvent e) {
UUID uuid = e.getPlayer().getUniqueId();
packetHandlerInternal.ejectPlayer(e.getPlayer());
PacketEvents.get().getPlayerUtils().loginTime.remove(uuid);
PacketEvents.get().getPlayerUtils().playerPingMap.remove(uuid);
PacketEvents.get().getPlayerUtils().playerSmoothedPingMap.remove(uuid);
public void onQuit(PlayerQuitEvent e) {
Player player = e.getPlayer();
UUID uuid = player.getUniqueId();
InetSocketAddress address = player.getAddress();
injector.ejectPlayer(e.getPlayer());
playerUtils.loginTime.remove(uuid);
playerUtils.playerPingMap.remove(uuid);
playerUtils.playerSmoothedPingMap.remove(uuid);
packetProcessorInternal.keepAliveMap.remove(uuid);
packetProcessorInternal.channelMap.remove(player.getName());
playerUtils.clientVersionsMap.remove(address);
playerUtils.tempClientVersionMap.remove(address);
}
private void handleUpdateCheck() {
Thread thread = new Thread(() -> {
PacketEvents.get().getPlugin().getLogger().info("[packetevents] Checking for an update, please wait...");
UpdateChecker.UpdateCheckerStatus status = updateChecker.checkForUpdate();
int seconds = 5;
for (int i = 0; i < 5; i++) {
if (status == UpdateChecker.UpdateCheckerStatus.FAILED) {
PacketEvents.get().getPlugin().getLogger().severe("[packetevents] Checking for an update again in " + seconds + " seconds...");
try {
Thread.sleep(seconds * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
seconds *= 2;
status = updateChecker.checkForUpdate();
if (i == 4) {
PacketEvents.get().getPlugin().getLogger().severe("[packetevents] PacketEvents failed to check for an update. No longer retrying.");
break;
}
} else {
break;
}
}
}, "PacketEvents-update-check-thread");
thread.start();
}
}
@@ -24,28 +24,27 @@
package io.github.retrooper.packetevents;
import io.github.retrooper.packetevents.settings.PacketEventsSettings;
import org.bukkit.plugin.java.JavaPlugin;
public class PacketEventsPlugin extends JavaPlugin {
@Override
public void onLoad() {
PacketEvents.create(this);
PacketEventsSettings settings = PacketEvents.get().getSettings();
settings.compatInjector(true);
settings.checkForUpdates(false);
PacketEvents.get().loadAsyncNewThread();
}
@Override
public void onEnable() {
if (PacketEvents.get() == null) {
PacketEvents.create(this);
PacketEvents.get().load();
PacketEvents.get().init(this);
}
PacketEvents.get().init(this);
}
@Override
public void onDisable() {
if (PacketEvents.get() != null) {
PacketEvents.get().terminate();
}
PacketEvents.get().terminate();
}
}
@@ -55,7 +55,11 @@ public abstract class PacketEvent implements CallableEvent {
this.timestamp = timestamp;
}
public void callPacketEvent(final PacketListenerDynamic listener) {
listener.onPacketEvent(this);
public void callPacketEventExternal(final PacketListenerDynamic listener) {
listener.onPacketEventExternal(this);
}
public boolean isInbuilt() {
return false;
}
}
@@ -26,6 +26,7 @@ package io.github.retrooper.packetevents.event;
import io.github.retrooper.packetevents.event.impl.*;
import io.github.retrooper.packetevents.event.priority.PacketEventPriority;
import io.github.retrooper.packetevents.utils.immutableset.ImmutableSetCustom;
/**
* Dynamic packet event listener.
@@ -37,31 +38,144 @@ import io.github.retrooper.packetevents.event.priority.PacketEventPriority;
*/
public abstract class PacketListenerDynamic {
private final PacketEventPriority priority;
public ImmutableSetCustom<Byte> serverSidedStatusAllowance;
public ImmutableSetCustom<Byte> serverSidedLoginAllowance;
public ImmutableSetCustom<Byte> serverSidedPlayAllowance;
public ImmutableSetCustom<Byte> clientSidedStatusAllowance;
public ImmutableSetCustom<Byte> clientSidedHandshakeAllowance;
public ImmutableSetCustom<Byte> clientSidedLoginAllowance;
public ImmutableSetCustom<Byte> clientSidedPlayAllowance;
public PacketListenerDynamic(final PacketEventPriority priority) {
this.priority = priority;
this.serverSidedStatusAllowance = null;
this.serverSidedLoginAllowance = null;
this.serverSidedPlayAllowance = null;
this.clientSidedStatusAllowance = null;
this.clientSidedHandshakeAllowance = null;
this.clientSidedLoginAllowance = null;
this.clientSidedPlayAllowance = null;
}
public PacketListenerDynamic() {
this(PacketEventPriority.NORMAL);
}
public PacketEventPriority getPriority() {
public final PacketEventPriority getPriority() {
return priority;
}
public void onPacketStatusReceive(PacketStatusReceiveEvent event) {
public final void addServerSidedStatusFilter(Byte... statusPacketIDs) {
if (this.serverSidedStatusAllowance == null) {
this.serverSidedStatusAllowance = new ImmutableSetCustom<>(statusPacketIDs);
} else {
this.serverSidedStatusAllowance.addAll(statusPacketIDs);
}
}
public final void addServerSidedLoginFilter(Byte... loginPacketIDs) {
if (this.serverSidedLoginAllowance == null) {
this.serverSidedLoginAllowance = new ImmutableSetCustom<>(loginPacketIDs);
} else {
this.serverSidedLoginAllowance.addAll(loginPacketIDs);
}
}
public final void addServerSidedPlayFilter(Byte... playPacketIDs) {
if (this.serverSidedPlayAllowance == null) {
this.serverSidedPlayAllowance = new ImmutableSetCustom<>(playPacketIDs);
} else {
this.serverSidedPlayAllowance.addAll(playPacketIDs);
}
}
public final void addClientSidedStatusFilter(Byte... statusPacketIDs) {
if (this.clientSidedStatusAllowance == null) {
this.clientSidedStatusAllowance = new ImmutableSetCustom<>(statusPacketIDs);
} else {
this.clientSidedStatusAllowance.addAll(statusPacketIDs);
}
}
public final void addClientSidedHandshakeFilter(Byte... handshakePacketIDs) {
if (this.clientSidedHandshakeAllowance == null) {
this.clientSidedHandshakeAllowance = new ImmutableSetCustom<>(handshakePacketIDs);
} else {
this.clientSidedHandshakeAllowance.addAll(handshakePacketIDs);
}
}
public final void addClientSidedLoginFilter(Byte... loginPacketIDs) {
if (this.clientSidedLoginAllowance == null) {
this.clientSidedLoginAllowance = new ImmutableSetCustom<>(loginPacketIDs);
} else {
this.clientSidedLoginAllowance.addAll(loginPacketIDs);
}
}
public final void addClientSidedPlayFilter(Byte... playPacketIDs) {
if (this.clientSidedPlayAllowance == null) {
this.clientSidedPlayAllowance = new ImmutableSetCustom<>(playPacketIDs);
} else {
this.clientSidedPlayAllowance.addAll(playPacketIDs);
}
}
public final void filterAll() {
filterServerSidedStatus();
filterServerSidedLogin();
filterServerSidedPlay();
filterClientSidedStatus();
filterClientSidedHandshake();
filterClientSidedLogin();
filterClientSidedPlay();
}
public final void filterServerSidedStatus() {
this.serverSidedStatusAllowance = new ImmutableSetCustom<>();
}
public final void filterServerSidedLogin() {
this.serverSidedLoginAllowance = new ImmutableSetCustom<>();
}
public final void filterServerSidedPlay() {
this.serverSidedPlayAllowance = new ImmutableSetCustom<>();
}
public final void filterClientSidedStatus() {
this.clientSidedStatusAllowance = new ImmutableSetCustom<>();
}
public final void filterClientSidedHandshake() {
this.clientSidedHandshakeAllowance = new ImmutableSetCustom<>();
}
public final void filterClientSidedLogin() {
this.clientSidedLoginAllowance = new ImmutableSetCustom<>();
}
public final void filterClientSidedPlay() {
this.clientSidedPlayAllowance = new ImmutableSetCustom<>();
}
public void onPacketStatusReceive(PacketStatusReceiveEvent event) {
}
public void onPacketStatusSend(PacketStatusSendEvent event) {
}
public void onPacketHandshakeReceive(PacketHandshakeReceiveEvent event) {
}
public void onPacketLoginReceive(PacketLoginReceiveEvent event) {
}
public void onPacketLoginSend(PacketLoginSendEvent event) {
}
public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
@@ -77,7 +191,6 @@ public abstract class PacketListenerDynamic {
}
public void onPostPlayerInject(PostPlayerInjectEvent event) {
}
public void onPlayerInject(PlayerInjectEvent event) {
@@ -86,6 +199,6 @@ public abstract class PacketListenerDynamic {
public void onPlayerEject(PlayerEjectEvent event) {
}
public void onPacketEvent(PacketEvent event) {
public void onPacketEventExternal(PacketEvent event) {
}
}
@@ -32,7 +32,7 @@ import io.github.retrooper.packetevents.event.impl.PacketPlayReceiveEvent;
* The {@link PacketEvent} implements this interface.
* Every inbuilt event should implement the {@link #call(PacketListenerDynamic)} method.
* If you are making a custom event, don't implement this.
* The {@link PacketListenerDynamic#onPacketEvent(PacketEvent)} method is called for every event,
* The {@link PacketListenerDynamic#onPacketEventExternal(PacketEvent)} method is called for every event that is not in-built.
* including custom events.
*
* @author retrooper
@@ -54,18 +54,4 @@ public interface CancellableEvent {
* @param val Is the event cancelled
*/
void setCancelled(boolean val);
/**
* Cancel the event.
* You can achieve the same result by just using {@link #setCancelled(boolean)}
*/
@Deprecated
void cancel();
/**
* Uncancel the event.
* You can achieve the same result by just using {@link #setCancelled(boolean)}
*/
@Deprecated
void uncancel();
}
@@ -15,16 +15,6 @@ public abstract class CancellableNMSPacketEvent extends NMSPacketEvent implement
super(address, packet);
}
@Override
public void cancel() {
cancelled = true;
}
@Override
public void uncancel() {
cancelled = false;
}
@Override
public boolean isCancelled() {
return cancelled;
@@ -104,5 +104,12 @@ public abstract class NMSPacketEvent extends PacketEvent implements CallableEven
*
* @return Packet ID.
*/
public abstract byte getPacketId();
public byte getPacketId() {
return PacketType.packetIDMap.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public boolean isInbuilt() {
return true;
}
}
@@ -0,0 +1,35 @@
package io.github.retrooper.packetevents.event.impl;
import io.github.retrooper.packetevents.event.PacketListenerDynamic;
import io.github.retrooper.packetevents.event.eventtypes.CancellableNMSPacketEvent;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import java.net.InetSocketAddress;
/**
* The {@code PacketHandshakeReceiveEvent} event is fired whenever the a HANDSHAKE packet is received from a client.
* The {@code PacketHandshakeReceiveEvent} does not have to do with a bukkit player object due to the player object being null in this state.
* Use the {@link #getSocketAddress()} to identify who sends the packet.
*
* @author retrooper
* @see <a href="https://wiki.vg/Protocol#Handshaking">https://wiki.vg/Protocol#Handshaking</a>
* @since 1.8
*/
public class PacketHandshakeReceiveEvent extends CancellableNMSPacketEvent {
public PacketHandshakeReceiveEvent(final Object channel, final NMSPacket packet) {
super(channel, packet);
}
public PacketHandshakeReceiveEvent(final InetSocketAddress socketAddress, final NMSPacket packet) {
super(socketAddress, packet);
}
@Override
public void call(PacketListenerDynamic listener) {
if (listener.clientSidedLoginAllowance == null || listener.clientSidedLoginAllowance.contains(getPacketId())) {
listener.onPacketHandshakeReceive(this);
}
}
}
@@ -50,22 +50,10 @@ public class PacketLoginReceiveEvent extends CancellableNMSPacketEvent {
super(socketAddress, packet);
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Login.Client.HANDSHAKE) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Login.Client.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketLoginReceive(this);
if (listener.clientSidedLoginAllowance == null || listener.clientSidedLoginAllowance.contains(getPacketId())) {
listener.onPacketLoginReceive(this);
}
}
}
@@ -50,13 +50,10 @@ public class PacketLoginSendEvent extends CancellableNMSPacketEvent {
super(address, packet);
}
@Override
public byte getPacketId() {
return PacketType.Login.Server.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketLoginSend(this);
if (listener.serverSidedLoginAllowance == null || listener.serverSidedLoginAllowance.contains(getPacketId())) {
listener.onPacketLoginSend(this);
}
}
}
@@ -45,7 +45,7 @@ import org.jetbrains.annotations.NotNull;
public final class PacketPlayReceiveEvent extends CancellableNMSPacketEvent implements PlayerEvent {
private final Player player;
public PacketPlayReceiveEvent(final Player player, final Object channel, final NMSPacket packet) {
public PacketPlayReceiveEvent(Player player, Object channel, NMSPacket packet) {
super(channel, packet);
this.player = player;
}
@@ -62,31 +62,10 @@ public final class PacketPlayReceiveEvent extends CancellableNMSPacketEvent impl
return player;
}
/**
* This method returns the socket address of the packet sender.
* This address if guaranteed to never be null.
* You could use this to identify who is sending packets
* whenever the player object is null.
*
* @return Packet sender's socket address.
*/
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Play.Client.USE_ENTITY) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Play.Client.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketPlayReceive(this);
if (listener.clientSidedPlayAllowance == null || listener.clientSidedPlayAllowance.contains(getPacketId())) {
listener.onPacketPlayReceive(this);
}
}
}
@@ -29,9 +29,12 @@ import io.github.retrooper.packetevents.event.eventtypes.CancellableNMSPacketEve
import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* The {@code PacketPlaySendEvent} event is fired whenever the a PLAY packet is about to be sent.
* Cancelling this event will result in preventing minecraft from sending the packet.
@@ -61,23 +64,11 @@ public final class PacketPlaySendEvent extends CancellableNMSPacketEvent impleme
return player;
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Play.Server.KEEP_ALIVE) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Play.Server.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketPlaySend(this);
if (listener.serverSidedPlayAllowance == null || listener.serverSidedPlayAllowance.contains(getPacketId())) {
listener.onPacketPlaySend(this);
}
}
}
@@ -50,22 +50,10 @@ public class PacketStatusReceiveEvent extends CancellableNMSPacketEvent {
super(socketAddress, packet);
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Status.Client.PING) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Status.Client.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketStatusReceive(this);
if (listener.clientSidedStatusAllowance == null || listener.clientSidedStatusAllowance.contains(getPacketId())) {
listener.onPacketStatusReceive(this);
}
}
}
@@ -52,22 +52,11 @@ public class PacketStatusSendEvent extends CancellableNMSPacketEvent {
super(address, packet);
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Status.Server.PONG) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Status.Server.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPacketStatusSend(this);
if (listener.serverSidedStatusAllowance == null || listener.serverSidedStatusAllowance.contains(getPacketId())) {
listener.onPacketStatusSend(this);
}
}
}
@@ -24,12 +24,10 @@
package io.github.retrooper.packetevents.event.impl;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.PacketEvent;
import io.github.retrooper.packetevents.event.PacketListenerDynamic;
import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent;
import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent;
import io.github.retrooper.packetevents.settings.PacketEventsSettings;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -44,22 +42,10 @@ import org.jetbrains.annotations.NotNull;
*/
public final class PlayerEjectEvent extends PacketEvent implements CancellableEvent, PlayerEvent {
private final Player player;
private final boolean async;
private boolean cancelled;
public PlayerEjectEvent(final Player player, final boolean isAsync) {
public PlayerEjectEvent(final Player player) {
this.player = player;
this.async = isAsync;
}
@Override
public void cancel() {
cancelled = true;
}
@Override
public void uncancel() {
cancelled = false;
}
@Override
@@ -85,20 +71,21 @@ public final class PlayerEjectEvent extends PacketEvent implements CancellableEv
}
/**
* This method returns if the event has been called asynchronously.
* If the {@link PacketEventsSettings#shouldEjectAsync()} is enabled, the event will be called asynchronously
* and the player will be ejected asynchronously.
* The {@link PacketEventsSettings} can be accessed here:
*
* @return Is the ejection asynchronous.
* @see PacketEvents#getSettings()
* @return Is the event called async?
* @deprecated Now always false
*/
@Deprecated
public boolean isAsync() {
return async;
return false;
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPlayerEject(this);
}
@Override
public boolean isInbuilt() {
return true;
}
}
@@ -29,10 +29,8 @@ import io.github.retrooper.packetevents.event.PacketEvent;
import io.github.retrooper.packetevents.event.PacketListenerDynamic;
import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent;
import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent;
import io.github.retrooper.packetevents.settings.PacketEventsSettings;
import io.github.retrooper.packetevents.utils.netty.channel.ChannelUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.InetSocketAddress;
@@ -49,23 +47,11 @@ import java.net.InetSocketAddress;
public final class PlayerInjectEvent extends PacketEvent implements CancellableEvent, PlayerEvent {
private final Player player;
private final InetSocketAddress address;
private final boolean async;
private boolean cancelled;
public PlayerInjectEvent(final Player player, final boolean isAsync) {
public PlayerInjectEvent(final Player player) {
this.player = player;
this.address = ChannelUtils.getSocketAddress(PacketEvents.get().packetHandlerInternal.getChannel(player));
this.async = isAsync;
}
@Override
public void cancel() {
cancelled = true;
}
@Override
public void uncancel() {
cancelled = false;
this.address = ChannelUtils.getSocketAddress(PacketEvents.get().packetProcessorInternal.getChannel(player));
}
@Override
@@ -95,26 +81,26 @@ public final class PlayerInjectEvent extends PacketEvent implements CancellableE
*
* @return Socket address of the injecting player.
*/
@NotNull
@Nullable
public InetSocketAddress getSocketAddress() {
return address;
}
/**
* This method returns if the event has been called asynchronously.
* If the {@link PacketEventsSettings#shouldInjectAsync()} is enabled, the event will be called asynchronously
* and the player will be injected asynchronously.
* The {@link PacketEventsSettings} can be accessed here:
*
* @return Is the injection asynchronous.
* @see PacketEvents#getSettings()
* @deprecated Will always be false
*/
@Deprecated
public boolean isAsync() {
return async;
return false;
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPlayerInject(this);
}
@Override
public boolean isInbuilt() {
return true;
}
}
@@ -30,7 +30,6 @@ import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
/**
* The {@code PostPacketPlayReceiveEvent} event is fired after minecraft processes
@@ -54,32 +53,18 @@ public class PostPacketPlayReceiveEvent extends NMSPacketEvent implements Player
/**
* This method returns the bukkit player object of the packet sender.
* The player object might be null during early packets.
*
* @return Packet sender.
*/
@Nullable
@Override
public Player getPlayer() {
return player;
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Play.Client.ARM_ANIMATION) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Play.Client.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPostPacketPlayReceive(this);
if (listener.clientSidedPlayAllowance == null || listener.clientSidedPlayAllowance.contains(getPacketId())) {
listener.onPostPacketPlayReceive(this);
}
}
}
@@ -30,7 +30,6 @@ import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
/**
* The {@code PostPacketPlaySendEvent} event is fired after minecraft processes
@@ -54,32 +53,18 @@ public class PostPacketPlaySendEvent extends NMSPacketEvent implements PlayerEve
/**
* This method returns the bukkit player object of the packet sender.
* The player object is might be null during early packets.
*
* @return Packet receiver.
*/
@Nullable
@Override
public Player getPlayer() {
return player;
}
/**
* Each binding in each packet state has their own constants.
* Example Usage:
* <p>
* {@code if (getPacketId() == PacketType.Play.Server.KEEP_ALIVE) }
* </p>
*
* @return Packet ID.
*/
@Override
public byte getPacketId() {
return PacketType.Play.Server.packetIds.getOrDefault(packet.getRawNMSPacket().getClass(), PacketType.INVALID);
}
@Override
public void call(PacketListenerDynamic listener) {
listener.onPostPacketPlaySend(this);
if (listener.serverSidedPlayAllowance == null || listener.serverSidedPlayAllowance.contains(getPacketId())) {
listener.onPostPacketPlaySend(this);
}
}
}
@@ -75,7 +75,7 @@ public class PostPlayerInjectEvent extends PacketEvent implements PlayerEvent {
*/
@NotNull
public Object getChannel() {
return PacketEvents.get().packetHandlerInternal.getChannel(player);
return PacketEvents.get().packetProcessorInternal.getChannel(player);
}
@NotNull
@@ -107,4 +107,9 @@ public class PostPlayerInjectEvent extends PacketEvent implements PlayerEvent {
public void call(PacketListenerDynamic listener) {
listener.onPostPlayerInject(this);
}
@Override
public boolean isInbuilt() {
return true;
}
}
@@ -136,29 +136,6 @@ public interface EventManager {
return this;
}
/**
* Unregister the dynamic event listener.
*
* @param listener {@link PacketListenerDynamic}
* @return Same event manager instance.
*/
default EventManager unregisterListener(PacketListenerDynamic listener) {
PEEventManager.EVENT_MANAGER_DYNAMIC.unregisterListener(listener);
return this;
}
/**
* Unregister multiple dynamic event listeners with one method.
* Recommended to use the dynamic event listener.
*
* @param listeners {@link PacketListenerDynamic}
* @return Same event manager instance.
*/
default EventManager unregisterListeners(PacketListenerDynamic... listeners) {
PEEventManager.EVENT_MANAGER_DYNAMIC.unregisterListeners(listeners);
return this;
}
/**
* Unregister all registered event listeners.
* All the deprecated and the dynamic listeners will be unregistered.
@@ -30,16 +30,13 @@ import io.github.retrooper.packetevents.event.PacketListenerDynamic;
import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent;
import io.github.retrooper.packetevents.event.priority.PacketEventPriority;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
class EventManagerDynamic {
/**
* All listeners ordered by their priority.
* The most low priority ones are at the begging of the list and the most high priority ones are at the end.
*/
private final List<PacketListenerDynamic> listeners = new ArrayList<>();
private final Map<Byte, HashSet<PacketListenerDynamic>> listenersMap = new ConcurrentHashMap<>();
/**
* Call the PacketEvent.
@@ -58,26 +55,31 @@ class EventManagerDynamic {
cancel = ((CancellableEvent) event).isCancelled();
}
byte highestReachedPriority = PacketEventPriority.LOWEST.getPriorityValue();
for (PacketListenerDynamic listener : listeners) {
highestReachedPriority = listener.getPriority().getPriorityValue();
try {
event.callPacketEvent(listener);
event.call(listener);
}
catch (Exception ex) {
PacketEvents.get().getPlugin().getLogger()
.log(Level.SEVERE, "PacketEvents found an exception while calling a packet listener.", ex);
}
if (event instanceof CancellableEvent) {
CancellableEvent ce = (CancellableEvent) event;
cancel = ce.isCancelled();
for (byte priority = PacketEventPriority.LOWEST.getPriorityValue(); priority <= PacketEventPriority.MONITOR.getPriorityValue(); priority++) {
HashSet<PacketListenerDynamic> listeners = listenersMap.get(priority);
if (listeners != null) {
for (PacketListenerDynamic listener : listeners) {
try {
if (!event.isInbuilt()) {
event.callPacketEventExternal(listener);
}
event.call(listener);
} catch (Exception ex) {
PacketEvents.get().getPlugin().getLogger()
.log(Level.SEVERE, "PacketEvents found an exception while calling a packet listener.", ex);
}
if (event instanceof CancellableEvent) {
CancellableEvent ce = (CancellableEvent) event;
cancel = ce.isCancelled();
highestReachedPriority = priority;
}
}
if (event instanceof CancellableEvent) {
CancellableEvent ce = (CancellableEvent) event;
ce.setCancelled(cancel);
}
}
}
if (event instanceof CancellableEvent) {
CancellableEvent ce = (CancellableEvent) event;
ce.setCancelled(cancel);
}
PEEventManager.EVENT_MANAGER_LEGACY.callEvent(event, highestReachedPriority);
}
@@ -86,26 +88,14 @@ class EventManagerDynamic {
*
* @param listener {@link PacketListenerDynamic}
*/
public void registerListener(final PacketListenerDynamic listener) {
final byte priorityValue = listener.getPriority().getPriorityValue();
if (listeners.isEmpty()) {
listeners.add(listener);
return;
}
for (int i = 0; i < listeners.size(); i++) {
PacketListenerDynamic other = listeners.get(i);
byte otherPriorityValue = other.getPriority().getPriorityValue();
if (i + 1 == listeners.size()) {
listeners.add(listener);
break;
} else if (otherPriorityValue == priorityValue) {
listeners.add(i + 1, listener);
break;
} else if (otherPriorityValue > priorityValue) {
listeners.add(i, listener);
break;
}
public synchronized void registerListener(final PacketListenerDynamic listener) {
byte priority = listener.getPriority().getPriorityValue();
HashSet<PacketListenerDynamic> listenerSet = listenersMap.get(priority);
if (listenerSet == null) {
listenerSet = new HashSet<>();
}
listenerSet.add(listener);
listenersMap.put(priority, listenerSet);
}
/**
@@ -113,36 +103,16 @@ class EventManagerDynamic {
*
* @param listeners {@link PacketListenerDynamic}
*/
public void registerListeners(PacketListenerDynamic... listeners) {
public synchronized void registerListeners(PacketListenerDynamic... listeners) {
for (PacketListenerDynamic listener : listeners) {
registerListener(listener);
}
}
/**
* Unregister the dynamic packet event listener.
*
* @param listener {@link PacketListenerDynamic}
*/
public void unregisterListener(PacketListenerDynamic listener) {
listeners.remove(listener);
}
/**
* Unregister multiple dynamic packet event listeners with one method.
*
* @param listeners {@link PacketListenerDynamic}
*/
public void unregisterListeners(PacketListenerDynamic... listeners) {
for (PacketListenerDynamic listener : listeners) {
unregisterListener(listener);
}
}
/**
* Unregister all dynamic packet event listeners.
*/
public void unregisterAllListeners() {
listeners.clear();
public synchronized void unregisterAllListeners() {
listenersMap.clear();
}
}
}
@@ -163,4 +163,4 @@ class EventManagerLegacy {
public void unregisterAllListeners() {
staticRegisteredMethods.clear();
}
}
}
@@ -41,7 +41,7 @@ public enum PacketEventPriority {
* Use this if you need to be the first processing the event and
* need no power in cancelling an event or preventing an event cancellation.
*/
LOWEST((byte) 0),
LOWEST,
/**
* A weak event priority.
@@ -49,7 +49,7 @@ public enum PacketEventPriority {
* Use this if you would prefer to be one of the first to process the event,
* but don't mind if some other listener processes before you.
*/
LOW((byte) 1),
LOW,
/**
* Default event priority.
@@ -57,14 +57,14 @@ public enum PacketEventPriority {
* Use this if you don't really care/know when you process or just want to
* be in the middle.
*/
NORMAL((byte) 2),
NORMAL,
/**
* Higher than the {@link PacketEventPriority#NORMAL} event priority.
* Fourth to be processed(IN THE DYNAMIC EVENT SYSTEM ONLY).
* Use this if you want to process before the default event prioritized listeners.
*/
HIGH((byte) 3),
HIGH,
/**
* Second most powerful event priority.
@@ -75,7 +75,7 @@ public enum PacketEventPriority {
* mind if some other listener urgently needs to decide over you.
* {@link PacketEventPriority#MONITOR} is rarely ever recommended to use.
*/
HIGHEST((byte) 4),
HIGHEST,
/**
* Most powerful event priority.
@@ -84,24 +84,14 @@ public enum PacketEventPriority {
* or urgently need to be the final decider whether the event has cancelled or not.
* This is rarely recommended.
*/
MONITOR((byte) 5);
MONITOR;
final byte priorityValue;
PacketEventPriority(byte priorityValue) {
this.priorityValue = priorityValue;
public static PacketEventPriority getPacketEventPriority(byte bytePriority) {
return values()[bytePriority];
}
public byte getPriorityValue() {
return priorityValue;
}
public static PacketEventPriority getPacketEventPriority(byte bytePriority) {
for (PacketEventPriority priority : values()) {
if (priority.priorityValue == bytePriority) {
return priority;
}
}
return NORMAL;
return (byte) ordinal();
}
}
@@ -0,0 +1,48 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.exceptions;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.reflection.ClassUtil;
/**
* An exception thrown by PacketEvents when a wrapper is used although it isn't supported on the local server version.
* Make sure to decompile a wrapper before using it, if you are unsure if it is supported through out all the server versions.
* We override a {@link WrappedPacket#isSupported()} method if it isn't supported on all server versions.
*
* @author retrooper
* @see WrappedPacket#isSupported()
* @see WrappedPacket.SupportedVersions
* @since 1.7
*/
public class WrapperUnsupportedUsageException extends RuntimeException {
public WrapperUnsupportedUsageException(String message) {
super(message);
}
public WrapperUnsupportedUsageException(Class<? extends WrappedPacket> wrapperClass) {
this("You are using a packet wrapper which happens to be unsupported on the local server version. Packet wrapper you attempted to use: " + ClassUtil.getClassSimpleName(wrapperClass));
}
}
@@ -1,444 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.handler;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.*;
import io.github.retrooper.packetevents.injector.GlobalChannelInjector;
import io.github.retrooper.packetevents.packettype.PacketType;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.login.in.handshake.WrappedPacketLoginInHandshake;
import io.github.retrooper.packetevents.packetwrappers.login.in.start.WrappedPacketLoginInStart;
import io.github.retrooper.packetevents.settings.PacketEventsSettings;
import io.github.retrooper.packetevents.utils.gameprofile.WrappedGameProfile;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.player.ClientVersion;
import io.github.retrooper.packetevents.utils.reflection.ClassUtil;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* Internal manager of everything related to packets.
* For example injecting, channel caching, internal processing, packet sending....
* This class is only meant to be used internally.
* This could end up being refactored or renamed without a deprecation as it is only meant to be used internally.
*
* @author retrooper
* @see <a href="http://netty.io">http://netty.io</a>
* @since 1.7.9
*/
public class PacketHandlerInternal {
public final GlobalChannelInjector injector = new GlobalChannelInjector();
public final Map<UUID, Long> keepAliveMap = new HashMap<>();
public final Map<String, Object> channelMap = new WeakHashMap<>();
public PacketHandlerInternal() {
injector.prepare();
}
/**
* Get a player's netty channel object with their name.
* This netty channel object is cached in a {@link ConcurrentHashMap} as the value and
* the name is the key.
* We cache the name and the channel quite early infact,
* once we receive the {@link PacketType.Login.Client#START} packet(which contains the game profile)
* and the game profile contains the player name.
* The wrapper for that packet is {@link WrappedPacketLoginInStart}.
* If PacketEvents couldn't cache(when you have {@link PacketEventsSettings#shouldInjectEarly()} set to false),
* PacketEvents will use some reflection to see if CraftBukkit has the netty channel.
* If you access this before the START login packet was sent, you will for sure experience issues.
*
* @param player Target player.
* @return Netty channel of a player by their name.
* @see <a href="https://wiki.vg/Protocol#Login_Start">https://wiki.vg/Protocol#Login_Start</a>
* @see Player#getName()
*/
public Object getChannel(Player player) {
String name = player.getName();
Object channel = channelMap.get(name);
if (channel == null) {
channel = NMSUtils.getChannel(player);
channelMap.put(name, channel);
}
return channel;
}
/**
* Inject a player.
* Executed synchronously or asynchronously depending on what you have the associated setting set to.
* Injecting a player is basically pairing the cached netty channel
* {@link #getChannel(Player)} to a bukkit player object.
* Bukkit initializes the {@link Player} object some time after
* we inject the netty channel, so we need to pair the two.
* PacketEvents already injects a player when the bukkit
* {@link org.bukkit.event.player.PlayerJoinEvent} or {@link org.bukkit.event.player.PlayerLoginEvent}
* is called, depending on your {@link PacketEventsSettings#shouldInjectEarly()} setting is set to.
*
* @param player Target bukkit player.
*/
public void injectPlayer(Player player) {
if (PacketEvents.get().getSettings().shouldInjectAsync()) {
injectPlayerAsync(player);
} else {
injectPlayerSync(player);
}
}
/**
* Eject a player.
* Executed synchronously or asynchronously depending on what you have the associated setting set to.
* Ejecting a player is basically unpairing the cached netty channel
* {@link #getChannel(Player)} from the bukkit player.
* Do this if you want to stop listening to a user's packets.
* PacketEvents already ejects a player when the bukkit
* {@link org.bukkit.event.player.PlayerQuitEvent} is called.
*
* @param player Target player.
*/
public void ejectPlayer(Player player) {
if (PacketEvents.get().getSettings().shouldEjectAsync()) {
ejectPlayerAsync(player);
} else {
ejectPlayerSync(player);
}
}
/**
* Synchronously inject a player.
*
* @param player Target player.
* @see #injectPlayer(Player)
*/
public void injectPlayerSync(Player player) {
PlayerInjectEvent injectEvent = new PlayerInjectEvent(player, false);
PacketEvents.get().getEventManager().callEvent(injectEvent);
if (!injectEvent.isCancelled()) {
injector.injectPlayerSync(player);
}
}
/**
* Asynchronously inject a player.
*
* @param player Target player.
* @see #injectPlayer(Player)
*/
public void injectPlayerAsync(Player player) {
PlayerInjectEvent injectEvent = new PlayerInjectEvent(player, true);
PacketEvents.get().getEventManager().callEvent(injectEvent);
if (!injectEvent.isCancelled()) {
injector.injectPlayerAsync(player);
}
}
/**
* Synchronously eject a player.
*
* @param player Target player.
* @see #ejectPlayer(Player)
*/
public void ejectPlayerSync(Player player) {
PlayerEjectEvent ejectEvent = new PlayerEjectEvent(player, false);
PacketEvents.get().getEventManager().callEvent(ejectEvent);
if (!ejectEvent.isCancelled()) {
injector.ejectPlayerSync(player);
keepAliveMap.remove(player.getUniqueId());
channelMap.remove(player.getName());
}
}
/**
* Asynchronously eject a player.
*
* @param player Target player.
* @see #ejectPlayer(Player)
*/
public void ejectPlayerAsync(Player player) {
PlayerEjectEvent ejectEvent = new PlayerEjectEvent(player, true);
PacketEvents.get().getEventManager().callEvent(ejectEvent);
if (!ejectEvent.isCancelled()) {
injector.ejectPlayerAsync(player);
}
}
/**
* Write and flush a packet to a netty channel.
* This netty channel is an object as 1.7.10 and 1.8 and above
* have different netty package locations.
*
* @param channel Netty channel.
* @param packet NMS Packet.
*/
public void sendPacket(Object channel, Object packet) {
injector.sendPacket(channel, packet);
}
/**
* Make PacketEvents process an incoming packet.
*
* @param player Packet sender.
* @param channel Packet sender's netty channel.
* @param packet NMS Packet.
* @return NMS Packet, null if the event was cancelled.
*/
public Object read(Player player, Object channel, Object packet) {
if (PacketTypeClasses.Login.Client.START.equals(packet.getClass())) {
WrappedPacketLoginInStart startWrapper = new WrappedPacketLoginInStart(new NMSPacket(packet));
WrappedGameProfile gameProfile = startWrapper.getGameProfile();
channelMap.put(gameProfile.name, channel);
}
if (player == null) {
String simpleClassName = ClassUtil.getClassSimpleName(packet.getClass());
//Status packet
if (simpleClassName.startsWith("PacketS")) {
final PacketStatusReceiveEvent event = new PacketStatusReceiveEvent(channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptStatusReceive(event);
if (event.isCancelled()) {
packet = null;
}
} else {
//Login packet
final PacketLoginReceiveEvent event = new PacketLoginReceiveEvent(channel, new NMSPacket(packet));//Cache the channel
//f (event.getPacketId() == PacketType.Login.Client.START) {
// WrappedPacketLoginInStart startWrapper = new WrappedPacketLoginInStart(event.getNMSPacket());
//WrappedGameProfile gameProfile = startWrapper.getGameProfile();
//channelMap.put(gameProfile.name, channel);
//}
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptLoginReceive(event);
if (event.isCancelled()) {
packet = null;
}
}
} else {
final PacketPlayReceiveEvent event = new PacketPlayReceiveEvent(player, channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptRead(event);
if (event.isCancelled()) {
packet = null;
}
}
return packet;
}
/**
* Make PacketEvents process an outgoing packet.
* The NMS Packet will be null when netty should cancel a packet.
*
* @param player Packet receiver.
* @param channel Packet receiver's netty channel.
* @param packet NMS Packet.
* @return NMS Packet, null if the event was cancelled.
*/
public Object write(Player player, Object channel, Object packet) {
if (player == null) {
String simpleClassName = ClassUtil.getClassSimpleName(packet.getClass());
//Status packet
if (simpleClassName.startsWith("PacketS")) {
final PacketStatusSendEvent event = new PacketStatusSendEvent(channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptStatusSend(event);
if (event.isCancelled()) {
packet = null;
}
}
//Login packet
else {
final PacketLoginSendEvent event = new PacketLoginSendEvent(channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptLoginSend(event);
if (event.isCancelled()) {
packet = null;
}
}
} else {
final PacketPlaySendEvent event = new PacketPlaySendEvent(player, channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
packet = event.getNMSPacket().getRawNMSPacket();
interceptWrite(event);
if (event.isCancelled()) {
packet = null;
}
}
return packet;
}
/**
* Make PacketEvents process an incoming PLAY packet after minecraft has processed it.
* As minecraft has already processed the packet, we cannot cancel the action, nor the event.
*
* @param player Packet sender.
* @param channel Netty channel of the packet sender.
* @param packet NMS Packet.
*/
public void postRead(Player player, Object channel, Object packet) {
if (player != null) {
//Since player != null check is done, status and login packets won't come passed this point.
PostPacketPlayReceiveEvent event = new PostPacketPlayReceiveEvent(player, channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
interceptPostPlayReceive(event);
}
}
/**
* Make PacketEvents process an outgoing PLAY packet after minecraft has already sent the packet.
* This doesn't necessarily mean the client already received the packet,
* but the server has sent it for sure by this time.
* As minecraft has already processed the packet, we cannot cancel the action, nor the event.
*
* @param player Packet receiver.
* @param channel Netty channel of the packet receiver.
* @param packet NMS Packet.
*/
public void postWrite(Player player, Object channel, Object packet) {
if (player != null) {
//Since player != null check is done, status and login packets won't come passed this point.
PostPacketPlaySendEvent event = new PostPacketPlaySendEvent(player, channel, new NMSPacket(packet));
PacketEvents.get().getEventManager().callEvent(event);
interceptPostPlaySend(event);
}
}
/**
* Internal processing of an incoming PLAY packet.
*
* @param event PLAY server-bound packet event.
*/
private void interceptRead(PacketPlayReceiveEvent event) {
if (event.getPacketId() == PacketType.Play.Client.KEEP_ALIVE) {
UUID uuid = event.getPlayer().getUniqueId();
long timestamp = keepAliveMap.getOrDefault(uuid, event.getTimestamp());
long currentTime = event.getTimestamp();
long ping = currentTime - timestamp;
long smoothedPing = (PacketEvents.get().getPlayerUtils().getSmoothedPing(event.getPlayer().getUniqueId()) * 3L + ping) / 4;
PacketEvents.get().getPlayerUtils().playerPingMap.put(uuid, (int) ping);
PacketEvents.get().getPlayerUtils().playerSmoothedPingMap.put(uuid, (int) smoothedPing);
}
}
/**
* Internal processing of an outgoing PLAY packet.
*
* @param event PLAY client-bound packet event.
*/
private void interceptWrite(PacketPlaySendEvent event) {
}
/**
* Internal processing of an incoming LOGIN packet.
*
* @param event LOGIN server-bound packet event.
*/
private void interceptLoginReceive(PacketLoginReceiveEvent event) {
if (event.getPacketId() == PacketType.Login.Client.HANDSHAKE) {
WrappedPacketLoginInHandshake handshake = new WrappedPacketLoginInHandshake(event.getNMSPacket());
int protocolVersion = handshake.getProtocolVersion();
ClientVersion version = ClientVersion.getClientVersion(protocolVersion);
PacketEvents.get().getPlayerUtils().tempClientVersionMap.put(event.getSocketAddress(), version);
}
}
/**
* Internal processing of an outgoing LOGIN packet.
*
* @param event client-bound LOGIN packet event.
*/
private void interceptLoginSend(PacketLoginSendEvent event) {
}
/**
* Internal processing of an incoming STATUS packet.
*
* @param event server-bound STATUS packet event.
*/
private void interceptStatusReceive(PacketStatusReceiveEvent event) {
}
/**
* Internal processing of an outgoing STATUS packet.
*
* @param event client-bound STATUS packet event.
*/
private void interceptStatusSend(PacketStatusSendEvent event) {
}
/**
* Internal processing of a packet that has already been processed by minecraft.
*
* @param event post server-bound play packet event.
*/
private void interceptPostPlayReceive(PostPacketPlayReceiveEvent event) {
}
/**
* Internal processing of a packet that has already been sent to a client.
* Doesn't necessarily mean the client has received it yet.
*
* @param event post client-bound play packet event.
*/
private void interceptPostPlaySend(PostPacketPlaySendEvent event) {
if (event.getPacketId() == PacketType.Play.Server.KEEP_ALIVE) {
if (event.getPlayer() != null) {
keepAliveMap.put(event.getPlayer().getUniqueId(), event.getTimestamp());
}
}
}
/**
* If you are using the EarlyInjector,
* calling this close method will unregister the channel handlers it registered when the plugin enabled.
* PacketEvents already unregisters them in the {@link PacketEvents#terminate()} method.
*/
public void cleanup() {
injector.cleanup();
}
/**
* If you are using the EarlyInjector,
* calling this close method will unregister the channel handlers it registered when the plugin enabled
* ASYNCHRONOUSLY.
* PacketEvents already unregisters them in the {@link PacketEvents#terminate()} method.
*/
public void cleanupAsync() {
injector.cleanupAsync();
}
}
@@ -1,33 +1,17 @@
package io.github.retrooper.packetevents.injector;
import io.github.retrooper.packetevents.PacketEvents;
import org.bukkit.entity.Player;
public interface ChannelInjector {
default void prepare() {
void inject();
}
void eject();
default void cleanup() {
void injectPlayer(Player player);
}
void ejectPlayer(Player player);
default void cleanupAsync() {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
cleanup();
}
});
}
void injectPlayerSync(Player player);
void ejectPlayerSync(Player player);
void injectPlayerAsync(Player player);
void ejectPlayerAsync(Player player);
boolean hasInjected(Player player);
void sendPacket(Object channel, Object rawNMSPacket);
}
@@ -1,65 +1,66 @@
package io.github.retrooper.packetevents.injector;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.injector.earlyinjector.EarlyChannelInjector7;
import io.github.retrooper.packetevents.injector.earlyinjector.EarlyChannelInjector8;
import io.github.retrooper.packetevents.injector.lateinjector.LateChannelInjector7;
import io.github.retrooper.packetevents.injector.lateinjector.LateChannelInjector8;
import io.github.retrooper.packetevents.event.impl.PlayerEjectEvent;
import io.github.retrooper.packetevents.event.impl.PlayerInjectEvent;
import io.github.retrooper.packetevents.injector.earlyinjector.legacy.EarlyChannelInjectorLegacy;
import io.github.retrooper.packetevents.injector.earlyinjector.modern.EarlyChannelInjector;
import io.github.retrooper.packetevents.injector.lateinjector.legacy.LateChannelInjectorLegacy;
import io.github.retrooper.packetevents.injector.lateinjector.modern.LateChannelInjector;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import org.bukkit.entity.Player;
public class GlobalChannelInjector implements ChannelInjector {
private final ChannelInjector injector;
private ChannelInjector injector;
public GlobalChannelInjector() {
}
public void load() {
boolean legacy = NMSUtils.legacyNettyImportMode;
//Early injector
if (PacketEvents.get().getSettings().shouldInjectEarly()) {
injector = legacy ? new EarlyChannelInjector7() : new EarlyChannelInjector8();
}
//Late injector
else {
injector = legacy ? new LateChannelInjector7() : new LateChannelInjector8();
if (!PacketEvents.get().getSettings().shouldUseCompatibilityInjector()) {
injector = legacy ? new EarlyChannelInjectorLegacy() : new EarlyChannelInjector();
} else {
injector = legacy ? new LateChannelInjectorLegacy() : new LateChannelInjector();
}
}
@Override
public void prepare() {
injector.prepare();
public void inject() {
injector.inject();
}
@Override
public void cleanup() {
injector.cleanup();
public void eject() {
injector.eject();
}
@Override
public void cleanupAsync() {
injector.cleanupAsync();
public void injectPlayer(Player player) {
PlayerInjectEvent injectEvent = new PlayerInjectEvent(player);
PacketEvents.get().callEvent(injectEvent);
if (!injectEvent.isCancelled()) {
injector.injectPlayer(player);
}
}
@Override
public void injectPlayerSync(Player player) {
injector.injectPlayerSync(player);
public void ejectPlayer(Player player) {
PlayerEjectEvent ejectEvent = new PlayerEjectEvent(player);
PacketEvents.get().callEvent(ejectEvent);
if (!ejectEvent.isCancelled()) {
injector.ejectPlayer(player);
}
}
@Override
public void ejectPlayerSync(Player player) {
injector.ejectPlayerSync(player);
public boolean hasInjected(Player player) {
return injector.hasInjected(player);
}
@Override
public void injectPlayerAsync(Player player) {
injector.injectPlayerAsync(player);
}
@Override
public void ejectPlayerAsync(Player player) {
injector.ejectPlayerAsync(player);
}
@Override
public void sendPacket(Object channel, Object rawNMSPacket) {
injector.sendPacket(channel, rawNMSPacket);
public void sendPacket(Object ch, Object rawNMSPacket) {
injector.sendPacket(ch, rawNMSPacket);
}
}
@@ -1,337 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import net.minecraft.util.io.netty.channel.*;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* 1.7.10 Spigot Early channel injector.
* This is an early injector for 1.7.10 spigot servers.
* The netty import on spigot changed since 1.8.
* So we needed to make a new class using the older import location to support 1.7.10.
* This is why we also commonly use a java {@link Object} class when it comes to netty channels
* in some classes to maintain support for both netty imports.
*
* @author retrooper
* @since 1.8
*/
public class EarlyChannelInjector7 implements EarlyInjector {
/**
* Netty minecraft server channels.
*/
private final List<Channel> serverChannels = new ArrayList<>();
/**
* First channel initializer.
* Used to inject incoming channels.
*/
private ChannelInitializer<Channel> firstChannelInitializer;
/**
* Second channel initializer.
*/
private ChannelInitializer<Channel> secondChannelInitializer;
/**
* Channel handler.
*/
private ChannelInboundHandlerAdapter channelHandler;
/**
* Network managers.
*/
private List<Object> networkMarkers;
/**
* Access minecraft's network managers to synchronize against as they interfere with our channel injection.
* Initiate the channel initializers and the channel handler.
* Access minecraft's server channels and add our own.
*/
@Override
public void prepare() {
networkMarkers = NMSUtils.getNetworkMarkers();
firstChannelInitializer = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(final Channel channel) {
if (networkMarkers != null) {
synchronized (networkMarkers) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (PacketEvents.get().getServerUtils().getVersion().isNewerThan(ServerVersion.v_1_11_2)) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
injectChannel(channel);
}
});
} else {
injectChannel(channel);
}
}
});
}
} else {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (PacketEvents.get().getServerUtils().getVersion().isNewerThan(ServerVersion.v_1_11_2)) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
injectChannel(channel);
}
});
} else {
injectChannel(channel);
}
}
});
}
}
};
secondChannelInitializer = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
channel.pipeline().addLast(firstChannelInitializer);
}
};
channelHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel channel = (Channel) msg;
channel.pipeline().addFirst(secondChannelInitializer);
ctx.fireChannelRead(msg);
}
};
//Wrapper for the minecraft server connection.
WrappedPacket serverConnectionWrapper = new WrappedPacket(new NMSPacket(NMSUtils.getMinecraftServerConnection()));
boolean searching = true;
for (int i = 0; searching; i++) {
try {
//Get the list.
List<Object> serverChannelList = (List<Object>) serverConnectionWrapper.readObject(i, List.class);
for (Object serverChannel : serverChannelList) {
//Is this the server channel list?
if (serverChannel instanceof ChannelFuture) {
//Yes it is...
Channel channel = ((ChannelFuture) serverChannel).channel();
serverChannels.add(channel);
//Add our channel handler to all the server channel pipelines.
channel.pipeline().addFirst(channelHandler);
searching = false;
}
}
} catch (Exception ex) {
break;
}
}
}
/**
* Remove our channel handler from all server channel pipelines from the current thread.
*/
@Override
public void cleanup() {
for (Channel channel : serverChannels) {
try {
channel.pipeline().remove(channelHandler);
} catch (NoSuchElementException ignored) {
}
}
}
/**
* Inject a netty channel to listen to packets.
* If already injected, get the channel interceptor.
*
* @param ch Netty channel.
* @return {@link PlayerChannelInterceptor}
*/
@Override
public PlayerChannelInterceptor injectChannel(Object ch) {
Channel channel = (Channel) ch;
String handlerName = PacketEvents.handlerName;
PlayerChannelInterceptor interceptor = (PlayerChannelInterceptor) channel.pipeline().get(handlerName);
if (interceptor == null) {
interceptor = new PlayerChannelInterceptor();
if (channel.pipeline().get("packet_handler") != null) {
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
} else {
throw new IllegalStateException("Failed to inject an incoming channel due to \"packet_handler\" not being added to the pipeline! Let them rejoin!");
}
}
return interceptor;
}
/**
* Eject a netty channel.
*
* @param ch Netty channel.
*/
@Override
public void ejectChannel(Object ch) {
Channel channel = (Channel) ch;
String handlerName = PacketEvents.handlerName;
if (channel.pipeline().get(handlerName) != null) {
channel.pipeline().remove(handlerName);
}
}
/**
* Inject a player on the current thread.
*
* @param player Target player.
*/
@Override
public void injectPlayerSync(Player player) {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
injectChannel(channel).player = player;
}
/**
* Eject a player on the current thread.
*
* @param player Target player.
*/
@Override
public void ejectPlayerSync(Player player) {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
ejectChannel(channel);
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
/**
* Inject a player on our custom inject and eject (fixed) thread pool (=asynchronously).
*
* @param player Target player.
*/
@Override
public void injectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
injectChannel(channel).player = player;
}
});
}
/**
* Eject a player on our custom inject and eject (fixed) thread pool (=asynchronously).
*
* @param player Target player.
*/
@Override
public void ejectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
ejectChannel(channel);
PacketEvents.get().packetHandlerInternal.keepAliveMap.remove(player.getUniqueId());
PacketEvents.get().packetHandlerInternal.channelMap.remove(player.getName());
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
});
}
/**
* Send a raw NMS packet to a netty channel.
*
* @param ch Netty channel.
* @param packet Raw NMS Packet.
*/
@Override
public void sendPacket(Object ch, Object packet) {
Channel channel = (Channel) ch;
channel.writeAndFlush(packet);
}
/**
* Player channel interceptor.
*
* @author retrooper
* @since 1.8
*/
public static class PlayerChannelInterceptor extends ChannelDuplexHandler {
/**
* Associated player.
* This is null until you inject the player.
* (We inject the PLAYER by PlayerLoginEvent or PlayerJoinEvent depending on your settings)
*/
public volatile Player player;
/**
* Incoming packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet.
* @throws Exception Possible exception.
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetHandlerInternal.postRead(player, ctx.channel(), packet);
}
}
/**
* Outgoing packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet
* @param promise Netty channel promise.
* @throws Exception Possible exception.
*/
@Override
public void write(final ChannelHandlerContext ctx, Object packet, final ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetHandlerInternal.postWrite(player, ctx.channel(), packet);
}
}
}
}
@@ -1,332 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.netty.channel.*;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* 1.8 (and above) Spigot Early channel injector.
* This is an early injector for 1.8 (and above) spigot servers.
* The netty import on spigot changed since 1.8.
* So we needed to make separate classes for the 1.7.10 and the 1.8+ injector.
* This is why we also commonly use a java {@link Object} class when it comes to netty channels
* in some classes to maintain support for both netty imports.
*
* @author retrooper
* @since 1.8
*/
public class EarlyChannelInjector8 implements EarlyInjector {
/**
* Netty minecraft server channels.
*/
private final List<Channel> serverChannels = new ArrayList<>();
/**
* First channel initializer.
* Used to inject incoming channels.
*/
private ChannelInitializer<Channel> firstChannelInitializer;
/**
* Second channel initializer.
*/
private ChannelInitializer<Channel> secondChannelInitializer;
/**
* Channel handler.
*/
private ChannelInboundHandlerAdapter channelHandler;
/**
* Network managers.
*/
private List<Object> networkMarkers;
/**
* Access minecraft's network managers to synchronize against as they interfere with our channel injection.
* Initiate the channel initializers and the channel handler.
* Access minecraft's server channels and add our own.
*/
@Override
public void prepare() {
networkMarkers = NMSUtils.getNetworkMarkers();
firstChannelInitializer = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(final Channel channel) {
if (networkMarkers != null) {
synchronized (networkMarkers) {
if (PacketEvents.get().getServerUtils().getVersion().isNewerThan(ServerVersion.v_1_11_2)) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
injectChannel(channel);
}
});
} else {
injectChannel(channel);
}
}
} else {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (PacketEvents.get().getServerUtils().getVersion().isNewerThan(ServerVersion.v_1_11_2)) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
injectChannel(channel);
}
});
} else {
injectChannel(channel);
}
}
});
}
}
};
secondChannelInitializer = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
channel.pipeline().addLast(firstChannelInitializer);
}
};
channelHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel channel = (Channel) msg;
channel.pipeline().addFirst(secondChannelInitializer);
ctx.fireChannelRead(msg);
}
};
//Wrapper for the minecraft server connection.
WrappedPacket serverConnectionWrapper = new WrappedPacket(new NMSPacket(NMSUtils.getMinecraftServerConnection()));
boolean searching = true;
for (int i = 0; searching; i++) {
try {
//Get the list.
List<Object> serverChannelList = (List<Object>) serverConnectionWrapper.readObject(i, List.class);
for (Object serverChannel : serverChannelList) {
//Is this the server channel list?
if (serverChannel instanceof ChannelFuture) {
//Yes it is...
Channel channel = ((ChannelFuture) serverChannel).channel();
serverChannels.add(channel);
//Add our channel handler to all server channel pipelines.
channel.pipeline().addFirst(channelHandler);
searching = false;
}
}
} catch (Exception ex) {
break;
}
}
}
/**
* Remove our channel handler from all server channel pipelines from the current thread.
*/
@Override
public void cleanup() {
for (Channel channel : serverChannels) {
try {
channel.pipeline().remove(channelHandler);
} catch (NoSuchElementException ignored) {
}
}
}
/**
* Inject a netty channel to listen to packets.
* If already injected, get the channel interceptor.
*
* @param ch Netty channel.
* @return {@link PlayerChannelInterceptor}
*/
@Override
public PlayerChannelInterceptor injectChannel(Object ch) {
Channel channel = (Channel) ch;
final String handlerName = PacketEvents.handlerName;
try {
PlayerChannelInterceptor interceptor = (PlayerChannelInterceptor) channel.pipeline().get(handlerName);
if (interceptor == null) {
interceptor = new PlayerChannelInterceptor();
if (channel.pipeline().get("packet_handler") != null) {
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
}
}
return interceptor;
} catch (IllegalArgumentException ex) {
//Let us try again
return (PlayerChannelInterceptor) channel.pipeline().get(handlerName);
}
}
/**
* Eject a netty channel.
*
* @param ch Netty channel.
*/
@Override
public void ejectChannel(Object ch) {
Channel channel = (Channel) ch;
String handlerName = PacketEvents.handlerName;
if (channel.pipeline().get(handlerName) != null) {
channel.pipeline().remove(handlerName);
}
}
/**
* Inject a player on the current thread.
*
* @param player Target player.
*/
@Override
public void injectPlayerSync(Player player) {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
injectChannel(channel).player = player;
}
/**
* Eject a player on the current thread.
*
* @param player Target player.
*/
@Override
public void ejectPlayerSync(Player player) {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
ejectChannel(channel);
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
/**
* Inject a player on our custom inject and eject (fixed) thread pool (=asynchronously).
*
* @param player Target player.
*/
@Override
public void injectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
injectChannel(channel).player = player;
}
});
}
/**
* Eject a player on our custom inject and eject (fixed) thread pool (=asynchronously).
*
* @param player Target player.
*/
@Override
public void ejectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
ejectChannel(channel);
PacketEvents.get().packetHandlerInternal.keepAliveMap.remove(player.getUniqueId());
PacketEvents.get().packetHandlerInternal.channelMap.remove(player.getName());
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
});
}
/**
* Send a raw NMS packet to a netty channel.
*
* @param ch Netty channel.
* @param packet Raw NMS Packet.
*/
@Override
public void sendPacket(Object ch, Object packet) {
Channel channel = (Channel) ch;
channel.writeAndFlush(packet);
}
/**
* Player channel interceptor.
*
* @author retrooper
* @since 1.8
*/
public static class PlayerChannelInterceptor extends ChannelDuplexHandler {
/**
* Associated player.
* This is null until you inject the player.
* (We inject the PLAYER by PlayerLoginEvent or PlayerJoinEvent depending on your settings)
*/
public volatile Player player;
/**
* Incoming packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet.
* @throws Exception Possible exception.
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetHandlerInternal.postRead(player, ctx.channel(), packet);
}
}
/**
* Outgoing packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet
* @param promise Netty channel promise.
* @throws Exception Possible exception.
*/
@Override
public void write(final ChannelHandlerContext ctx, Object packet, final ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetHandlerInternal.postWrite(player, ctx.channel(), packet);
}
}
}
}
@@ -1,13 +1,34 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector;
import io.github.retrooper.packetevents.injector.ChannelInjector;
import org.bukkit.entity.Player;
public interface EarlyInjector extends ChannelInjector {
void prepare();
void updatePlayerObject(Player player, Object channel);
void cleanup();
Object injectChannel(Object channel);
void ejectChannel(Object channel);
boolean isBound();
}
@@ -0,0 +1,316 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.legacy;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent;
import io.github.retrooper.packetevents.injector.earlyinjector.EarlyInjector;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.list.ListWrapper;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelFuture;
import net.minecraft.util.io.netty.channel.ChannelHandler;
import net.minecraft.util.io.netty.channel.ChannelInitializer;
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Early channel injector for 1.7.10 server versions.
* Thank you ViaVersion.
* https://github.com/ViaVersion/ViaVersion/tree/master/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java
*
* @author retrooper, Thomazz, ViaVersion
* @since 1.8
*/
public class EarlyChannelInjectorLegacy implements EarlyInjector {
private final List<ChannelFuture> injectedFutures = new ArrayList<>();
private final List<Map<Field, Object>> injectedLists = new ArrayList<>();
@Override
public void inject() {
try {
Object serverConnection = NMSUtils.getMinecraftServerConnection();
for (Field field : serverConnection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = null;
try {
value = field.get(serverConnection);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (value instanceof List) {
//Get the list.
List listWrapper = new ListWrapper((List) value) {
@Override
public void processAdd(Object o) {
if (o instanceof ChannelFuture) {
try {
injectChannelFuture((ChannelFuture) o);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
HashMap<Field, Object> map = new HashMap<>();
map.put(field, serverConnection);
injectedLists.add(map);
field.set(serverConnection, listWrapper);
synchronized (listWrapper) {
for (Object serverChannel : (List) value) {
//Is this the server channel future list?
if (serverChannel instanceof ChannelFuture) {
//Yes it is...
injectChannelFuture((ChannelFuture) serverChannel);
} else {
break;//Wrong list
}
}
}
}
}
} catch (Exception ex) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to inject!");
ex.printStackTrace();
}
//Player channels might have been registered already. Let us add our handlers. We are a little late though.
//This only happens when you join extremely early on older versions of minecraft.
List<Object> networkManagers = NMSUtils.getNetworkManagers();
synchronized (networkManagers) {
for (Object networkManager : networkManagers) {
WrappedPacket networkManagerWrapper = new WrappedPacket(new NMSPacket(networkManager));
Channel channel = (Channel) networkManagerWrapper.readObject(0, NMSUtils.nettyChannelClass);
if (channel == null) {
continue;
}
if (channel.pipeline().get("packet_handler") != null) {
channel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, new PlayerChannelHandlerLegacy());
}
}
}
}
private void injectChannelFuture(ChannelFuture channelFuture) {
List<String> channelHandlerNames = channelFuture.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
Field bootstrapAcceptorField = null;
for (String handlerName : channelHandlerNames) {
ChannelHandler handler = channelFuture.channel().pipeline().get(handlerName);
try {
bootstrapAcceptorField = handler.getClass().getDeclaredField("childHandler");
bootstrapAcceptorField.setAccessible(true);
bootstrapAcceptorField.get(handler);
bootstrapAcceptor = handler;
} catch (Exception ex) {
}
}
if (bootstrapAcceptor == null) {
bootstrapAcceptor = channelFuture.channel().pipeline().first();
}
ChannelInitializer<?> oldChannelInitializer = null;
try {
oldChannelInitializer = (ChannelInitializer<?>) bootstrapAcceptorField.get(bootstrapAcceptor);
ChannelInitializer<?> channelInitializer = new PEChannelInitializerLegacy(oldChannelInitializer);
Reflection.getFieldWithoutFinalModifier(bootstrapAcceptorField);
//Replace the old channel initializer with our own.
bootstrapAcceptorField.set(bootstrapAcceptor, channelInitializer);
injectedFutures.add(channelFuture);
} catch (IllegalAccessException e) {
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = null;
try {
yaml = (PluginDescriptionFile) PluginDescriptionFile.class.getDeclaredField("description").get(cl);
} catch (IllegalAccessException | NoSuchFieldException e2) {
e2.printStackTrace();
}
System.err.println("PacketEvents failed to inject, because of " + bootstrapAcceptor.getClass().getName() + ", you might want to try running without " + yaml.getName() + "?");
} else {
System.err.println("PacketEvents failed to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
}
@Override
public void eject() {
Field childHandlerField = null;
for (ChannelFuture future : injectedFutures) {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
try {
ChannelHandler handler = future.channel().pipeline().get(name);
if (childHandlerField == null) {
childHandlerField = handler.getClass().getDeclaredField("childHandler");
childHandlerField.setAccessible(true);
}
ChannelInitializer<SocketChannel> oldInit = (ChannelInitializer<SocketChannel>) childHandlerField.get(handler);
if (oldInit instanceof PEChannelInitializerLegacy) {
bootstrapAcceptor = handler;
}
} catch (Exception e) {
// Not this one
}
}
// Default to first
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
Reflection.getFieldWithoutFinalModifier(childHandlerField);
ChannelInitializer<SocketChannel> oldInit = (ChannelInitializer<SocketChannel>) childHandlerField.get(bootstrapAcceptor);
if (oldInit instanceof PEChannelInitializerLegacy) {
childHandlerField.set(bootstrapAcceptor, oldInit);
childHandlerField.set(bootstrapAcceptor, ((PEChannelInitializerLegacy) oldInit).getOldChannelInitializer());
}
} catch (Exception e) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to eject the injection handler! Please reboot!");
}
}
injectedFutures.clear();
for (Map<Field, Object> map : injectedLists) {
try {
for (Field key : map.keySet()) {
key.setAccessible(true);
Object o = map.get(key);
if (o instanceof ListWrapper) {
key.set(o, ((ListWrapper) o).getOriginalList());
}
}
} catch (IllegalAccessException e) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to eject the injection handler! Please reboot!!");
}
}
injectedLists.clear();
}
@Override
public void injectPlayer(Player player) {
Object channel = PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel != null) {
updatePlayerObject(player, channel);
}
}
@Override
public void ejectPlayer(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel != null) {
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
channel.pipeline().remove(PacketEvents.handlerName);
}
}
}
@Override
public boolean hasInjected(Player player) {
Object channel = PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel == null) {
return false;
}
PlayerChannelHandlerLegacy handler = getHandler(channel);
return handler != null && handler.player != null;
}
@Override
public void sendPacket(Object ch, Object rawNMSPacket) {
Channel channel = (Channel) ch;
channel.writeAndFlush(rawNMSPacket);
}
private PlayerChannelHandlerLegacy getHandler(Object rawChannel) {
Channel channel = (Channel) rawChannel;
ChannelHandler handler = channel.pipeline().get(PacketEvents.handlerName);
if (handler instanceof PlayerChannelHandlerLegacy) {
return (PlayerChannelHandlerLegacy) handler;
} else {
return null;
}
}
@Override
public void updatePlayerObject(Player player, Object rawChannel) {
PlayerChannelHandlerLegacy handler = getHandler(rawChannel);
if (handler != null) {
handler.player = player;
}
}
@Override
public boolean isBound() {
try {
Object connection = NMSUtils.getMinecraftServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return false;
}
}
@@ -0,0 +1,59 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.legacy;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import net.minecraft.util.io.netty.channel.ChannelInitializer;
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
import java.lang.reflect.Method;
public class PEChannelInitializerLegacy extends ChannelInitializer<SocketChannel> {
private final ChannelInitializer<?> oldChannelInitializer;
private Method initChannelMethod;
public PEChannelInitializerLegacy(ChannelInitializer<?> oldChannelInitializer) {
this.oldChannelInitializer = oldChannelInitializer;
load();
}
private void load() {
initChannelMethod = Reflection.getMethod(oldChannelInitializer.getClass(), "initChannel", 0);
}
public ChannelInitializer<?> getOldChannelInitializer() {
return oldChannelInitializer;
}
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
initChannelMethod.invoke(oldChannelInitializer, socketChannel);
PlayerChannelHandlerLegacy channelHandler = new PlayerChannelHandlerLegacy();
if (socketChannel.pipeline().get("packet_handler") != null) {
socketChannel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, channelHandler);
}
}
}
@@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.legacy;
import io.github.retrooper.packetevents.PacketEvents;
import net.minecraft.util.io.netty.channel.ChannelDuplexHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import org.bukkit.entity.Player;
public class PlayerChannelHandlerLegacy extends ChannelDuplexHandler {
/**
* Associated player.
* This is null until the player is injected.
*/
public volatile Player player;
/**
* Incoming packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet.
* @throws Exception Possible exception.
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetProcessorInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetProcessorInternal.postRead(player, ctx.channel(), packet);
}
}
/**
* Outgoing packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet
* @param promise Netty channel promise.
* @throws Exception Possible exception.
*/
@Override
public void write(final ChannelHandlerContext ctx, Object packet, final ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetProcessorInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetProcessorInternal.postWrite(player, ctx.channel(), packet);
}
}
}
@@ -0,0 +1,316 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.modern;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent;
import io.github.retrooper.packetevents.injector.earlyinjector.EarlyInjector;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.list.ListWrapper;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Early channel injector for all server versions higher than 1.7.10.
* Thank you ViaVersion.
* https://github.com/ViaVersion/ViaVersion/tree/master/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java
*
* @author retrooper, Thomazz, ViaVersion
* @since 1.8
*/
public class EarlyChannelInjector implements EarlyInjector {
private final List<ChannelFuture> injectedFutures = new ArrayList<>();
private final List<Map<Field, Object>> injectedLists = new ArrayList<>();
@Override
public void inject() {
try {
Object serverConnection = NMSUtils.getMinecraftServerConnection();
for (Field field : serverConnection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = null;
try {
value = field.get(serverConnection);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (value instanceof List) {
//Get the list.
List listWrapper = new ListWrapper((List) value) {
@Override
public void processAdd(Object o) {
if (o instanceof ChannelFuture) {
try {
injectChannelFuture((ChannelFuture) o);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
HashMap<Field, Object> map = new HashMap<>();
map.put(field, serverConnection);
injectedLists.add(map);
field.set(serverConnection, listWrapper);
synchronized (listWrapper) {
for (Object serverChannel : (List) value) {
//Is this the server channel future list?
if (serverChannel instanceof ChannelFuture) {
//Yes it is...
injectChannelFuture((ChannelFuture) serverChannel);
} else {
break;//Wrong list
}
}
}
}
}
} catch (Exception ex) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to inject!");
ex.printStackTrace();
}
//Player channels might have been registered already. Let us add our handlers. We are a little late though.
//This only happens when you join extremely early on older versions of minecraft.
List<Object> networkManagers = NMSUtils.getNetworkManagers();
synchronized (networkManagers) {
for (Object networkManager : networkManagers) {
WrappedPacket networkManagerWrapper = new WrappedPacket(new NMSPacket(networkManager));
Channel channel = (Channel) networkManagerWrapper.readObject(0, NMSUtils.nettyChannelClass);
if (channel == null) {
continue;
}
if (channel.pipeline().get("packet_handler") != null) {
channel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, new PlayerChannelHandler());
}
}
}
}
private void injectChannelFuture(ChannelFuture channelFuture) {
List<String> channelHandlerNames = channelFuture.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
Field bootstrapAcceptorField = null;
for (String handlerName : channelHandlerNames) {
ChannelHandler handler = channelFuture.channel().pipeline().get(handlerName);
try {
bootstrapAcceptorField = handler.getClass().getDeclaredField("childHandler");
bootstrapAcceptorField.setAccessible(true);
bootstrapAcceptorField.get(handler);
bootstrapAcceptor = handler;
} catch (Exception ex) {
}
}
if (bootstrapAcceptor == null) {
bootstrapAcceptor = channelFuture.channel().pipeline().first();
}
ChannelInitializer<?> oldChannelInitializer = null;
try {
oldChannelInitializer = (ChannelInitializer<?>) bootstrapAcceptorField.get(bootstrapAcceptor);
ChannelInitializer<?> channelInitializer = new PEChannelInitializer(oldChannelInitializer);
Reflection.getFieldWithoutFinalModifier(bootstrapAcceptorField);
//Replace the old channel initializer with our own.
bootstrapAcceptorField.set(bootstrapAcceptor, channelInitializer);
injectedFutures.add(channelFuture);
} catch (IllegalAccessException e) {
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = null;
try {
yaml = (PluginDescriptionFile) PluginDescriptionFile.class.getDeclaredField("description").get(cl);
} catch (IllegalAccessException | NoSuchFieldException e2) {
e2.printStackTrace();
}
System.err.println("PacketEvents failed to inject, because of " + bootstrapAcceptor.getClass().getName() + ", you might want to try running without " + yaml.getName() + "?");
} else {
System.err.println("PacketEvents failed to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
}
@Override
public void eject() {
Field childHandlerField = null;
for (ChannelFuture future : injectedFutures) {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
try {
ChannelHandler handler = future.channel().pipeline().get(name);
if (childHandlerField == null) {
childHandlerField = handler.getClass().getDeclaredField("childHandler");
childHandlerField.setAccessible(true);
}
ChannelInitializer<SocketChannel> oldInit = (ChannelInitializer<SocketChannel>) childHandlerField.get(handler);
if (oldInit instanceof PEChannelInitializer) {
bootstrapAcceptor = handler;
}
} catch (Exception e) {
// Not this one
}
}
// Default to first
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
Reflection.getFieldWithoutFinalModifier(childHandlerField);
ChannelInitializer<SocketChannel> oldInit = (ChannelInitializer<SocketChannel>) childHandlerField.get(bootstrapAcceptor);
if (oldInit instanceof PEChannelInitializer) {
childHandlerField.set(bootstrapAcceptor, oldInit);
childHandlerField.set(bootstrapAcceptor, ((PEChannelInitializer) oldInit).getOldChannelInitializer());
}
} catch (Exception e) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to eject the injection handler! Please reboot!");
}
}
injectedFutures.clear();
for (Map<Field, Object> map : injectedLists) {
try {
for (Field key : map.keySet()) {
key.setAccessible(true);
Object o = map.get(key);
if (o instanceof ListWrapper) {
key.set(o, ((ListWrapper) o).getOriginalList());
}
}
} catch (IllegalAccessException e) {
PacketEvents.get().getPlugin().getLogger().severe("PacketEvents failed to eject the injection handler! Please reboot!!");
}
}
injectedLists.clear();
}
@Override
public void injectPlayer(Player player) {
Object channel = PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel != null) {
updatePlayerObject(player, channel);
}
}
@Override
public void ejectPlayer(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel != null) {
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
channel.pipeline().remove(PacketEvents.handlerName);
}
}
}
@Override
public boolean hasInjected(Player player) {
Object channel = PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel == null) {
return false;
}
PlayerChannelHandler handler = getHandler(channel);
return handler != null && handler.player != null;
}
@Override
public void sendPacket(Object ch, Object rawNMSPacket) {
Channel channel = (Channel) ch;
channel.writeAndFlush(rawNMSPacket);
}
private PlayerChannelHandler getHandler(Object rawChannel) {
Channel channel = (Channel) rawChannel;
ChannelHandler handler = channel.pipeline().get(PacketEvents.handlerName);
if (handler instanceof PlayerChannelHandler) {
return (PlayerChannelHandler) handler;
} else {
return null;
}
}
@Override
public void updatePlayerObject(Player player, Object rawChannel) {
PlayerChannelHandler handler = getHandler(rawChannel);
if (handler != null) {
handler.player = player;
}
}
@Override
public boolean isBound() {
try {
Object connection = NMSUtils.getMinecraftServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return false;
}
}
@@ -0,0 +1,59 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.modern;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import java.lang.reflect.Method;
public class PEChannelInitializer extends ChannelInitializer<SocketChannel> {
private final ChannelInitializer<?> oldChannelInitializer;
private Method initChannelMethod;
public PEChannelInitializer(ChannelInitializer<?> oldChannelInitializer) {
this.oldChannelInitializer = oldChannelInitializer;
load();
}
private void load() {
initChannelMethod = Reflection.getMethod(oldChannelInitializer.getClass(), "initChannel", 0);
}
public ChannelInitializer<?> getOldChannelInitializer() {
return oldChannelInitializer;
}
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
initChannelMethod.invoke(oldChannelInitializer, socketChannel);
PlayerChannelHandler channelHandler = new PlayerChannelHandler();
if (socketChannel.pipeline().get("packet_handler") != null) {
socketChannel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, channelHandler);
}
}
}
@@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.earlyinjector.modern;
import io.github.retrooper.packetevents.PacketEvents;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import org.bukkit.entity.Player;
public class PlayerChannelHandler extends ChannelDuplexHandler {
/**
* Associated player.
* This is null until we inject the player.
*/
public volatile Player player;
/**
* Incoming packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet.
* @throws Exception Possible exception.
*/
@Override
public void channelRead(final ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetProcessorInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetProcessorInternal.postRead(player, ctx.channel(), packet);
}
}
/**
* Outgoing packet interception.
*
* @param ctx Netty channel handler context.
* @param packet Raw NMS Packet
* @param promise Netty channel promise.
* @throws Exception Possible exception.
*/
@Override
public void write(final ChannelHandlerContext ctx, Object packet, final ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetProcessorInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetProcessorInternal.postWrite(player, ctx.channel(), packet);
}
}
}
@@ -22,76 +22,65 @@
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.lateinjector;
package io.github.retrooper.packetevents.injector.lateinjector.legacy;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent;
import io.github.retrooper.packetevents.injector.lateinjector.LateInjector;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelDuplexHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import org.bukkit.entity.Player;
public class LateChannelInjector7 implements LateInjector {
public class LateChannelInjectorLegacy implements LateInjector {
@Override
public void injectPlayerSync(Player player) {
final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
public void inject() {
}
@Override
public void eject() {
}
@Override
public void injectPlayer(Player player) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.read(player, ctx.channel(), packet);
packet = PacketEvents.get().packetProcessorInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetHandlerInternal.postRead(player, ctx.channel(), packet);
PacketEvents.get().packetProcessorInternal.postRead(player, ctx.channel(), packet);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.write(player, ctx.channel(), packet);
packet = PacketEvents.get().packetProcessorInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetHandlerInternal.postWrite(player, ctx.channel(), packet);
PacketEvents.get().packetProcessorInternal.postWrite(player, ctx.channel(), packet);
}
}
};
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
channel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, channelDuplexHandler);
}
@Override
public void ejectPlayerSync(Player player) {
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
public void ejectPlayer(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
channel.pipeline().remove(PacketEvents.handlerName);
}
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
@Override
public void injectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
injectPlayerSync(player);
}
});
}
@Override
public void ejectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
channel.pipeline().remove(PacketEvents.handlerName);
}
PacketEvents.get().packetHandlerInternal.keepAliveMap.remove(player.getUniqueId());
PacketEvents.get().packetHandlerInternal.channelMap.remove(player.getName());
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
});
public boolean hasInjected(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
return channel.pipeline().get(PacketEvents.handlerName) != null;
}
@Override
@@ -22,9 +22,10 @@
* SOFTWARE.
*/
package io.github.retrooper.packetevents.injector.lateinjector;
package io.github.retrooper.packetevents.injector.lateinjector.modern;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.injector.lateinjector.LateInjector;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
@@ -33,35 +34,45 @@ import org.bukkit.entity.Player;
import java.util.NoSuchElementException;
public class LateChannelInjector8 implements LateInjector {
public class LateChannelInjector implements LateInjector {
@Override
public void injectPlayerSync(Player player) {
final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
public void inject() {
}
@Override
public void eject() {
}
@Override
public void injectPlayer(Player player) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.read(player, ctx.channel(), packet);
packet = PacketEvents.get().packetProcessorInternal.read(player, ctx.channel(), packet);
if (packet != null) {
super.channelRead(ctx, packet);
PacketEvents.get().packetHandlerInternal.postRead(player, ctx.channel(), packet);
PacketEvents.get().packetProcessorInternal.postRead(player, ctx.channel(), packet);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
packet = PacketEvents.get().packetHandlerInternal.write(player, ctx.channel(), packet);
packet = PacketEvents.get().packetProcessorInternal.write(player, ctx.channel(), packet);
if (packet != null) {
super.write(ctx, packet, promise);
PacketEvents.get().packetHandlerInternal.postWrite(player, ctx.channel(), packet);
PacketEvents.get().packetProcessorInternal.postWrite(player, ctx.channel(), packet);
}
}
};
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
channel.pipeline().addBefore("packet_handler", PacketEvents.handlerName, channelDuplexHandler);
}
@Override
public void ejectPlayerSync(Player player) {
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
public void ejectPlayer(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
try {
channel.pipeline().remove(PacketEvents.handlerName);
@@ -69,39 +80,12 @@ public class LateChannelInjector8 implements LateInjector {
}
}
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
@Override
public void injectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
injectPlayerSync(player);
}
});
}
@Override
public void ejectPlayerAsync(Player player) {
PacketEvents.get().injectAndEjectExecutorService.execute(new Runnable() {
@Override
public void run() {
final Channel channel = (Channel) PacketEvents.get().packetHandlerInternal.getChannel(player);
if (channel.pipeline().get(PacketEvents.handlerName) != null) {
try {
channel.pipeline().remove(PacketEvents.handlerName);
} catch (NoSuchElementException ignored) {
}
}
PacketEvents.get().packetHandlerInternal.keepAliveMap.remove(player.getUniqueId());
PacketEvents.get().packetHandlerInternal.channelMap.remove(player.getName());
PacketEvents.get().getPlayerUtils().clientVersionsMap.remove(player.getAddress());
PacketEvents.get().getPlayerUtils().tempClientVersionMap.remove(player.getAddress());
}
});
public boolean hasInjected(Player player) {
Channel channel = (Channel) PacketEvents.get().packetProcessorInternal.getChannel(player);
return channel.pipeline().get(PacketEvents.handlerName) != null;
}
@Override
@@ -0,0 +1,5 @@
package io.github.retrooper.packetevents.packettype;
public enum PacketState {
STATUS, HANDSHAKING, LOGIN, PLAY
}
@@ -25,8 +25,10 @@
package io.github.retrooper.packetevents.packettype;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -43,7 +45,39 @@ public class PacketType {
* If a Packet Type could not be resolved, it will be set to the current value of this constant.
* This value may change over the versions so it is important to use the variable and not hard code its value.
*/
public static final byte INVALID = -1;
public static final byte INVALID = -128;
public static Map<Class<?>, Byte> packetIDMap = new HashMap<>();
public static void load() {
for (Class<?> cls : PacketType.class.getDeclaredClasses()) {
Class<?> packetTypeClassesSubclass = SubclassUtil.getSubClass(PacketTypeClasses.class, cls.getSimpleName());
for (Class<?> boundCls : cls.getDeclaredClasses()) {
Class<?> packetTypeClassesSubclassBoundClass = SubclassUtil.getSubClass(packetTypeClassesSubclass, boundCls.getSimpleName());
for (Field f : boundCls.getFields()) {
String fieldName = f.getName();
Class<?> fieldType = f.getType();
if (fieldType.equals(byte.class)) {
Field packetTypeClassesSubclassBoundField = null;
try {
packetTypeClassesSubclassBoundField = packetTypeClassesSubclassBoundClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
byte byteID = f.getByte(null);
packetIDMap.put((Class<?>) packetTypeClassesSubclassBoundField.get(null), byteID);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* Status Packet IDs.
*
@@ -60,16 +94,7 @@ public class PacketType {
* @since 1.8
*/
public static class Client {
public static final byte START = 0, PING = 1;
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
/**
* Load the server-bound status Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Status.Client.START, Client.START);
packetIds.put(PacketTypeClasses.Status.Client.PING, Client.PING);
}
public static final byte START = -127, PING = -126;
}
/**
@@ -80,16 +105,26 @@ public class PacketType {
* @since 1.8
*/
public static class Server {
public static final byte PONG = 0, SERVER_INFO = 1;
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
public static final byte PONG = -125, SERVER_INFO = -124;
}
}
/**
* Load the client-bound Status Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Status.Server.PONG, Server.PONG);
packetIds.put(PacketTypeClasses.Status.Server.SERVER_INFO, Server.SERVER_INFO);
}
/**
* Handshaking Packet IDs.
*
* @author retrooper
* @see <a href="https://wiki.vg/Protocol#Handshaking">https://wiki.vg/Protocol#Handshaking</a>
* @since 1.8
*/
public static class Handshaking {
/**
* Server-bound (client-sided) Handshaking Packet IDs.
*
* @author retrooper
* @since 1.8
*/
public static class Client {
public static final byte SET_PROTOCOL = -123;
}
}
@@ -109,18 +144,7 @@ public class PacketType {
* @since 1.8
*/
public static class Client {
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
public static final byte HANDSHAKE = 0, CUSTOM_PAYLOAD = 1, START = 2, ENCRYPTION_BEGIN = 3;
/**
* Load the server-bound Login Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Login.Client.HANDSHAKE, Client.HANDSHAKE);
packetIds.put(PacketTypeClasses.Login.Client.CUSTOM_PAYLOAD, Client.CUSTOM_PAYLOAD);
packetIds.put(PacketTypeClasses.Login.Client.START, Client.START);
packetIds.put(PacketTypeClasses.Login.Client.ENCRYPTION_BEGIN, Client.ENCRYPTION_BEGIN);
}
public static final byte CUSTOM_PAYLOAD = -122, START = -121, ENCRYPTION_BEGIN = -120;
}
/**
@@ -131,19 +155,7 @@ public class PacketType {
* @since 1.8
*/
public static class Server {
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
public static final byte CUSTOM_PAYLOAD = 0, DISCONNECT = 1, ENCRYPTION_BEGIN = 2, SUCCESS = 3, SET_COMPRESSION = 4;
/**
* Load the client-bound Login Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Login.Server.CUSTOM_PAYLOAD, Server.CUSTOM_PAYLOAD);
packetIds.put(PacketTypeClasses.Login.Server.DISCONNECT, Server.DISCONNECT);
packetIds.put(PacketTypeClasses.Login.Server.ENCRYPTION_BEGIN, Server.ENCRYPTION_BEGIN);
packetIds.put(PacketTypeClasses.Login.Server.SUCCESS, Server.SUCCESS);
packetIds.put(PacketTypeClasses.Login.Server.SET_COMPRESSION, Server.SET_COMPRESSION);
}
public static final byte CUSTOM_PAYLOAD = -119, DISCONNECT = -118, ENCRYPTION_BEGIN = -117, SUCCESS = -116, SET_COMPRESSION = -115;
}
}
@@ -163,73 +175,19 @@ public class PacketType {
* @since 1.8
*/
public static class Client {
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
public static final byte TELEPORT_ACCEPT = 0,
TILE_NBT_QUERY = 1, DIFFICULTY_CHANGE = 2, CHAT = 3, CLIENT_COMMAND = 4,
SETTINGS = 5, TAB_COMPLETE = 6, TRANSACTION = 7, ENCHANT_ITEM = 8,
WINDOW_CLICK = 9, CLOSE_WINDOW = 10, CUSTOM_PAYLOAD = 11, B_EDIT = 12,
ENTITY_NBT_QUERY = 13, USE_ENTITY = 14, JIGSAW_GENERATE = 15, KEEP_ALIVE = 16,
DIFFICULTY_LOCK = 17, POSITION = 18, POSITION_LOOK = 19, LOOK = 20,
FLYING = 21, VEHICLE_MOVE = 22, BOAT_MOVE = 23, PICK_ITEM = 24,
AUTO_RECIPE = 25, ABILITIES = 26, BLOCK_DIG = 27, ENTITY_ACTION = 28,
STEER_VEHICLE = 29, RECIPE_DISPLAYED = 30, ITEM_NAME = 31, RESOURCE_PACK_STATUS = 32,
ADVANCEMENTS = 33, TR_SEL = 34, BEACON = 35, HELD_ITEM_SLOT = 36,
SET_COMMAND_BLOCK = 37, SET_COMMAND_MINECART = 38, SET_CREATIVE_SLOT = 39, SET_JIGSAW = 40,
STRUCT = 41, UPDATE_SIGN = 42, ARM_ANIMATION = 43, SPECTATE = 44,
USE_ITEM = 45, BLOCK_PLACE = 46;
/**
* Load the server-bound Play Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Play.Client.TELEPORT_ACCEPT, TELEPORT_ACCEPT);
packetIds.put(PacketTypeClasses.Play.Client.TILE_NBT_QUERY, TILE_NBT_QUERY);
packetIds.put(PacketTypeClasses.Play.Client.DIFFICULTY_CHANGE, DIFFICULTY_CHANGE);
packetIds.put(PacketTypeClasses.Play.Client.CHAT, CHAT);
packetIds.put(PacketTypeClasses.Play.Client.CLIENT_COMMAND, CLIENT_COMMAND);
packetIds.put(PacketTypeClasses.Play.Client.SETTINGS, SETTINGS);
packetIds.put(PacketTypeClasses.Play.Client.TAB_COMPLETE, TAB_COMPLETE);
packetIds.put(PacketTypeClasses.Play.Client.TRANSACTION, TRANSACTION);
packetIds.put(PacketTypeClasses.Play.Client.ENCHANT_ITEM, ENCHANT_ITEM);
packetIds.put(PacketTypeClasses.Play.Client.WINDOW_CLICK, WINDOW_CLICK);
packetIds.put(PacketTypeClasses.Play.Client.CLOSE_WINDOW, CLOSE_WINDOW);
packetIds.put(PacketTypeClasses.Play.Client.CUSTOM_PAYLOAD, CUSTOM_PAYLOAD);
packetIds.put(PacketTypeClasses.Play.Client.B_EDIT, B_EDIT);
packetIds.put(PacketTypeClasses.Play.Client.ENTITY_NBT_QUERY, ENTITY_NBT_QUERY);
packetIds.put(PacketTypeClasses.Play.Client.USE_ENTITY, USE_ENTITY);
packetIds.put(PacketTypeClasses.Play.Client.JIGSAW_GENERATE, JIGSAW_GENERATE);
packetIds.put(PacketTypeClasses.Play.Client.KEEP_ALIVE, KEEP_ALIVE);
packetIds.put(PacketTypeClasses.Play.Client.DIFFICULTY_LOCK, DIFFICULTY_LOCK);
packetIds.put(PacketTypeClasses.Play.Client.POSITION, POSITION);
packetIds.put(PacketTypeClasses.Play.Client.POSITION_LOOK, POSITION_LOOK);
packetIds.put(PacketTypeClasses.Play.Client.LOOK, LOOK);
packetIds.put(PacketTypeClasses.Play.Client.FLYING, FLYING);
packetIds.put(PacketTypeClasses.Play.Client.VEHICLE_MOVE, VEHICLE_MOVE);
packetIds.put(PacketTypeClasses.Play.Client.BOAT_MOVE, BOAT_MOVE);
packetIds.put(PacketTypeClasses.Play.Client.PICK_ITEM, PICK_ITEM);
packetIds.put(PacketTypeClasses.Play.Client.AUTO_RECIPE, AUTO_RECIPE);
packetIds.put(PacketTypeClasses.Play.Client.ABILITIES, ABILITIES);
packetIds.put(PacketTypeClasses.Play.Client.BLOCK_DIG, BLOCK_DIG);
packetIds.put(PacketTypeClasses.Play.Client.ENTITY_ACTION, ENTITY_ACTION);
packetIds.put(PacketTypeClasses.Play.Client.STEER_VEHICLE, STEER_VEHICLE);
packetIds.put(PacketTypeClasses.Play.Client.RECIPE_DISPLAYED, RECIPE_DISPLAYED);
packetIds.put(PacketTypeClasses.Play.Client.ITEM_NAME, ITEM_NAME);
packetIds.put(PacketTypeClasses.Play.Client.RESOURCE_PACK_STATUS, RESOURCE_PACK_STATUS);
packetIds.put(PacketTypeClasses.Play.Client.ADVANCEMENTS, ADVANCEMENTS);
packetIds.put(PacketTypeClasses.Play.Client.TR_SEL, TR_SEL);
packetIds.put(PacketTypeClasses.Play.Client.BEACON, BEACON);
packetIds.put(PacketTypeClasses.Play.Client.HELD_ITEM_SLOT, HELD_ITEM_SLOT);
packetIds.put(PacketTypeClasses.Play.Client.SET_COMMAND_BLOCK, SET_COMMAND_BLOCK);
packetIds.put(PacketTypeClasses.Play.Client.SET_COMMAND_MINECART, SET_COMMAND_MINECART);
packetIds.put(PacketTypeClasses.Play.Client.SET_CREATIVE_SLOT, SET_CREATIVE_SLOT);
packetIds.put(PacketTypeClasses.Play.Client.SET_JIGSAW, SET_JIGSAW);
packetIds.put(PacketTypeClasses.Play.Client.STRUCT, STRUCT);
packetIds.put(PacketTypeClasses.Play.Client.UPDATE_SIGN, UPDATE_SIGN);
packetIds.put(PacketTypeClasses.Play.Client.ARM_ANIMATION, ARM_ANIMATION);
packetIds.put(PacketTypeClasses.Play.Client.SPECTATE, SPECTATE);
packetIds.put(PacketTypeClasses.Play.Client.USE_ITEM, USE_ITEM);
packetIds.put(PacketTypeClasses.Play.Client.BLOCK_PLACE, BLOCK_PLACE);
}
public static final byte TELEPORT_ACCEPT = -114,
TILE_NBT_QUERY = -113, DIFFICULTY_CHANGE = -112, CHAT = -111, CLIENT_COMMAND = -110,
SETTINGS = -109, TAB_COMPLETE = -108, TRANSACTION = -107, ENCHANT_ITEM = -106,
WINDOW_CLICK = -105, CLOSE_WINDOW = -104, CUSTOM_PAYLOAD = -103, B_EDIT = -102,
ENTITY_NBT_QUERY = -101, USE_ENTITY = -100, JIGSAW_GENERATE = -99, KEEP_ALIVE = -98,
DIFFICULTY_LOCK = -97, POSITION = -96, POSITION_LOOK = -95, LOOK = -94,
FLYING = -93, VEHICLE_MOVE = -92, BOAT_MOVE = -91, PICK_ITEM = -90,
AUTO_RECIPE = -89, ABILITIES = -88, BLOCK_DIG = -87, ENTITY_ACTION = -86,
STEER_VEHICLE = -85, RECIPE_DISPLAYED = -84, ITEM_NAME = -83, RESOURCE_PACK_STATUS = -82,
ADVANCEMENTS = -81, TR_SEL = -80, BEACON = -79, HELD_ITEM_SLOT = -78,
SET_COMMAND_BLOCK = -77, SET_COMMAND_MINECART = -76, SET_CREATIVE_SLOT = -75, SET_JIGSAW = -74,
STRUCT = -73, UPDATE_SIGN = -72, ARM_ANIMATION = -71, SPECTATE = -70,
USE_ITEM = -69, BLOCK_PLACE = -68;
/**
* Server-bound Play Packet Type utility.
@@ -270,8 +228,8 @@ public class PacketType {
public static boolean isBlockPlace(final byte packetID) {
final ServerVersion version = PacketEvents.get().getServerUtils().getVersion();
return version.isNewerThan(ServerVersion.v_1_8_8) ?
packetID == Client.USE_ITEM
: packetID == Client.BLOCK_PLACE;
packetID == PacketType.Play.Client.USE_ITEM
: packetID == PacketType.Play.Client.BLOCK_PLACE;
}
}
}
@@ -284,133 +242,29 @@ public class PacketType {
* @since 1.8
*/
public static class Server {
public static final Map<Class<?>, Byte> packetIds = new HashMap<>();
public static final byte SPAWN_ENTITY = 0, SPAWN_ENTITY_EXPERIENCE_ORB = 1, SPAWN_ENTITY_WEATHER = 2, SPAWN_ENTITY_LIVING = 3,
SPAWN_ENTITY_PAINTING = 4, SPAWN_ENTITY_SPAWN = 5, ANIMATION = 6, STATISTIC = 7,
BLOCK_BREAK = 8, BLOCK_BREAK_ANIMATION = 9, TILE_ENTITY_DATA = 10, BLOCK_ACTION = 11,
BLOCK_CHANGE = 12, BOSS = 13, SERVER_DIFFICULTY = 14, CHAT = 15, MULTI_BLOCK_CHANGE = 16,
TAB_COMPLETE = 17, COMMANDS = 18, TRANSACTION = 19, CLOSE_WINDOW = 20,
WINDOW_ITEMS = 21, WINDOW_DATA = 22, SET_SLOT = 23, SET_COOLDOWN = 24,
CUSTOM_PAYLOAD = 25, CUSTOM_SOUND_EFFECT = 26, KICK_DISCONNECT = 27, ENTITY_STATUS = 28,
EXPLOSION = 29, UNLOAD_CHUNK = 30, GAME_STATE_CHANGE = 31, OPEN_WINDOW_HORSE = 32,
KEEP_ALIVE = 33, MAP_CHUNK = 34, WORLD_EVENT = 35, WORLD_PARTICLES = 36,
LIGHT_UPDATE = 37, LOGIN = 38, MAP = 39, OPEN_WINDOW_MERCHANT = 40,
REL_ENTITY_MOVE = 41, REL_ENTITY_MOVE_LOOK = 42, ENTITY_LOOK = 43, ENTITY = 44,
VEHICLE_MOVE = 45, OPEN_BOOK = 46, OPEN_WINDOW = 47, OPEN_SIGN_EDITOR = 48,
AUTO_RECIPE = 49, ABILITIES = 50, COMBAT_EVENT = 51, PLAYER_INFO = 52,
LOOK_AT = 53, POSITION = 54, RECIPES = 55, ENTITY_DESTROY = 56,
REMOVE_ENTITY_EFFECT = 57, RESOURCE_PACK_SEND = 58, RESPAWN = 59, ENTITY_HEAD_ROTATION = 60,
SELECT_ADVANCEMENT_TAB = 61, WORLD_BORDER = 62, CAMERA = 63, HELD_ITEM_SLOT = 64,
VIEW_CENTRE = 65, VIEW_DISTANCE = 66, SCOREBOARD_DISPLAY_OBJECTIVE = 67, ENTITY_METADATA = 68,
ATTACH_ENTITY = 69, ENTITY_VELOCITY = 70, ENTITY_EQUIPMENT = 71, EXPERIENCE = 72,
UPDATE_HEALTH = 73, SCOREBOARD_OBJECTIVE = 74, MOUNT = 75, SCOREBOARD_TEAM = 76,
SCOREBOARD_SCORE = 77, SPAWN_POSITION = 78, UPDATE_TIME = 79, TITLE = 80,
ENTITY_SOUND = 81, NAMED_SOUND_EFFECT = 82, STOP_SOUND = 83, PLAYER_LIST_HEADER_FOOTER = 84,
NBT_QUERY = 85, COLLECT = 86, ENTITY_TELEPORT = 87, ADVANCEMENTS = 88, UPDATE_ATTRIBUTES = 89,
ENTITY_EFFECT = 90, RECIPE_UPDATE = 91, TAGS = 92, MAP_CHUNK_BULK = 93, NAMED_ENTITY_SPAWN = 94;
/**
* Load the client-bound Play Packet IDs.
*/
public static void init() {
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY, SPAWN_ENTITY);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY_EXPERIENCE_ORB, SPAWN_ENTITY_EXPERIENCE_ORB);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY_WEATHER, SPAWN_ENTITY_WEATHER);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY_LIVING, SPAWN_ENTITY_LIVING);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_PAINTING);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY_SPAWN, SPAWN_ENTITY_SPAWN);
packetIds.put(PacketTypeClasses.Play.Server.ANIMATION, ANIMATION);
packetIds.put(PacketTypeClasses.Play.Server.STATISTIC, STATISTIC);
packetIds.put(PacketTypeClasses.Play.Server.BLOCK_BREAK, BLOCK_BREAK);
packetIds.put(PacketTypeClasses.Play.Server.BLOCK_BREAK_ANIMATION, BLOCK_BREAK_ANIMATION);
packetIds.put(PacketTypeClasses.Play.Server.TILE_ENTITY_DATA, TILE_ENTITY_DATA);
packetIds.put(PacketTypeClasses.Play.Server.BLOCK_ACTION, BLOCK_ACTION);
packetIds.put(PacketTypeClasses.Play.Server.BLOCK_CHANGE, BLOCK_CHANGE);
packetIds.put(PacketTypeClasses.Play.Server.BOSS, BOSS);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_ENTITY, SPAWN_ENTITY);
packetIds.put(PacketTypeClasses.Play.Server.SERVER_DIFFICULTY, SERVER_DIFFICULTY);
packetIds.put(PacketTypeClasses.Play.Server.CHAT, CHAT);
packetIds.put(PacketTypeClasses.Play.Server.MULTI_BLOCK_CHANGE, MULTI_BLOCK_CHANGE);
packetIds.put(PacketTypeClasses.Play.Server.TAB_COMPLETE, TAB_COMPLETE);
packetIds.put(PacketTypeClasses.Play.Server.COMMANDS, COMMANDS);
packetIds.put(PacketTypeClasses.Play.Server.TRANSACTION, TRANSACTION);
packetIds.put(PacketTypeClasses.Play.Server.CLOSE_WINDOW, CLOSE_WINDOW);
packetIds.put(PacketTypeClasses.Play.Server.WINDOW_ITEMS, WINDOW_ITEMS);
packetIds.put(PacketTypeClasses.Play.Server.WINDOW_DATA, WINDOW_DATA);
packetIds.put(PacketTypeClasses.Play.Server.SET_SLOT, SET_SLOT);
packetIds.put(PacketTypeClasses.Play.Server.SET_COOLDOWN, SET_COOLDOWN);
packetIds.put(PacketTypeClasses.Play.Server.CUSTOM_PAYLOAD, CUSTOM_PAYLOAD);
packetIds.put(PacketTypeClasses.Play.Server.CUSTOM_SOUND_EFFECT, CUSTOM_SOUND_EFFECT);
packetIds.put(PacketTypeClasses.Play.Server.KICK_DISCONNECT, KICK_DISCONNECT);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_STATUS, ENTITY_STATUS);
packetIds.put(PacketTypeClasses.Play.Server.EXPLOSION, EXPLOSION);
packetIds.put(PacketTypeClasses.Play.Server.UNLOAD_CHUNK, UNLOAD_CHUNK);
packetIds.put(PacketTypeClasses.Play.Server.GAME_STATE_CHANGE, GAME_STATE_CHANGE);
packetIds.put(PacketTypeClasses.Play.Server.OPEN_WINDOW_HORSE, OPEN_WINDOW_HORSE);
packetIds.put(PacketTypeClasses.Play.Server.KEEP_ALIVE, KEEP_ALIVE);
packetIds.put(PacketTypeClasses.Play.Server.MAP_CHUNK, MAP_CHUNK);
packetIds.put(PacketTypeClasses.Play.Server.WORLD_EVENT, WORLD_EVENT);
packetIds.put(PacketTypeClasses.Play.Server.WORLD_EVENT, SPAWN_ENTITY);
packetIds.put(PacketTypeClasses.Play.Server.WORLD_PARTICLES, WORLD_PARTICLES);
packetIds.put(PacketTypeClasses.Play.Server.LIGHT_UPDATE, LIGHT_UPDATE);
packetIds.put(PacketTypeClasses.Play.Server.LOGIN, LOGIN);
packetIds.put(PacketTypeClasses.Play.Server.MAP, MAP);
packetIds.put(PacketTypeClasses.Play.Server.OPEN_WINDOW_MERCHANT, OPEN_WINDOW_MERCHANT);
packetIds.put(PacketTypeClasses.Play.Server.REL_ENTITY_MOVE, REL_ENTITY_MOVE);
packetIds.put(PacketTypeClasses.Play.Server.REL_ENTITY_MOVE_LOOK, REL_ENTITY_MOVE_LOOK);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_LOOK, ENTITY_LOOK);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY, ENTITY);
packetIds.put(PacketTypeClasses.Play.Server.VEHICLE_MOVE, VEHICLE_MOVE);
packetIds.put(PacketTypeClasses.Play.Server.OPEN_BOOK, OPEN_BOOK);
packetIds.put(PacketTypeClasses.Play.Server.OPEN_WINDOW, OPEN_WINDOW);
packetIds.put(PacketTypeClasses.Play.Server.OPEN_SIGN_EDITOR, OPEN_SIGN_EDITOR);
packetIds.put(PacketTypeClasses.Play.Server.AUTO_RECIPE, AUTO_RECIPE);
packetIds.put(PacketTypeClasses.Play.Server.ABILITIES, ABILITIES);
packetIds.put(PacketTypeClasses.Play.Server.COMBAT_EVENT, COMBAT_EVENT);
packetIds.put(PacketTypeClasses.Play.Server.PLAYER_INFO, PLAYER_INFO);
packetIds.put(PacketTypeClasses.Play.Server.LOOK_AT, LOOK_AT);
packetIds.put(PacketTypeClasses.Play.Server.POSITION, POSITION);
packetIds.put(PacketTypeClasses.Play.Server.RECIPES, RECIPES);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_DESTROY, ENTITY_DESTROY);
packetIds.put(PacketTypeClasses.Play.Server.REMOVE_ENTITY_EFFECT, REMOVE_ENTITY_EFFECT);
packetIds.put(PacketTypeClasses.Play.Server.RESOURCE_PACK_SEND, RESOURCE_PACK_SEND);
packetIds.put(PacketTypeClasses.Play.Server.RESPAWN, RESPAWN);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_HEAD_ROTATION, ENTITY_HEAD_ROTATION);
packetIds.put(PacketTypeClasses.Play.Server.SELECT_ADVANCEMENT_TAB, SELECT_ADVANCEMENT_TAB);
packetIds.put(PacketTypeClasses.Play.Server.WORLD_BORDER, WORLD_BORDER);
packetIds.put(PacketTypeClasses.Play.Server.CAMERA, CAMERA);
packetIds.put(PacketTypeClasses.Play.Server.HELD_ITEM_SLOT, HELD_ITEM_SLOT);
packetIds.put(PacketTypeClasses.Play.Server.VIEW_CENTRE, VIEW_CENTRE);
packetIds.put(PacketTypeClasses.Play.Server.VIEW_DISTANCE, VIEW_DISTANCE);
packetIds.put(PacketTypeClasses.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE, SCOREBOARD_DISPLAY_OBJECTIVE);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_METADATA, ENTITY_METADATA);
packetIds.put(PacketTypeClasses.Play.Server.ATTACH_ENTITY, ATTACH_ENTITY);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_VELOCITY, ENTITY_VELOCITY);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_EQUIPMENT, ENTITY_EQUIPMENT);
packetIds.put(PacketTypeClasses.Play.Server.EXPERIENCE, EXPERIENCE);
packetIds.put(PacketTypeClasses.Play.Server.UPDATE_HEALTH, UPDATE_HEALTH);
packetIds.put(PacketTypeClasses.Play.Server.SCOREBOARD_OBJECTIVE, SCOREBOARD_OBJECTIVE);
packetIds.put(PacketTypeClasses.Play.Server.MOUNT, MOUNT);
packetIds.put(PacketTypeClasses.Play.Server.SCOREBOARD_TEAM, SCOREBOARD_TEAM);
packetIds.put(PacketTypeClasses.Play.Server.SCOREBOARD_SCORE, SCOREBOARD_SCORE);
packetIds.put(PacketTypeClasses.Play.Server.SPAWN_POSITION, SPAWN_POSITION);
packetIds.put(PacketTypeClasses.Play.Server.UPDATE_TIME, UPDATE_TIME);
packetIds.put(PacketTypeClasses.Play.Server.TITLE, TITLE);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_SOUND, ENTITY_SOUND);
packetIds.put(PacketTypeClasses.Play.Server.NAMED_SOUND_EFFECT, NAMED_SOUND_EFFECT);
packetIds.put(PacketTypeClasses.Play.Server.STOP_SOUND, STOP_SOUND);
packetIds.put(PacketTypeClasses.Play.Server.PLAYER_LIST_HEADER_FOOTER, PLAYER_LIST_HEADER_FOOTER);
packetIds.put(PacketTypeClasses.Play.Server.NBT_QUERY, NBT_QUERY);
packetIds.put(PacketTypeClasses.Play.Server.COLLECT, COLLECT);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_TELEPORT, ENTITY_TELEPORT);
packetIds.put(PacketTypeClasses.Play.Server.ADVANCEMENTS, ADVANCEMENTS);
packetIds.put(PacketTypeClasses.Play.Server.UPDATE_ATTRIBUTES, UPDATE_ATTRIBUTES);
packetIds.put(PacketTypeClasses.Play.Server.ENTITY_EFFECT, ENTITY_EFFECT);
packetIds.put(PacketTypeClasses.Play.Server.RECIPE_UPDATE, RECIPE_UPDATE);
packetIds.put(PacketTypeClasses.Play.Server.TAGS, TAGS);
packetIds.put(PacketTypeClasses.Play.Server.MAP_CHUNK_BULK, MAP_CHUNK_BULK);
packetIds.put(PacketTypeClasses.Play.Server.NAMED_ENTITY_SPAWN, NAMED_ENTITY_SPAWN);
}
public static final byte SPAWN_ENTITY = -67, SPAWN_ENTITY_EXPERIENCE_ORB = -66, SPAWN_ENTITY_WEATHER = -65, SPAWN_ENTITY_LIVING = -64,
SPAWN_ENTITY_PAINTING = -63, SPAWN_ENTITY_SPAWN = -62, ANIMATION = -61, STATISTIC = -60,
BLOCK_BREAK = -59, BLOCK_BREAK_ANIMATION = -58, TILE_ENTITY_DATA = -57, BLOCK_ACTION = -56,
BLOCK_CHANGE = -55, BOSS = -54, SERVER_DIFFICULTY = -53, CHAT = -52, MULTI_BLOCK_CHANGE = -51,
TAB_COMPLETE = -50, COMMANDS = -49, TRANSACTION = -48, CLOSE_WINDOW = -47,
WINDOW_ITEMS = -46, WINDOW_DATA = -45, SET_SLOT = -44, SET_COOLDOWN = -43,
CUSTOM_PAYLOAD = -42, CUSTOM_SOUND_EFFECT = -41, KICK_DISCONNECT = -40, ENTITY_STATUS = -39,
EXPLOSION = -38, UNLOAD_CHUNK = -37, GAME_STATE_CHANGE = -36, OPEN_WINDOW_HORSE = -35,
KEEP_ALIVE = -34, MAP_CHUNK = -33, WORLD_EVENT = -32, WORLD_PARTICLES = -31,
LIGHT_UPDATE = -30, LOGIN = -29, MAP = -28, OPEN_WINDOW_MERCHANT = -27,
REL_ENTITY_MOVE = -26, REL_ENTITY_MOVE_LOOK = -25, ENTITY_LOOK = -24, ENTITY = -23,
VEHICLE_MOVE = -22, OPEN_BOOK = -21, OPEN_WINDOW = -20, OPEN_SIGN_EDITOR = -19,
AUTO_RECIPE = -18, ABILITIES = -17, COMBAT_EVENT = -16, PLAYER_INFO = -15,
LOOK_AT = -14, POSITION = -13, RECIPES = -12, ENTITY_DESTROY = -11,
REMOVE_ENTITY_EFFECT = -10, RESOURCE_PACK_SEND = -9, RESPAWN = -8, ENTITY_HEAD_ROTATION = -7,
SELECT_ADVANCEMENT_TAB = -6, WORLD_BORDER = -5, CAMERA = -4, HELD_ITEM_SLOT = -3,
VIEW_CENTRE = -2, VIEW_DISTANCE = -1, SCOREBOARD_DISPLAY_OBJECTIVE = 0, ENTITY_METADATA = 1,
ATTACH_ENTITY = 2, ENTITY_VELOCITY = 3, ENTITY_EQUIPMENT = 4, EXPERIENCE = 5,
UPDATE_HEALTH = 6, SCOREBOARD_OBJECTIVE = 7, MOUNT = 8, SCOREBOARD_TEAM = 9,
SCOREBOARD_SCORE = 10, SPAWN_POSITION = 11, UPDATE_TIME = 12, TITLE = 13,
ENTITY_SOUND = 14, NAMED_SOUND_EFFECT = 15, STOP_SOUND = 16, PLAYER_LIST_HEADER_FOOTER = 17,
NBT_QUERY = 18, COLLECT = 19, ENTITY_TELEPORT = 20, ADVANCEMENTS = 21, UPDATE_ATTRIBUTES = 22,
ENTITY_EFFECT = 23, RECIPE_UPDATE = 24, TAGS = 25, MAP_CHUNK_BULK = 26, NAMED_ENTITY_SPAWN = 27;
/**
* Client-bound Play Packet Type utility.
@@ -29,15 +29,17 @@ import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
public class PacketTypeClasses {
public static void load() {
//PLAY
PacketTypeClasses.Play.Client.load();
PacketTypeClasses.Play.Server.load();
//LOGIN
PacketTypeClasses.Login.Client.load();
PacketTypeClasses.Login.Server.load();
//STATUS
PacketTypeClasses.Status.Client.load();
PacketTypeClasses.Status.Server.load();
//HANDSHAKING
PacketTypeClasses.Handshaking.Client.load();
//LOGIN
PacketTypeClasses.Login.Client.load();
PacketTypeClasses.Login.Server.load();
//PLAY
PacketTypeClasses.Play.Client.load();
PacketTypeClasses.Play.Server.load();
}
public static class Status {
@@ -47,7 +49,6 @@ public class PacketTypeClasses {
public static void load() {
Client.START = NMSUtils.getNMSClassWithoutException("PacketStatusInStart");
Client.PING = NMSUtils.getNMSClassWithoutException("PacketStatusInPing");
PacketType.Status.Client.init();
}
}
@@ -57,22 +58,29 @@ public class PacketTypeClasses {
public static void load() {
Server.PONG = NMSUtils.getNMSClassWithoutException("PacketStatusOutPong");
Server.SERVER_INFO = NMSUtils.getNMSClassWithoutException("PacketStatusOutServerInfo");
PacketType.Status.Server.init();
}
}
}
public static class Handshaking {
public static class Client {
public static Class<?> SET_PROTOCOL;
public static void load() {
Handshaking.Client.SET_PROTOCOL = NMSUtils.getNMSClassWithoutException("PacketHandshakingInSetProtocol");
}
}
}
public static class Login {
public static class Client {
public static Class<?> HANDSHAKE, CUSTOM_PAYLOAD, START, ENCRYPTION_BEGIN;
public static Class<?> CUSTOM_PAYLOAD, START, ENCRYPTION_BEGIN;
public static void load() {
Client.HANDSHAKE = NMSUtils.getNMSClassWithoutException("PacketHandshakingInSetProtocol");
//In and Out custom payload login packets have been here since AROUND 1.13.2.
Client.CUSTOM_PAYLOAD = NMSUtils.getNMSClassWithoutException("PacketLoginInCustomPayload");
Client.START = NMSUtils.getNMSClassWithoutException("PacketLoginInStart");
Client.ENCRYPTION_BEGIN = NMSUtils.getNMSClassWithoutException("PacketLoginInEncryptionBegin");
PacketType.Login.Client.init();
}
}
@@ -85,7 +93,6 @@ public class PacketTypeClasses {
Server.ENCRYPTION_BEGIN = NMSUtils.getNMSClassWithoutException("PacketLoginOutEncryptionBegin");
Server.SUCCESS = NMSUtils.getNMSClassWithoutException("PacketLoginOutSuccess");
Server.SET_COMPRESSION = NMSUtils.getNMSClassWithoutException("PacketLoginOutSetCompression");
PacketType.Login.Server.init();
}
}
}
@@ -176,7 +183,6 @@ public class PacketTypeClasses {
} catch (ClassNotFoundException ignored) {
}
PacketType.Play.Client.init();
}
}
@@ -311,7 +317,6 @@ public class PacketTypeClasses {
TAGS = NMSUtils.getNMSClassWithoutException(s + "Tags");
MAP_CHUNK_BULK = NMSUtils.getNMSClassWithoutException(s + "MapChunkBulk");
NAMED_ENTITY_SPAWN = NMSUtils.getNMSClassWithoutException(s + "NamedEntitySpawn");
PacketType.Play.Server.init();
}
}
}
@@ -24,17 +24,23 @@
package io.github.retrooper.packetevents.packetwrappers;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.exceptions.WrapperFieldNotFoundException;
import io.github.retrooper.packetevents.exceptions.WrapperUnsupportedUsageException;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.utils.reflection.ClassUtil;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
@@ -42,8 +48,8 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
private static final Map<Class<?>, Map<Class<?>, Field[]>> FIELD_CACHE = new ConcurrentHashMap<>();
private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
public static ServerVersion version;
protected NMSPacket packet;
private final Class<?> packetClass;
protected final NMSPacket packet;
public WrappedPacket() {
packet = null;
@@ -67,9 +73,18 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
}
private void load0() {
if (!isSupported()) {
throw new WrapperUnsupportedUsageException(getClass());
}
final Class<? extends WrappedPacket> clazz = getClass();
if (!LOADED_WRAPPERS.containsKey(clazz)) {
load();
try {
load();
} catch (Exception ex) {
String wrapperName = ClassUtil.getClassSimpleName(getClass());
PacketEvents.get().getPlugin().getLogger()
.log(Level.SEVERE, "PacketEvents found an exception while loading the " + wrapperName + " packet wrapper. Please report this bug! Tell us about your server version, spigot and code(of you using the wrapper)", ex);
}
LOADED_WRAPPERS.put(clazz, true);
}
}
@@ -78,6 +93,74 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
}
protected void throwUnsupportedOperation(Enum<?> enumConst) throws UnsupportedOperationException {
Class<?> enumConstClass = enumConst.getClass();
Field field = null;
try {
field = enumConstClass.getField(enumConst.name());
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
if (field.isAnnotationPresent(SupportedVersions.class)) {
SupportedVersions supportedVersions = field.getAnnotation(SupportedVersions.class);
List<ServerVersion> versionList = parseSupportedVersionsAnnotation(supportedVersions);
String supportedVersionsMsg = Arrays.toString(versionList.toArray(new ServerVersion[0]));
throw new UnsupportedOperationException("PacketEvents failed to use the " + enumConst.name() + " enum constant in the " + enumConstClass.getSimpleName() + " enum. This enum constant is not supported on your server version. (" + PacketEvents.get().getServerUtils().getVersion() + ")\n This enum constant is only supported on these server versions: " + supportedVersionsMsg);
} else {
throw new UnsupportedOperationException("PacketEvents failed to use the " + enumConst.name() + " enum constant in the " + enumConstClass.getSimpleName() + " enum. This enum constant is not supported on your server version. (" + PacketEvents.get().getServerUtils().getVersion() + ")\n Failed to find out what server versions this enum constant is supported on.");
}
}
protected void throwUnsupportedOperation() throws UnsupportedOperationException {
final String currentMethodName = "throwUnsupportedOperation";
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int stackTraceElementIndex = 2;
for (int i = 0; i < stackTraceElements.length; i++) {
StackTraceElement element = stackTraceElements[i];
if (element.getMethodName().equals(currentMethodName)) {
stackTraceElementIndex = i + 1; //It is the next method
break;
}
}
StackTraceElement stackTraceElement = stackTraceElements[stackTraceElementIndex];
String methodName = stackTraceElement.getMethodName();
List<Method> possibleMethods = Reflection.getMethods(getClass(), methodName, null);
Method method = null;
for (Method m : possibleMethods) {
if (m.isAnnotationPresent(SupportedVersions.class)) {
method = m;
break;
}
}
if (method == null) {
throw new UnsupportedOperationException("PacketEvents failed to access your requested field. This field is not supported on your server version. Failed to lookup the server versions this field supports...");
} else {
SupportedVersions supportedVersions = method.getAnnotation(SupportedVersions.class);
List<ServerVersion> versionList = parseSupportedVersionsAnnotation(supportedVersions);
String supportedVersionsMsg = Arrays.toString(versionList.toArray(new ServerVersion[0]));
throw new UnsupportedOperationException("PacketEvents failed to access your requested field. This field is not supported on your server version. (" + PacketEvents.get().getServerUtils().getVersion() + ")\n This field is only supported on these server versions: " + supportedVersionsMsg);
}
}
private List<ServerVersion> parseSupportedVersionsAnnotation(SupportedVersions supportedVersions) {
List<ServerVersion> versionList = new ArrayList<>();
for (int i = 0; i < supportedVersions.ranges().length; i += 2) {
ServerVersion first = supportedVersions.ranges()[i];
ServerVersion last = supportedVersions.ranges()[i + 1];
if (first == last) {
versionList.add(first);
continue;
} else if (first == ServerVersion.ERROR) {
first = ServerVersion.getOldest();
} else if (last == ServerVersion.ERROR) {
last = ServerVersion.getLatest();
}
versionList.addAll(Arrays.asList(ServerVersion.values()).subList(first.ordinal(), last.ordinal() + 1));
}
return versionList;
}
@Override
public boolean readBoolean(int index) {
@@ -151,7 +234,7 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
@Override
public String[] readStringArray(int index) {
return read(index, String[].class); // Can we be sure that returning the original array is okay?
return read(index, String[].class); // JavaImpact: Can we be sure that returning the original array is okay? retrooper: Yes
}
@Override
@@ -159,6 +242,7 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
return read(index, String.class);
}
@Override
public Object readAnyObject(int index) {
try {
Field f = packetClass.getDeclaredFields()[index];
@@ -176,7 +260,13 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
return null;
}
public Object readObject(int index, Class<?> type) {
@Override
public <T> T readObject(int index, Class<? extends T> type) {
return read(index, type);
}
@Override
public Enum<?> readEnumConstant(int index, Class<? extends Enum<?>> type) {
return read(index, type);
}
@@ -185,7 +275,7 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
Field field = getField(type, index);
try {
return (T) field.get(packet.getRawNMSPacket());
} catch (IllegalAccessException | NullPointerException e) {
} catch (IllegalAccessException | NullPointerException | ArrayIndexOutOfBoundsException e) {
throw new WrapperFieldNotFoundException(packetClass, type, index);
}
}
@@ -235,8 +325,69 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
write(value.getClass(), index, value);
}
@Override
public void writeBooleanArray(int index, boolean[] array) {
write(boolean[].class, index, array);
}
@Override
public void writeByteArray(int index, byte[] value) {
write(byte[].class, index, value);
}
@Override
public void writeShortArray(int index, short[] value) {
write(short[].class, index, value);
}
@Override
public void writeIntArray(int index, int[] value) {
write(int[].class, index, value);
}
@Override
public void writeLongArray(int index, long[] value) {
write(long[].class, index, value);
}
@Override
public void writeFloatArray(int index, float[] value) {
write(float[].class, index, value);
}
@Override
public void writeDoubleArray(int index, double[] value) {
write(double[].class, index, value);
}
@Override
public void writeStringArray(int index, String[] value) {
write(String[].class, index, value);
}
@Override
public void writeAnyObject(int index, Object value) {
try {
Field f = packetClass.getDeclaredFields()[index];
Reflection.getFieldWithoutFinalModifier(f);
try {
f.set(packet.getRawNMSPacket(), value);
} catch (IllegalAccessException | NullPointerException e) {
e.printStackTrace();
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new WrapperFieldNotFoundException("PacketEvents failed to find any field indexed " + index + " in the " + ClassUtil.getClassSimpleName(packetClass) + " class!");
}
}
@Override
public void writeEnumConstant(int index, Enum<?> enumConstant) {
write(enumConstant.getClass(), index, enumConstant);
}
public void write(Class<?> type, int index, Object value) throws WrapperFieldNotFoundException {
Field field = getField(type, index);
Reflection.getFieldWithoutFinalModifier(field);
if (field == null) {
throw new WrapperFieldNotFoundException(packetClass, type, index);
}
@@ -266,8 +417,28 @@ public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter {
return ret.toArray(EMPTY_FIELD_ARRAY);
}
// Does the server version support reading/sending this packet?
/**
* Does the local server version support reading at-least one field with this packet wrapper?
* If it does, we can label this wrapper to be supported on the local server version.
* One example where it would not be supported would be if the packet the wrapper is wrapping doesn't even exist on the local server version.
*
* @return Is the wrapper supported on the local server version?
*/
public boolean isSupported() {
return true;
}
/**
* If a method in a wrapper is annotated with this, it means it isn't supported on all server versions.
* It not being supported by all server versions can lead to exceptions. Make sure you always decompile a wrapper getter before using it.
* This annotation will specify what versions are supported.
*
* @author retrooper
* @since 1.8
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportedVersions {
ServerVersion[] ranges() default {};
}
}
@@ -58,7 +58,9 @@ public interface WrapperPacketReader {
String readString(int index);
Object readObject(int index, Class<?> type);
<T> T readObject(int index, Class<? extends T> type);
Enum<?> readEnumConstant(int index, Class<? extends Enum<?>> type);
Object readAnyObject(int index);
}
@@ -25,8 +25,6 @@
package io.github.retrooper.packetevents.packetwrappers;
public interface WrapperPacketWriter {
// TODO write for the arrays
void writeBoolean(int index, boolean value);
void writeByte(int index, byte value);
@@ -43,5 +41,26 @@ public interface WrapperPacketWriter {
void writeString(int index, String value);
//ARRAYS
void writeBooleanArray(int index, boolean[] array);
void writeByteArray(int index, byte[] value);
void writeShortArray(int index, short[] value);
void writeIntArray(int index, int[] value);
void writeLongArray(int index, long[] value);
void writeFloatArray(int index, float[] value);
void writeDoubleArray(int index, double[] value);
void writeStringArray(int index, String[] value);
void writeObject(int index, Object object);
void writeAnyObject(int index, Object value);
void writeEnumConstant(int index, Enum<?> enumConstant);
}
@@ -22,14 +22,15 @@
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.login.in.handshake;
package io.github.retrooper.packetevents.packetwrappers.handshaking.setprotocol;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
public class WrappedPacketLoginInHandshake extends WrappedPacket {
public WrappedPacketLoginInHandshake(NMSPacket packet) {
public class WrappedPacketHandshakingInSetProtocol extends WrappedPacket {
public WrappedPacketHandshakingInSetProtocol(NMSPacket packet) {
super(packet);
}
@@ -37,16 +38,28 @@ public class WrappedPacketLoginInHandshake extends WrappedPacket {
return readInt(0);
}
public String getHostName() {
return readString(0);
public void setProtocolVersion(int protocolVersion) {
writeInt(0, protocolVersion);
}
public int getPort() {
return readInt(1);
}
public void setPort(int port) {
writeInt(1, port);
}
public String getHostName() {
return readString(0);
}
public void setHostName(String hostName) {
writeString(0, hostName);
}
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Client.HANDSHAKE != null;
return PacketTypeClasses.Handshaking.Client.SET_PROTOCOL != null;
}
}
@@ -30,9 +30,6 @@ import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
/**
* This packet exists since 1.13
*/
public class WrappedPacketLoginInCustomPayload extends WrappedPacket {
private static Class<?> byteBufClass;
private static Class<?> packetDataSerializerClass;
@@ -54,6 +51,10 @@ public class WrappedPacketLoginInCustomPayload extends WrappedPacket {
return readInt(0);
}
public void setMessageId(int id) {
writeInt(0, id);
}
public byte[] getData() {
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket byteBufWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
@@ -61,6 +62,13 @@ public class WrappedPacketLoginInCustomPayload extends WrappedPacket {
return PacketEvents.get().getByteBufUtil().getBytes(byteBuf);
}
public void setData(byte[] data) {
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket dataSerializerWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
Object byteBuf = dataSerializerWrapper.readObject(0, byteBufClass);
PacketEvents.get().getByteBufUtil().setBytes(byteBuf, data);
}
@Override
public boolean isSupported() {
return version.isNewerThan(ServerVersion.v_1_12_2);
@@ -37,10 +37,18 @@ public class WrappedPacketLoginInEncryptionBegin extends WrappedPacket {
return readByteArray(0);
}
public void setPublicKey(byte[] key) {
writeByteArray(0, key);
}
public byte[] getVerifyToken() {
return readByteArray(1);
}
public void setVerifyToken(byte[] token) {
writeByteArray(1, token);
}
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Client.ENCRYPTION_BEGIN != null;
@@ -40,6 +40,11 @@ public class WrappedPacketLoginInStart extends WrappedPacket {
return GameProfileUtil.getWrappedGameProfile(readObject(0, NMSUtils.gameProfileClass));
}
public void setGameProfile(WrappedGameProfile wrappedGameProfile) {
Object gameProfile = GameProfileUtil.getGameProfile(wrappedGameProfile.id, wrappedGameProfile.name);
write(NMSUtils.gameProfileClass, 0, gameProfile);
}
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Client.START != null;
@@ -81,14 +81,31 @@ public class WrappedPacketLoginOutCustomPayload extends WrappedPacket implements
return messageID;
}
public void setMessageId(int messageID) {
if (packet != null) {
writeInt(0, messageID);
} else {
this.messageID = messageID;
}
}
public String getChannelName() {
if (packet != null) {
Object minecraftKey = readObject(0, NMSUtils.minecraftKeyClass);
return NMSUtils.getStringFromMinecraftKey(minecraftKey);
return NMSUtils.getStringFromMinecraftKey(minecraftKey);
}
return channelName;
}
public void setChannelName(String channelName) {
if (packet != null) {
Object minecraftKey = NMSUtils.generateMinecraftKey(channelName);
writeObject(0, minecraftKey);
} else {
this.channelName = channelName;
}
}
public byte[] getData() {
if (packet != null) {
Object dataSerializer = readObject(0, packetDataSerializerClass);
@@ -99,6 +116,13 @@ public class WrappedPacketLoginOutCustomPayload extends WrappedPacket implements
return data;
}
public void setData(byte[] data) {
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket dataSerializerWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
Object byteBuf = dataSerializerWrapper.readObject(0, byteBufClass);
PacketEvents.get().getByteBufUtil().setBytes(byteBuf, data);
}
@Override
public Object asNMSPacket() {
Object byteBufObject = PacketEvents.get().getByteBufUtil().wrappedBuffer(data);
@@ -27,7 +27,6 @@ package io.github.retrooper.packetevents.packetwrappers.login.out.disconnect;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.packetwrappers.play.out.chat.WrappedPacketOutChat;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
public class WrappedPacketLoginOutDisconnect extends WrappedPacket {
@@ -37,7 +36,12 @@ public class WrappedPacketLoginOutDisconnect extends WrappedPacket {
public String getReason() {
Object iChatBaseComponent = readObject(0, NMSUtils.iChatBaseComponentClass);
return WrappedPacketOutChat.toStringFromIChatBaseComponent(iChatBaseComponent);
return NMSUtils.readIChatBaseComponent(iChatBaseComponent);
}
public void setReason(String reason) {
Object iChatBaseComponent = NMSUtils.generateIChatBaseComponent(reason);
writeObject(0, iChatBaseComponent);
}
@Override
@@ -39,14 +39,26 @@ public class WrappedPacketLoginOutEncryptionBegin extends WrappedPacket {
return readString(0);
}
public PublicKey getPublicKey() {
return (PublicKey) readObject(1, PublicKey.class);
public void setEncodedString(String encodedString) {
writeString(0, encodedString);
}
public byte[] getByteArray() {
public PublicKey getPublicKey() {
return readObject(1, PublicKey.class);
}
public void setPublicKey(PublicKey key) {
writeObject(0, key);
}
public byte[] getVerifyToken() {
return readByteArray(0);
}
public void setVerifyToken(byte[] verifyToken) {
writeByteArray(0, verifyToken);
}
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Server.ENCRYPTION_BEGIN != null;
@@ -28,6 +28,7 @@ import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -68,10 +69,18 @@ public class WrappedPacketLoginOutSetCompression extends WrappedPacket implement
return threshold;
}
public void setThreshold(int threshold) {
if (packet != null) {
writeInt(0, threshold);
} else {
this.threshold = threshold;
}
}
@Override
public Object asNMSPacket() {
try {
return constructor.newInstance(threshold);
return constructor.newInstance(getThreshold());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
@@ -80,6 +89,6 @@ public class WrappedPacketLoginOutSetCompression extends WrappedPacket implement
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Server.SET_COMPRESSION != null;
return version.isNewerThan(ServerVersion.v_1_7_10);
}
}
@@ -40,6 +40,12 @@ public class WrappedPacketLoginOutSuccess extends WrappedPacket {
return GameProfileUtil.getWrappedGameProfile(readObject(0, NMSUtils.gameProfileClass));
}
public void setGameProfile(WrappedGameProfile wrappedGameProfile) {
Object gameProfile = GameProfileUtil.getGameProfile(wrappedGameProfile.id, wrappedGameProfile.name);
write(NMSUtils.gameProfileClass, 0, gameProfile);
}
@Override
public boolean isSupported() {
return PacketTypeClasses.Login.Server.SUCCESS != null;
@@ -28,6 +28,7 @@ import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
public final class WrappedPacketInAbilities extends WrappedPacket {
private static boolean v_1_16_Mode;
@@ -41,21 +42,35 @@ public final class WrappedPacketInAbilities extends WrappedPacket {
v_1_16_Mode = Reflection.getField(PacketTypeClasses.Play.Client.ABILITIES, boolean.class, 1) == null;
}
public boolean isFlying() {
return readBoolean(v_1_16_Mode ? 0 : 1);
}
public void setFlying(boolean flying) {
writeBoolean(v_1_16_Mode ? 0 : 1, flying);
}
/**
* This will return null if the server version is not available in 1.16.x and above
*
* @return Whether the player is vulnerable to damage or not.
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public boolean isVulnerable() throws UnsupportedOperationException {
if (v_1_16_Mode) {
throw new UnsupportedOperationException("This field does not exist on your version!");
throwUnsupportedOperation();
}
return readBoolean(0);
}
public boolean isFlying() {
return readBoolean(v_1_16_Mode ? 0 : 1);
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public void setVulnerable(boolean vulnerable) throws UnsupportedOperationException {
if (v_1_16_Mode) {
throwUnsupportedOperation();
}
writeBoolean(0, vulnerable);
}
/**
@@ -63,50 +78,90 @@ public final class WrappedPacketInAbilities extends WrappedPacket {
*
* @return Whether or not the player can fly.
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public boolean isFlightAllowed() {
public boolean isFlightAllowed() throws UnsupportedOperationException {
if (v_1_16_Mode) {
throw new UnsupportedOperationException("This field does not exist on your version!");
throwUnsupportedOperation();
}
return readBoolean(2);
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public void setFlightAllowed(boolean flightAllowed) throws UnsupportedOperationException {
if (v_1_16_Mode) {
throwUnsupportedOperation();
}
writeBoolean(2, flightAllowed);
}
/**
* This will return null if the server version is not available in 1.16.x and above
*
* @return Whether or not the player can break blocks instantly.
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public boolean canInstantlyBuild() {
public boolean canInstantlyBuild() throws UnsupportedOperationException {
if (v_1_16_Mode) {
throw new UnsupportedOperationException("This field does not exist on your version!");
throwUnsupportedOperation();
}
return readBoolean(3);
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public void setCanInstantlyBuild(boolean canInstantlyBuild) throws UnsupportedOperationException {
if (v_1_16_Mode) {
throwUnsupportedOperation();
}
writeBoolean(3, canInstantlyBuild);
}
/**
* This will return null if the server version is not available in 1.16.x and above
*
* @return The speed at which the player can fly, as a float.
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public float getFlySpeed() {
public float getFlySpeed() throws UnsupportedOperationException {
if (v_1_16_Mode) {
throw new UnsupportedOperationException("This field does not exist on your version!");
throwUnsupportedOperation();
}
return readFloat(0);
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public void setFlySpeed(float flySpeed) throws UnsupportedOperationException {
if (v_1_16_Mode) {
throwUnsupportedOperation();
}
writeFloat(0, flySpeed);
}
/**
* This will return null if the server version is not available in 1.16.x and above
*
* @return The speed at which the player can walk, as a float.
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public float getWalkSpeed() {
public float getWalkSpeed() throws UnsupportedOperationException {
if (v_1_16_Mode) {
throw new UnsupportedOperationException("This field does not exist on your version!");
throwUnsupportedOperation();
}
return readFloat(1);
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
public void setWalkSpeed(float walkSpeed) throws UnsupportedOperationException {
if (v_1_16_Mode) {
throwUnsupportedOperation();
}
writeFloat(1, walkSpeed);
}
}
@@ -12,7 +12,15 @@ public class WrappedPacketInBeacon extends WrappedPacket {
return readInt(0);
}
public void setPrimaryEffect(int primaryEffect) {
writeInt(0, primaryEffect);
}
public int getSecondaryEffect() {
return readInt(1);
}
public void setSecondaryEffect(int secondaryEffect) {
writeInt(1, secondaryEffect);
}
}
@@ -27,22 +27,18 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.blockdig;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.player.Direction;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import org.jetbrains.annotations.Nullable;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketInBlockDig extends WrappedPacket {
private static Class<?> blockPositionClass;
private static Class<?> enumDirectionClass;
private static Class<?> digTypeClass;
private static Class<? extends Enum<?>> digTypeClass;
private static boolean isVersionLowerThan_v_1_8;
private Object blockPosObj;
private Object enumDirObj;
public WrappedPacketInBlockDig(NMSPacket packet) {
super(packet);
@@ -51,86 +47,47 @@ public final class WrappedPacketInBlockDig extends WrappedPacket {
@Override
protected void load() {
Class<?> blockDigClass = PacketTypeClasses.Play.Client.BLOCK_DIG;
try {
if (version.isNewerThan(ServerVersion.v_1_7_10)) {
blockPositionClass = NMSUtils.getNMSClass("BlockPosition");
enumDirectionClass = NMSUtils.getNMSClass("EnumDirection");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
isVersionLowerThan_v_1_8 = version.isOlderThan(ServerVersion.v_1_8);
if (version != ServerVersion.v_1_7_10) {
try {
digTypeClass = NMSUtils.getNMSClass("EnumPlayerDigType");
digTypeClass = NMSUtils.getNMSEnumClass("EnumPlayerDigType");
} catch (ClassNotFoundException e) {
//It is probably a subclass
digTypeClass = SubclassUtil.getSubClass(blockDigClass, "EnumPlayerDigType");
digTypeClass = SubclassUtil.getEnumSubClass(blockDigClass, "EnumPlayerDigType");
}
}
}
/**
* Get X position of the block
*
* @return Block Position X
*/
public int getX() {
public Vector3i getBlockPosition() {
int x = 0;
int y = 0;
int z = 0;
if (isVersionLowerThan_v_1_8) {
return readInt(0);
x = readInt(0);
y = readInt(1);
z = readInt(2);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPositionClass);
}
Object blockPosObj = readObject(0, NMSUtils.blockPosClass);
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj);
x = (int) NMSUtils.getBlockPosX.invoke(blockPosObj);
y = (int) NMSUtils.getBlockPosY.invoke(blockPosObj);
z = (int) NMSUtils.getBlockPosZ.invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
return new Vector3i(x, y, z);
}
/**
* Get Y position of the block
*
* @return Block Position Y
*/
public int getY() {
public void setBlockPosition(Vector3i blockPos) {
if (isVersionLowerThan_v_1_8) {
return readInt(1);
writeInt(0, blockPos.x);
writeInt(1, blockPos.y);
writeInt(2, blockPos.z);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPositionClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
}
/**
* Get Z position of the block
*
* @return Block Position Z
*/
public int getZ() {
if (isVersionLowerThan_v_1_8) {
return readInt(2);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPositionClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
Object blockPosObj = NMSUtils.generateNMSBlockPos(blockPos.x, blockPos.y, blockPos.z);
write(NMSUtils.blockPosClass, 0, blockPosObj);
}
}
@@ -139,17 +96,21 @@ public final class WrappedPacketInBlockDig extends WrappedPacket {
*
* @return Direction
*/
public byte getDirection() {
public Direction getDirection() {
if (isVersionLowerThan_v_1_8) {
return(byte) readInt(3);
return Direction.getDirection(readInt(3));
} else {
if (enumDirObj == null) {
enumDirObj = readObject(0, enumDirectionClass);
if (enumDirObj == null) {
return -1; //errorr
}
}
return Direction.getByName(((Enum) enumDirObj).name());
Enum<?> enumDir = readEnumConstant(0, NMSUtils.enumDirectionClass);
return Direction.valueOf(enumDir.name());
}
}
public void setDirection(Direction direction) {
if (isVersionLowerThan_v_1_8) {
writeInt(3, direction.getFaceValue());
} else {
Enum<?> enumConst = EnumUtil.valueOf(NMSUtils.enumDirectionClass, direction.name());
write(NMSUtils.enumDirectionClass, 0, enumConst);
}
}
@@ -162,7 +123,16 @@ public final class WrappedPacketInBlockDig extends WrappedPacket {
if (isVersionLowerThan_v_1_8) {
return PlayerDigType.values()[readInt(4)];
} else {
return PlayerDigType.valueOf(((Enum) readObject(0, digTypeClass)).name());
return PlayerDigType.valueOf(readObject(0, digTypeClass).name());
}
}
public void setDigType(PlayerDigType type) {
if (isVersionLowerThan_v_1_8) {
writeInt(4, type.ordinal());
} else {
Enum<?> enumConst = EnumUtil.valueOf(digTypeClass, type.name());
write(digTypeClass, 0, enumConst);
}
}
@@ -173,7 +143,13 @@ public final class WrappedPacketInBlockDig extends WrappedPacket {
DROP_ALL_ITEMS,
DROP_ITEM,
RELEASE_USE_ITEM,
/**
* Doesn't exist on the newest versions.
*/
@Deprecated
SWAP_HELD_ITEMS,
SWAP_ITEM_WITH_OFFHAND,
UNKNOWN
}
@@ -26,12 +26,19 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.blockplace;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.player.Direction;
import io.github.retrooper.packetevents.utils.player.Hand;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.github.retrooper.packetevents.utils.vector.Vector3f;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.inventory.ItemStack;
public final class WrappedPacketInBlockPlace extends WrappedPacket {
private static boolean isHigherThan_v_1_8_8, isHigherThan_v_1_7_10;
private static boolean newerThan_v_1_8_8, newerThan_v_1_7_10;
private static int handEnumIndex;
public WrappedPacketInBlockPlace(final NMSPacket packet) {
super(packet);
@@ -39,61 +46,127 @@ public final class WrappedPacketInBlockPlace extends WrappedPacket {
@Override
protected void load() {
isHigherThan_v_1_8_8 = version.isNewerThan(ServerVersion.v_1_8_8);
isHigherThan_v_1_7_10 = version.isNewerThan(ServerVersion.v_1_7_10);
}
public byte getDirection() {
if (isHigherThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet);
return Direction.getByName(((Enum) blockPlace_1_9.getEnumDirectionObject()).name());
} else if (isHigherThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return (byte) blockPlace_1_8.getFace();
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return (byte) blockPlace_1_7_10.face;
newerThan_v_1_7_10 = version.isNewerThan(ServerVersion.v_1_7_10);
newerThan_v_1_8_8 = version.isNewerThan(ServerVersion.v_1_8_8);
try {
Object handEnum = readObject(1, NMSUtils.enumHandClass);
handEnumIndex = 1;
} catch (Exception ex) {
handEnumIndex = 0;//Most likely a newer version
}
}
public int getX() {
if (isHigherThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet);
return blockPlace_1_9.getX();
} else if (isHigherThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return blockPlace_1_8.getX();
public Hand getHand() {
if (version.isOlderThan(ServerVersion.v_1_9)) {
return Hand.MAIN_HAND;
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return blockPlace_1_7_10.x;
Enum<?> enumConst = read(handEnumIndex, NMSUtils.enumHandClass);
return Hand.valueOf(enumConst.name());
}
}
public int getY() {
if (isHigherThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet);
return blockPlace_1_9.getY();
} else if (isHigherThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return blockPlace_1_8.getY();
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return blockPlace_1_7_10.y;
public void setHand(Hand hand) {
//Optimize to do nothing on legacy versions. The protocol of the legacy versions only support one hand, the main hand.
if (version.isNewerThan(ServerVersion.v_1_8_8)) {
Enum<?> enumConst = EnumUtil.valueOf(NMSUtils.enumHandClass, hand.name());
write(NMSUtils.enumHandClass, handEnumIndex, enumConst);
}
}
public int getZ() {
if (isHigherThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet);
return blockPlace_1_9.getZ();
} else if (isHigherThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return blockPlace_1_8.getZ();
public Direction getDirection() {
if (newerThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(new NMSPacket(packet.getRawNMSPacket()));
return blockPlace_1_9.getDirection();
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(new NMSPacket(packet.getRawNMSPacket()));
return Direction.getDirection(blockPlace_1_8.getFace());
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return blockPlace_1_7_10.z;
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(new NMSPacket(packet.getRawNMSPacket()));
return Direction.getDirection(blockPlace_1_7_10.getFace());
}
}
public void setDirection(Direction direction) {
if (newerThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(new NMSPacket(packet.getRawNMSPacket()));
blockPlace_1_9.setDirection(direction);
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(new NMSPacket(packet.getRawNMSPacket()));
blockPlace_1_8.setFace(direction.getFaceValue());
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(new NMSPacket(packet.getRawNMSPacket()));
blockPlace_1_7_10.setFace(direction.getFaceValue());
}
}
public Vector3i getBlockPosition() {
Vector3i blockPos = new Vector3i();
if (newerThan_v_1_8_8) {
WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet);
blockPos = blockPlace_1_9.getBlockPosition();
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
blockPos = blockPlace_1_8.getBlockPosition();
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
blockPos = blockPlace_1_7_10.getBlockPosition();
}
return blockPos;
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_8_8})
@Deprecated
public Vector3f getCursorPosition() throws UnsupportedOperationException {
if (newerThan_v_1_8_8) {
throwUnsupportedOperation();
return Vector3f.INVALID;
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return blockPlace_1_8.getCursorPosition();
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return blockPlace_1_7_10.getCursorPosition();
}
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_8_8})
@Deprecated
public void setCursorPosition(Vector3f cursorPos) throws UnsupportedOperationException {
if (newerThan_v_1_8_8) {
throwUnsupportedOperation();
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
blockPlace_1_8.setCursorPosition(cursorPos);
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
blockPlace_1_7_10.setCursorPosition(cursorPos);
}
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_8_8})
public ItemStack getItemStack() throws UnsupportedOperationException {
if (newerThan_v_1_8_8) {
throwUnsupportedOperation();
return null;
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
return blockPlace_1_8.getItemStack();
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
return blockPlace_1_7_10.getItemStack();
}
}
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_8_8})
public void setItemStack(ItemStack stack) throws UnsupportedOperationException {
if (newerThan_v_1_8_8) {
throwUnsupportedOperation();
} else if (newerThan_v_1_7_10) {
WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet);
blockPlace_1_8.setItemStack(stack);
} else {
WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet);
blockPlace_1_7_10.setItemStack(stack);
}
}
}
@@ -26,25 +26,67 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.blockplace;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.vector.Vector3f;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.inventory.ItemStack;
final class WrappedPacketInBlockPlace_1_7_10 extends WrappedPacket {
public int x, y, z;
public ItemStack itemStack;
public int face;
private final net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace blockPlace;
WrappedPacketInBlockPlace_1_7_10(final NMSPacket packet) {
super(packet);
final net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace blockPlace =
blockPlace =
(net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace) packet.getRawNMSPacket();
}
x = blockPlace.c();
y = blockPlace.d();
z = blockPlace.e();
public Vector3i getBlockPosition() {
return new Vector3i(blockPlace.c(), blockPlace.d(), blockPlace.e());
}
this.face = blockPlace.d();
public void setBlockPosition(Vector3i blockPos) {
Vector3i currentBlockPos = getBlockPosition();
if (blockPos.x != currentBlockPos.x) {
writeInt(0, blockPos.x);
}
if (blockPos.y != currentBlockPos.y) {
writeInt(1, blockPos.y);
}
if (blockPos.z != currentBlockPos.z) {
writeInt(2, blockPos.z);
}
}
net.minecraft.server.v1_7_R4.ItemStack stack = blockPlace.getItemStack();
this.itemStack = org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asBukkitCopy(stack);
public int getFace() {
return blockPlace.getFace();
}
public void setFace(int face) {
writeInt(3, face);
}
public ItemStack getItemStack() {
return org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asBukkitCopy(blockPlace.getItemStack());
}
public void setItemStack(ItemStack stack) {
Object nmsItemStack = org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asNMSCopy(stack);
writeObject(0, nmsItemStack);
}
public Vector3f getCursorPosition() {
return new Vector3f(blockPlace.h(), blockPlace.i(), blockPlace.j());
}
public void setCursorPosition(Vector3f cursorPos) {
Vector3f currentCursorPos = getCursorPosition();
if (cursorPos.x != currentCursorPos.x) {
writeFloat(0, cursorPos.x);
}
if (cursorPos.y != currentCursorPos.y) {
writeFloat(1, cursorPos.y);
}
if (cursorPos.z != currentCursorPos.z) {
writeFloat(2, cursorPos.z);
}
}
}
@@ -27,60 +27,61 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.blockplace;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.vector.Vector3f;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.InvocationTargetException;
final class WrappedPacketInBlockPlace_1_8 extends WrappedPacket {
private Object blockPosObj;
WrappedPacketInBlockPlace_1_8(final NMSPacket packet) {
super(packet);
}
public int getX() {
if (blockPosObj == null) {
blockPosObj = readObject(1, NMSUtils.blockPosClass);
}
public Vector3i getBlockPosition() {
Vector3i blockPos = new Vector3i();
Object blockPosObj = readObject(1, NMSUtils.blockPosClass);
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj);
blockPos.x = (int) NMSUtils.getBlockPosX.invoke(blockPosObj);
blockPos.y = (int) NMSUtils.getBlockPosY.invoke(blockPosObj);
blockPos.z = (int) NMSUtils.getBlockPosZ.invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
return blockPos;
}
public int getY() {
if (blockPosObj == null) {
blockPosObj = readObject(1, NMSUtils.blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
public int getZ() {
if (blockPosObj == null) {
blockPosObj = readObject(1, NMSUtils.blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
public void setBlockPosition(Vector3i blockPos) {
Object blockPosObj = NMSUtils.generateNMSBlockPos(blockPos.x, blockPos.y, blockPos.z);
write(NMSUtils.blockPosClass, 1, blockPosObj);
}
public ItemStack getItemStack() {
return NMSUtils.toBukkitItemStack(readObject(0, NMSUtils.nmsItemStackClass));
}
public void setItemStack(ItemStack stack) {
Object nmsItemStack = NMSUtils.toNMSItemStack(stack);
write(NMSUtils.nmsItemStackClass, 0, nmsItemStack);
}
public int getFace() {
return readInt(0);
}
public void setFace(int face) {
writeInt(0, face);
}
public Vector3f getCursorPosition() {
return new Vector3f(readFloat(0), readFloat(1), readFloat(2));
}
public void setCursorPosition(Vector3f cursorPos) {
writeFloat(0, cursorPos.x);
writeFloat(1, cursorPos.y);
writeFloat(2, cursorPos.z);
}
}
@@ -27,14 +27,16 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.blockplace;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.player.Direction;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import java.lang.reflect.InvocationTargetException;
final class WrappedPacketInBlockPlace_1_9 extends WrappedPacket {
private static Class<?> movingObjectPositionBlockClass;
private Object blockPosObj;
private Object movingObjPos;
public WrappedPacketInBlockPlace_1_9(final NMSPacket packet) {
super(packet);
@@ -45,67 +47,65 @@ final class WrappedPacketInBlockPlace_1_9 extends WrappedPacket {
movingObjectPositionBlockClass = NMSUtils.getNMSClassWithoutException("MovingObjectPositionBlock");
}
public int getX() {
if (blockPosObj == null) {
if (movingObjectPositionBlockClass == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
} else {
Object movingObjectPos = readObject(0, movingObjectPositionBlockClass);
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjectPos));
blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass);
}
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
public int getY() {
if (blockPosObj == null) {
if (movingObjectPositionBlockClass == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
} else {
Object movingObjectPos = readObject(0, movingObjectPositionBlockClass);
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjectPos));
blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass);
}
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
public int getZ() {
if (blockPosObj == null) {
if (movingObjectPositionBlockClass == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
} else {
Object movingObjectPos = readObject(0, movingObjectPositionBlockClass);
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjectPos));
blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass);
}
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
public Object getEnumDirectionObject() {
public Vector3i getBlockPosition() {
Vector3i blockPos = new Vector3i();
Object blockPosObj;
if (movingObjectPositionBlockClass == null) {
return readObject(0, NMSUtils.enumDirectionClass);
blockPosObj = readObject(0, NMSUtils.blockPosClass);
} else {
Object movingObjectPos = readObject(0, movingObjectPositionBlockClass);
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjectPos));
return movingObjectPosWrapper.readObject(0, NMSUtils.enumDirectionClass);
if (movingObjPos == null) {
movingObjPos = readObject(0, movingObjectPositionBlockClass);
}
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjPos));
blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass);
}
try {
blockPos.x = (int) NMSUtils.getBlockPosX.invoke(blockPosObj);
blockPos.y = (int) NMSUtils.getBlockPosY.invoke(blockPosObj);
blockPos.z = (int) NMSUtils.getBlockPosZ.invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return blockPos;
}
public void setBlockPosition(Vector3i blockPos) {
Object blockPosObj = NMSUtils.generateNMSBlockPos(blockPos.x, blockPos.y, blockPos.z);
if (movingObjectPositionBlockClass == null) {
write(NMSUtils.blockPosClass, 0, blockPosObj);
} else {
if (movingObjPos == null) {
movingObjPos = readObject(0, movingObjectPositionBlockClass);
}
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjPos));
movingObjectPosWrapper.write(NMSUtils.blockPosClass, 0, blockPos);
}
}
public Direction getDirection() {
Enum<?> enumConst;
if (movingObjectPositionBlockClass == null) {
enumConst = readEnumConstant(0, NMSUtils.enumDirectionClass);
} else {
if (movingObjPos == null) {
movingObjPos = readObject(0, movingObjectPositionBlockClass);
}
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjPos));
enumConst = movingObjectPosWrapper.readEnumConstant(0, NMSUtils.enumDirectionClass);
}
return Direction.valueOf(enumConst.name());
}
public void setDirection(Direction direction) {
Enum<?> enumConst = EnumUtil.valueOf(NMSUtils.enumDirectionClass, direction.name());
if (movingObjectPositionBlockClass == null) {
writeEnumConstant(0, enumConst);
} else {
if (movingObjPos == null) {
movingObjPos = readObject(0, movingObjectPositionBlockClass);
}
WrappedPacket movingObjectPosWrapper = new WrappedPacket(new NMSPacket(movingObjPos));
movingObjectPosWrapper.writeEnumConstant(0, enumConst);
}
}
}
@@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.play.in.boatmove;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
/**
* Wrapper for the BoatMove packet.
*
* @author Tecnio
* @since 1.8
*/
public final class WrappedPacketInBoatMove extends WrappedPacket {
public WrappedPacketInBoatMove(NMSPacket packet) {
super(packet);
}
public boolean getLeftPaddle() {
return readBoolean(0);
}
public void setLeftPaddle(boolean turning) {
writeBoolean(0, turning);
}
public boolean getRightPaddle() {
return readBoolean(1);
}
public void setRightPaddle(boolean turning) {
writeBoolean(1, turning);
}
@Override
public boolean isSupported() {
return version.isNewerThan(ServerVersion.v_1_8);
}
}
@@ -32,12 +32,11 @@ public final class WrappedPacketInChat extends WrappedPacket {
super(packet);
}
/**
* Get the message.
*
* @return Chat Message
*/
public String getMessage() {
return readString(0);
}
public void setMessage(String message) {
writeString(0, message);
}
}
@@ -27,12 +27,13 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.clientcommand;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
public final class WrappedPacketInClientCommand extends WrappedPacket {
private static Class<?> enumClientCommandClass;
private Object enumObj;
private static Class<? extends Enum<?>> enumClientCommandClass;
public WrappedPacketInClientCommand(NMSPacket packet) {
super(packet);
@@ -42,30 +43,34 @@ public final class WrappedPacketInClientCommand extends WrappedPacket {
protected void load() {
Class<?> packetClass = PacketTypeClasses.Play.Client.CLIENT_COMMAND;
try {
enumClientCommandClass = NMSUtils.getNMSClass("EnumClientCommand");
enumClientCommandClass = NMSUtils.getNMSEnumClass("EnumClientCommand");
} catch (ClassNotFoundException e) {
//Probably a subclass
enumClientCommandClass = SubclassUtil.getSubClass(packetClass, "EnumClientCommand");
enumClientCommandClass = SubclassUtil.getEnumSubClass(packetClass, "EnumClientCommand");
}
}
/**
* Get the Client Command enum sent in the packet
*
* @return ClientCommand
*/
public ClientCommand getClientCommand() {
if (enumObj == null) {
enumObj = readObject(0, enumClientCommandClass);
Enum<?> emumConst = readEnumConstant(0, enumClientCommandClass);
return ClientCommand.valueOf(emumConst.name());
}
public void setClientCommand(ClientCommand command) throws UnsupportedOperationException {
if (command == ClientCommand.OPEN_INVENTORY_ACHIEVEMENT && version.isNewerThan(ServerVersion.v_1_16)) {
throwUnsupportedOperation(command);
}
return ClientCommand.valueOf(enumObj.toString());
Enum<?> enumConst = EnumUtil.valueOf(enumClientCommandClass, command.name());
writeEnumConstant(0, enumConst);
}
public enum ClientCommand {
PERFORM_RESPAWN,
REQUEST_STATS,
//This field doesn't exist on 1.16
//This enum constant only exists on 1.7.10 -> 1.15.2
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_15_2})
@Deprecated
OPEN_INVENTORY_ACHIEVEMENT
}
@@ -36,4 +36,8 @@ public class WrappedPacketInCloseWindow extends WrappedPacket {
public int getWindowId() {
return readInt(0);
}
public void setWindowId(int windowID) {
writeInt(0, windowID);
}
}
@@ -62,16 +62,37 @@ public final class WrappedPacketInCustomPayload extends WrappedPacket {
}
}
public void setTag(String tag) {
if (strPresent) {
writeString(0, tag);
} else {
Object minecraftKey = NMSUtils.generateMinecraftKey(tag);
write(NMSUtils.minecraftKeyClass, 0, minecraftKey);
}
}
public byte[] getData() {
if (byteArrayPresent) {
return readByteArray(0);
} else {
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket byteBufWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
WrappedPacket dataSerializerWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
Object byteBuf = byteBufWrapper.readObject(0, byteBufClass);
Object byteBuf = dataSerializerWrapper.readObject(0, byteBufClass);
return PacketEvents.get().getByteBufUtil().getBytes(byteBuf);
}
}
public void setData(byte[] data) {
if (byteArrayPresent) {
writeByteArray(0, data);
} else {
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket dataSerializerWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
Object byteBuf = dataSerializerWrapper.readObject(0, byteBufClass);
PacketEvents.get().getByteBufUtil().setBytes(byteBuf, data);
}
}
}
@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.play.in.difficultychange;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
/**
* Wrapper for the DifficultyChange packet.
*
* @author Tecnio
* @since 1.8
*/
public final class WrappedPacketInDifficultyChange extends WrappedPacket {
private static Class<? extends Enum<?>> enumDifficultyClass;
public WrappedPacketInDifficultyChange(NMSPacket packet) {
super(packet);
}
@Override
protected void load() {
try {
enumDifficultyClass = NMSUtils.getNMSEnumClass("EnumDifficulty");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Difficulty getDifficulty() {
Enum<?> enumConstant = readEnumConstant(0, enumDifficultyClass);
return Difficulty.values()[enumConstant.ordinal()];
}
public void setDifficulty(Difficulty difficulty) {
Enum<?> enumConstant = EnumUtil.valueByIndex(enumDifficultyClass, difficulty.ordinal());
writeEnumConstant(0, enumConstant);
}
public enum Difficulty {
PEACEFUL,
EASY,
NORMAL,
HARD
}
}
@@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.play.in.difficultylock;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
/**
* Wrapper for the DifficultyLock packet.
*
* @author Tecnio
* @since 1.8
*/
public final class WrappedPacketInDifficultyLock extends WrappedPacket {
public WrappedPacketInDifficultyLock(NMSPacket packet) {
super(packet);
}
public boolean isLocked() {
return readBoolean(0);
}
public void setLocked(boolean locked) {
writeBoolean(0, locked);
}
@Override
public boolean isSupported() {
return version.isNewerThan(ServerVersion.v_1_15_2);
}
}
@@ -36,7 +36,15 @@ public class WrappedPacketInEnchantItem extends WrappedPacket {
return readInt(0);
}
public void setWindowId(int windowID) {
writeInt(0, windowID);
}
public int getButton() {
return readInt(1);
}
public void setButton(int button) {
writeInt(1, button);
}
}
@@ -27,20 +27,20 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.entityaction;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import java.util.HashMap;
import org.jetbrains.annotations.Nullable;
public final class WrappedPacketInEntityAction extends WrappedPacket {
private static final HashMap<String, PlayerAction> cachedPlayerActionNames = new HashMap<>();
private static final HashMap<Integer, PlayerAction> cachedPlayerActionIDs = new HashMap<>();
private static Class<?> enumPlayerActionClass;
private static boolean isLowerThan_v_1_8;
private static Class<? extends Enum<?>> enumPlayerActionClass;
private static boolean olderThan_v_1_8;
private static boolean newerThan_v_1_8_8;
private Entity entity;
private int entityId = -1;
private int entityID = -1;
public WrappedPacketInEntityAction(final NMSPacket packet) {
super(packet);
@@ -48,94 +48,154 @@ public final class WrappedPacketInEntityAction extends WrappedPacket {
@Override
protected void load() {
isLowerThan_v_1_8 = version.isOlderThan(ServerVersion.v_1_8);
if (!isLowerThan_v_1_8) {
enumPlayerActionClass = SubclassUtil.getSubClass(PacketTypeClasses.Play.Client.ENTITY_ACTION, "EnumPlayerAction");
olderThan_v_1_8 = version.isOlderThan(ServerVersion.v_1_8);
newerThan_v_1_8_8 = version.isNewerThan(ServerVersion.v_1_8_8);
if (!olderThan_v_1_8) {
enumPlayerActionClass = SubclassUtil.getEnumSubClass(PacketTypeClasses.Play.Client.ENTITY_ACTION, "EnumPlayerAction");
}
//All the already existing values
for (PlayerAction val : PlayerAction.values()) {
cachedPlayerActionNames.put(val.name(), val);
}
cachedPlayerActionNames.put("PRESS_SHIFT_KEY", PlayerAction.START_SNEAKING);
cachedPlayerActionNames.put("RELEASE_SHIFT_KEY", PlayerAction.STOP_SNEAKING);
cachedPlayerActionNames.put("RIDING_JUMP", PlayerAction.START_RIDING_JUMP);
cachedPlayerActionIDs.put(1, PlayerAction.START_SNEAKING);
cachedPlayerActionIDs.put(2, PlayerAction.STOP_SNEAKING);
cachedPlayerActionIDs.put(3, PlayerAction.STOP_SLEEPING);
cachedPlayerActionIDs.put(4, PlayerAction.START_SPRINTING);
cachedPlayerActionIDs.put(5, PlayerAction.STOP_SPRINTING);
cachedPlayerActionIDs.put(6, PlayerAction.START_RIDING_JUMP); //riding jump
cachedPlayerActionIDs.put(7, PlayerAction.OPEN_INVENTORY); //horse interaction
}
/**
* Lookup the associated entity by the ID that was sent in the packet.
*
* @return Entity
*/
@Nullable
public Entity getEntity() {
return getEntity(null);
}
@Nullable
public Entity getEntity(@Nullable World world) {
if (entity == null) {
return entity = NMSUtils.getEntityById(getEntityId());
entity = NMSUtils.getEntityById(world, getEntityId());
}
return entity;
}
/**
* Get the ID of the entity.
* If you do not want to use {@link #getEntity()},
* you lookup the entity by yourself with this entity ID.
*
* @return Entity ID
*/
public void setEntity(Entity entity) {
setEntityId(entity.getEntityId());
this.entity = entity;
}
public int getEntityId() {
if (entityId == -1) {
entityId = readInt(0);
if (entityID == -1) {
entityID = readInt(0);
}
return entityId;
return entityID;
}
public void setEntityId(int entityID) {
writeInt(0, this.entityID = entityID);
this.entity = null;
}
/**
* Get the player action.
*
* @return Player Action
*/
public PlayerAction getAction() {
if (isLowerThan_v_1_8) {
int animationIndex = readInt(1);
return cachedPlayerActionIDs.get(animationIndex);
if (olderThan_v_1_8) {
int animationIndex = readInt(1) - 1;
return PlayerAction.getByActionValue((byte) (animationIndex));
} else {
final Object enumObj = readObject(0, enumPlayerActionClass);
final String enumValueName = enumObj.toString();
return cachedPlayerActionNames.get(enumValueName);
Enum<?> enumConst = readEnumConstant(0, enumPlayerActionClass);
return PlayerAction.getByName(enumConst.name());
}
}
public void setAction(PlayerAction action) throws UnsupportedOperationException {
if (olderThan_v_1_8) {
byte animationIndex = action.actionID;
writeInt(1, animationIndex + 1);
} else {
Enum<?> enumConst;
if (newerThan_v_1_8_8) {
if (action == PlayerAction.RIDING_JUMP) {
throwUnsupportedOperation(action);
}
enumConst = EnumUtil.valueByIndex(enumPlayerActionClass, action.ordinal());
} else {
enumConst = EnumUtil.valueOf(enumPlayerActionClass, action.name());
if (enumConst == null) {
enumConst = EnumUtil.valueOf(enumPlayerActionClass, action.alias);
}
}
writeEnumConstant(0, enumConst);
}
}
/**
* Get the jump boost integer.
*
* @return Jump Boost
*/
public int getJumpBoost() {
if (isLowerThan_v_1_8) {
if (olderThan_v_1_8) {
return readInt(2);
} else {
return readInt(1);
}
}
public void setJumpBoost(int jumpBoost) {
if (olderThan_v_1_8) {
writeInt(2, jumpBoost);
} else {
writeInt(1, jumpBoost);
}
}
public enum PlayerAction {
START_SNEAKING,
STOP_SNEAKING,
STOP_SLEEPING,
START_SPRINTING,
STOP_SPRINTING,
START_RIDING_JUMP,
STOP_RIDING_JUMP,
OPEN_INVENTORY,
START_FALL_FLYING
START_SNEAKING((byte) 0, "PRESS_SHIFT_KEY"),
STOP_SNEAKING((byte) 1, "RELEASE_SHIFT_KEY"),
STOP_SLEEPING((byte) 2),
START_SPRINTING((byte) 3),
STOP_SPRINTING((byte) 4),
START_RIDING_JUMP((byte) 5),
STOP_RIDING_JUMP((byte) 6),
OPEN_INVENTORY((byte) 7),
START_FALL_FLYING((byte) 8),
/**
* Removed in minecraft server version 1.9.
* This constant will only work on 1.7.10-1.8
*/
@SupportedVersions(ranges = {ServerVersion.v_1_7_10, ServerVersion.v_1_8_8})
@Deprecated
RIDING_JUMP((byte) 5);
final byte actionID;
final String alias;
PlayerAction(byte actionID) {
this.actionID = actionID;
this.alias = "empty";
}
PlayerAction(byte actionID, String alias) {
this.actionID = actionID;
this.alias = alias;
}
@Nullable
public static PlayerAction getByActionValue(byte value) {
if (version.isOlderThan(ServerVersion.v_1_9)) {
if (value == RIDING_JUMP.actionID) {
return RIDING_JUMP;
} else {
for (PlayerAction action : values()) {
if (action.actionID == value) {
return action;
}
}
}
} else {
return values()[value];
}
return null;
}
@Nullable
public static PlayerAction getByName(String name) {
for (PlayerAction action : values()) {
if (action.name().equals(name) || action.alias.equals(name)) {
return action;
}
}
return null;
}
public byte getActionValue() {
return actionID;
}
}
}
@@ -37,31 +37,63 @@ public class WrappedPacketInFlying extends WrappedPacket {
return readDouble(0);
}
public void setX(double x) {
writeDouble(0, x);
}
public double getY() {
return readDouble(1);
}
public void setY(double y) {
writeDouble(1, y);
}
public double getZ() {
return readDouble(2);
}
public void setZ(double z) {
writeDouble(2, z);
}
public float getYaw() {
return readFloat(0);
}
public void setYaw(float yaw) {
writeFloat(0, yaw);
}
public float getPitch() {
return readFloat(1);
}
public void setPitch(float pitch) {
writeFloat(1, pitch);
}
public boolean isOnGround() {
return readBoolean(0);
}
public final boolean isPosition() {
public void setOnGround(boolean onGround) {
writeBoolean(0, onGround);
}
public boolean isPosition() {
return readBoolean(1);
}
public final boolean isLook() {
public void setIsPosition(boolean isPosition) {
writeBoolean(1, isPosition);
}
public boolean isLook() {
return readBoolean(2);
}
public void setIsLook(boolean isLook) {
writeBoolean(2, isLook);
}
}
@@ -32,13 +32,11 @@ public final class WrappedPacketInHeldItemSlot extends WrappedPacket {
super(packet);
}
/**
* Get the index of the item we currently have in hand.
*
* @return Item in hand Index
*/
public int getCurrentSelectedSlot() {
return readInt(0);
}
public void setCurrentSelectedSlot(int slot) {
writeInt(0, slot);
}
}
@@ -11,4 +11,8 @@ public class WrappedPacketInItemName extends WrappedPacket {
public String getItemName() {
return readString(0);
}
public void setItemName(String itemName) {
writeString(0, itemName);
}
}
@@ -42,14 +42,6 @@ public final class WrappedPacketInKeepAlive extends WrappedPacket {
integerPresentInIndex0 = Reflection.getField(packetClass, int.class, 0) != null;
}
/**
* Get the ID response from the client.
* <p>
* You may cast this down to an int if you are on 1.7.10 - 1.12.2.
* On 1.13.2 - 1.16.3 a long is sent.
*
* @return response ID
*/
public long getId() {
if (!integerPresentInIndex0) {
return readLong(0);
@@ -57,4 +49,15 @@ public final class WrappedPacketInKeepAlive extends WrappedPacket {
return readInt(0);
}
}
public void setId(long id) throws UnsupportedOperationException {
if (!integerPresentInIndex0) {
writeLong(0, id);
} else {
if (id > Integer.MAX_VALUE || id < Integer.MAX_VALUE) {
throw new UnsupportedOperationException("PacketEvents failed to set the Keep Alive ID in WrappedPacketInKeepAlive. Your server version does not support IDs outside the range of an int primitive type. Your Keep Alive ID seems to be in the range of a long primitive type.");
}
writeInt(0, (int) id);
}
}
}
@@ -11,4 +11,8 @@ public class WrappedPacketInPickItem extends WrappedPacket {
public int getSlot() {
return readInt(0);
}
public void setSlot(int slot) {
writeInt(0, slot);
}
}
@@ -27,11 +27,12 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.resourcepackstat
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
public class WrappedPacketInResourcePackStatus extends WrappedPacket {
private static Class<?> enumResourcePackStatusClass;
private static Class<? extends Enum<?>> enumResourcePackStatusClass;
public WrappedPacketInResourcePackStatus(NMSPacket packet) {
super(packet);
@@ -39,12 +40,22 @@ public class WrappedPacketInResourcePackStatus extends WrappedPacket {
@Override
protected void load() {
enumResourcePackStatusClass = SubclassUtil.getSubClass(PacketTypeClasses.Play.Client.RESOURCE_PACK_STATUS, "EnumResourcePackStatus");
enumResourcePackStatusClass = SubclassUtil.getEnumSubClass(PacketTypeClasses.Play.Client.RESOURCE_PACK_STATUS, "EnumResourcePackStatus");
}
public ResourcePackStatus getStatus() {
Object enumObj = readObject(0, enumResourcePackStatusClass);
return ResourcePackStatus.valueOf(enumObj.toString());
Enum<?> enumConst = readEnumConstant(0, enumResourcePackStatusClass);
return ResourcePackStatus.valueOf(enumConst.name());
}
public void setStatus(ResourcePackStatus status) {
Enum<?> enumConst = EnumUtil.valueOf(enumResourcePackStatusClass, status.name());
writeEnumConstant(0, enumConst);
}
@Override
public boolean isSupported() {
return version.isNewerThan(ServerVersion.v_1_7_10);
}
public enum ResourcePackStatus {
@@ -53,9 +64,4 @@ public class WrappedPacketInResourcePackStatus extends WrappedPacket {
FAILED_DOWNLOAD,
ACCEPTED
}
@Override
public boolean isSupported() {
return version.isNewerThan(ServerVersion.v_1_7_10);
}
}
@@ -35,35 +35,21 @@ public class WrappedPacketInSetCreativeSlot extends WrappedPacket {
super(packet);
}
/**
* Get the clicked slot
*
* @return the slot
*/
public int getSlot() {
return readInt(0);
}
/**
* Set the clicked slot
*
* @param value the slot
*/
public void setSlot(int value) {
writeInt(0, value);
}
/**
* Get the clicked item
*
* @return the clicked item
*/
public ItemStack getClickedItem() {
Object nmsItemStack = readObject(0, NMSUtils.nmsItemStackClass);
return NMSUtils.toBukkitItemStack(nmsItemStack);
}
public void setClickedItem(ItemStack stack) {
write(NMSUtils.nmsItemStackClass, 0, NMSUtils.toNMSItemStack(stack));
Object nmsItemStack = NMSUtils.toNMSItemStack(stack);
write(NMSUtils.nmsItemStackClass, 0, nmsItemStack);
}
}
@@ -26,18 +26,19 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.settings;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class WrappedPacketInSettings extends WrappedPacket {
private static Class<?> chatVisibilityEnumClass;
private static Class<? extends Enum<?>> chatVisibilityEnumClass;
private static boolean isLowerThan_v_1_8;
private Object chatVisibilityEnumObj;
private HashMap<DisplayedSkinPart, Boolean> displayedSkinParts = new HashMap<>();
public WrappedPacketInSettings(final NMSPacket packet) {
super(packet);
@@ -45,126 +46,116 @@ public class WrappedPacketInSettings extends WrappedPacket {
@Override
protected void load() {
isLowerThan_v_1_8 = version.isOlderThan(ServerVersion.v_1_8);
try {
chatVisibilityEnumClass = NMSUtils.getNMSClass("EnumChatVisibility");
chatVisibilityEnumClass = NMSUtils.getNMSEnumClass("EnumChatVisibility");
} catch (ClassNotFoundException e) {
Class<?> entityHumanClass = NMSUtils.getNMSClassWithoutException("EntityHuman");
//They are just on an outdated version
assert entityHumanClass != null;
chatVisibilityEnumClass = SubclassUtil.getSubClass(entityHumanClass, "EnumChatVisibility");
chatVisibilityEnumClass = SubclassUtil.getEnumSubClass(entityHumanClass, "EnumChatVisibility");
}
}
/**
* Get language client setting
*
* @return String Locale
*/
public String getLocale() {
return readString(0);
}
/**
* Get client view distance.
*
* @return View Distance
*/
public void setLocale(String locale) {
writeString(0, locale);
}
public int getViewDistance() {
return readInt(0);
}
/**
* Get Chat Visibility
*
* @return Chat Visibility
*/
public ChatVisibility getChatVisibility() {
if (chatVisibilityEnumObj == null) {
chatVisibilityEnumObj = readObject(0, chatVisibilityEnumClass);
}
String enumValueAsString = chatVisibilityEnumObj.toString();
if (enumValueAsString.equals("FULL")) {
return ChatVisibility.ENABLED;
} else if (enumValueAsString.equals("SYSTEM")) {
return ChatVisibility.COMMANDS_ONLY;
} else {
return ChatVisibility.HIDDEN;
}
public void setViewDistance(int viewDistance) {
writeInt(0, viewDistance);
}
/**
* Is chat colors
*
* @return Chat Colors
*/
public boolean isChatColors() {
public ChatVisibility getChatVisibility() {
Enum<?> enumConst = readEnumConstant(0, chatVisibilityEnumClass);
return ChatVisibility.valueOf(enumConst.name());
}
public void setChatVisibility(ChatVisibility visibility) {
Enum<?> enumConst = EnumUtil.valueByIndex(chatVisibilityEnumClass, visibility.ordinal()); //Ordinal is faster than name comparison.
writeEnumConstant(0, enumConst);
}
public boolean isChatColored() {
return readBoolean(0);
}
/**
* Get Displayed skin parts.
* <p>
* It is possible for some keys to not exist.
* If that is the case, the server version is 1.7.10.
* 1.7.10 only sends the cape skin part.
*
* @return A map with a Skin Parts as a key, and a boolean as a value.
*/
public HashMap<DisplayedSkinPart, Boolean> getDisplayedSkinPartsMap() {
if (displayedSkinParts.isEmpty()) {
if (isLowerThan_v_1_8) {
//in 1.7.10 only the cape display skin part is sent
boolean capeEnabled = readBoolean(1);
displayedSkinParts.put(DisplayedSkinPart.CAPE, capeEnabled);
} else {
//in 1.8, all the skin parts are sent
int skinPartFlags = readInt(1);
displayedSkinParts.put(DisplayedSkinPart.CAPE, (skinPartFlags & 0x01) != 0);
displayedSkinParts.put(DisplayedSkinPart.JACKET, (skinPartFlags & 0x02) != 0);
displayedSkinParts.put(DisplayedSkinPart.LEFT_SLEEVE, (skinPartFlags & 0x04) != 0);
displayedSkinParts.put(DisplayedSkinPart.RIGHT_SLEEVE, (skinPartFlags & 0x08) != 0);
displayedSkinParts.put(DisplayedSkinPart.LEFT_PANTS, (skinPartFlags & 0x10) != 0);
displayedSkinParts.put(DisplayedSkinPart.RIGHT_PANTS, (skinPartFlags & 0x20) != 0);
displayedSkinParts.put(DisplayedSkinPart.HAT, (skinPartFlags & 0x40) != 0);
public void setChatColored(boolean chatColors) {
writeBoolean(0, chatColors);
}
public byte getDisplaySkinPartsMask() {
byte mask = 0;
if (isLowerThan_v_1_8) {
boolean capeEnabled = readBoolean(1);
if (capeEnabled) {
mask |= 0x01;
}
} else {
mask = (byte) readInt(1);
}
return mask;
}
public void setDisplaySkinPartsMask(byte mask) {
if (isLowerThan_v_1_8) {
boolean capeEnabled = (mask & 0x01) == 0x01;
writeBoolean(1, capeEnabled);
} else {
writeInt(1, mask);
}
}
public Set<DisplayedSkinPart> getDisplayedSkinParts() {
Set<DisplayedSkinPart> displayedSkinParts = new HashSet<>();
byte mask = getDisplaySkinPartsMask();
for (DisplayedSkinPart part : DisplayedSkinPart.values()) {
if ((mask & part.maskFlag) == part.maskFlag) {
displayedSkinParts.add(part);
}
}
return displayedSkinParts;
}
/**
* Is the skin part enabled.
* <p>
* On 1.7.10, some skin parts will default to 'false' as 1.7.10
* only sends the 'cape' skin part.
*
* @param part The skin part to check the status of.
* @return Is the skin part enabled
*/
public boolean isDisplaySkinPartEnabled(DisplayedSkinPart part) {
if (displayedSkinParts.isEmpty()) {
displayedSkinParts = getDisplayedSkinPartsMap();
public void setDisplayedSkinParts(Set<DisplayedSkinPart> displayedSkinParts) {
byte mask = 0;
for (DisplayedSkinPart part : displayedSkinParts) {
mask |= part.maskFlag;
}
//1.7.10, we will default the other skin parts to return false.
if (!displayedSkinParts.containsKey(part)) {
return false;
}
return displayedSkinParts.get(part);
setDisplaySkinPartsMask(mask);
}
/**
* Enum for the client chat visibility setting
*/
public enum ChatVisibility {
ENABLED, COMMANDS_ONLY, HIDDEN
FULL, SYSTEM, HIDDEN
}
/**
* Enum for the client displayed skin parts settings
*/
public enum DisplayedSkinPart {
CAPE, JACKET, LEFT_SLEEVE, RIGHT_SLEEVE, LEFT_PANTS, RIGHT_PANTS, HAT
CAPE(0x01),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
JACKET(0x02),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
LEFT_SLEEVE(0x04),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
RIGHT_SLEEVE(0x08),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
LEFT_PANTS(0x10),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
RIGHT_PANTS(0x20),
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
HAT(0x40);
final byte maskFlag;
DisplayedSkinPart(int maskFlag) {
this.maskFlag = (byte) maskFlag;
}
}
}
@@ -35,6 +35,10 @@ public class WrappedPacketInSpectate extends WrappedPacket {
}
public UUID getUUID() {
return (UUID) readObject(0, UUID.class);
return readObject(0, UUID.class);
}
public void setUUID(UUID uuid) {
writeObject(0, uuid);
}
}
@@ -32,43 +32,35 @@ public class WrappedPacketInSteerVehicle extends WrappedPacket {
super(packet);
}
/**
* Get the side value.
* <p>
* If positive, they are moving to the left, if negative, they are moving to the right.
*
* @return Side Value
*/
public float getSideValue() {
return readFloat(0);
}
/**
* Get the forward value.
* <p>
* If positive, they are moving forward, if negative, they are moving backwards.
*
* @return Forward Value
*/
public void setSideValue(float value) {
writeFloat(0, value);
}
public float getForwardValue() {
return readFloat(1);
}
/**
* Is a Jump
*
* @return Is Jump
*/
public void setForwardValue(float value) {
writeFloat(1, value);
}
public boolean isJump() {
return readBoolean(0);
}
/**
* Is an unmount
*
* @return Is Unmounting
*/
public boolean isUnmount() {
public void setJump(boolean isJump) {
writeBoolean(0, isJump);
}
public boolean isDismount() {
return readBoolean(1);
}
public void setDismount(boolean isDismount) {
writeBoolean(1, isDismount);
}
}
@@ -35,4 +35,8 @@ public class WrappedPacketInTabComplete extends WrappedPacket {
public String getText() {
return readString(0);
}
public void setText(String text) {
writeString(0, text);
}
}
@@ -1,3 +1,27 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.play.in.teleportaccept;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
@@ -11,4 +35,8 @@ public class WrappedPacketInTeleportAccept extends WrappedPacket {
public int getTeleportId() {
return readInt(0);
}
public void setTeleportId(int teleportId) {
writeInt(0, teleportId);
}
}
@@ -32,30 +32,27 @@ public final class WrappedPacketInTransaction extends WrappedPacket {
super(packet);
}
/**
* Get the window ID.
*
* @return Window ID
*/
public int getWindowId() {
return readInt(0);
}
/**
* Get the action number.
*
* @return Action number
*/
public void setWindowId(int windowID) {
writeInt(0, windowID);
}
public short getActionNumber() {
return readShort(0);
}
/**
* Is transaction accepted.
*
* @return Transaction Accepted
*/
public void setActionNumber(short actionNumber) {
writeShort(0, actionNumber);
}
public boolean isAccepted() {
return readBoolean(0);
}
public void setAccepted(boolean isAccepted) {
writeBoolean(0, isAccepted);
}
}
@@ -35,4 +35,8 @@ public class WrappedPacketInTrSel extends WrappedPacket {
public int getSlot() {
return readInt(0);
}
public void setSlot(int slot) {
writeInt(0, slot);
}
}
@@ -27,9 +27,9 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.updatesign;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.packetwrappers.play.out.chat.WrappedPacketOutChat;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import java.lang.reflect.InvocationTargetException;
@@ -37,8 +37,6 @@ public class WrappedPacketInUpdateSign extends WrappedPacket {
private static boolean v_1_7_mode, strArrayMode;
private static Class<?> blockPosClass;
private Object blockPosObj;
public WrappedPacketInUpdateSign(NMSPacket packet) {
super(packet);
}
@@ -57,67 +55,56 @@ public class WrappedPacketInUpdateSign extends WrappedPacket {
}
}
public int getX() {
public Vector3i getBlockPosition() {
int x = 0;
int y = 0;
int z = 0;
if (v_1_7_mode) {
return readInt(0);
x = readInt(0);
y = readInt(1);
z = readInt(2);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPosClass);
}
Object blockPosObj = readObject(0, blockPosClass);
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj);
x = (int) NMSUtils.getBlockPosX.invoke(blockPosObj);
y = (int) NMSUtils.getBlockPosY.invoke(blockPosObj);
z = (int) NMSUtils.getBlockPosZ.invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
return new Vector3i(x, y, z);
}
public int getY() {
public void setBlockPosition(Vector3i blockPos) {
if (v_1_7_mode) {
return readInt(1);
writeInt(0, blockPos.x);
writeInt(1, blockPos.y);
writeInt(2, blockPos.z);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
}
public int getZ() {
if (v_1_7_mode) {
return readInt(2);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
Object blockPosObj = NMSUtils.generateNMSBlockPos(blockPos.x, blockPos.y, blockPos.z);
write(NMSUtils.blockPosClass, 0, blockPosObj);
}
}
public String[] getTextLines() {
if (strArrayMode) {
//1.7.10 and the newest versions use this
//1.7.10
return readStringArray(0);
} else {
//1.8 uses this for example
//1.8 and above
Object[] iChatComponents = (Object[]) readAnyObject(1);
String[] lines = new String[iChatComponents.length];
for (int i = 0; i < iChatComponents.length; i++) {
lines[i] = WrappedPacketOutChat.
toStringFromIChatBaseComponent(iChatComponents[i]);
}
return lines;
return NMSUtils.readIChatBaseComponents(iChatComponents);
}
}
public void setTextLines(String[] lines) {
if (strArrayMode) {
writeStringArray(0, lines);
} else {
Object[] iChatComponents = NMSUtils.generateIChatBaseComponents(lines);
writeAnyObject(1, iChatComponents);
}
}
}
@@ -27,20 +27,23 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.useentity;
import io.github.retrooper.packetevents.PacketEvents;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.player.Hand;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.github.retrooper.packetevents.utils.vector.Vector3d;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public final class WrappedPacketInUseEntity extends WrappedPacket {
private static Class<?> enumEntityUseActionClass, enumHandClass;
private static Class<? extends Enum<?>> enumEntityUseActionClass, enumHandClass;
private Entity entity;
private EntityUseAction action;
private int entityId = -1;
private int entityID = -1;
public WrappedPacketInUseEntity(final NMSPacket packet) {
super(packet);
@@ -50,72 +53,94 @@ public final class WrappedPacketInUseEntity extends WrappedPacket {
protected void load() {
Class<?> useEntityClass = NMSUtils.getNMSClassWithoutException("PacketPlayInUseEntity");
try {
enumHandClass = NMSUtils.getNMSClass("EnumHand");
enumHandClass = NMSUtils.getNMSEnumClass("EnumHand");
} catch (ClassNotFoundException e) {
//Probably a 1.7.10 or 1.8.x server
}
try {
enumEntityUseActionClass = NMSUtils.getNMSClass("EnumEntityUseAction");
enumEntityUseActionClass = NMSUtils.getNMSEnumClass("EnumEntityUseAction");
} catch (ClassNotFoundException e) {
//That is fine, it is probably a subclass
enumEntityUseActionClass = SubclassUtil.getSubClass(Objects.requireNonNull(useEntityClass), "EnumEntityUseAction");
enumEntityUseActionClass = SubclassUtil.getEnumSubClass(useEntityClass, "EnumEntityUseAction");
}
}
/**
* Lookup the associated entity by the ID that was sent in the packet.
*
* @return Entity
*/
@Nullable
public Entity getEntity() {
if (entity != null) {
return entity;
}
return entity = NMSUtils.getEntityById(getEntityId());
return getEntity(null);
}
@Nullable
public Entity getEntity(@Nullable World world) {
if (entity == null) {
entity = NMSUtils.getEntityById(world, getEntityId());
}
return entity;
}
public void setEntity(Entity entity) {
setEntityId(entity.getEntityId());
this.entity = entity;
}
/**
* Get the ID of the entity.
* If you do not want to use {@link #getEntity()},
* you lookup the entity by yourself with this entity ID.
*
* @return Entity ID
*/
public int getEntityId() {
if (entityId != -1) {
return entityId;
if (entityID != -1) {
return entityID;
}
return entityId = readInt(0);
return entityID = readInt(0);
}
public Vector3d getTarget() {
if (getAction() == EntityUseAction.INTERACT_AT
&& PacketEvents.get().getServerUtils().getVersion() != ServerVersion.v_1_7_10) {
public void setEntityId(int entityID) {
writeInt(0, this.entityID = entityID);
this.entity = null;
}
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
public Vector3d getTarget() throws UnsupportedOperationException {
if (PacketEvents.get().getServerUtils().getVersion() == ServerVersion.v_1_7_10) {
throwUnsupportedOperation();
return Vector3d.INVALID;
}
//We are certain we are on a 1.8 (or higher) server.
if (getAction() == EntityUseAction.INTERACT_AT) {
Object vec3DObj = readObject(0, NMSUtils.vec3DClass);
WrappedPacket vec3DWrapper = new WrappedPacket(new NMSPacket(vec3DObj));
return new Vector3d(vec3DWrapper.readDouble(0), vec3DWrapper.readDouble(1), vec3DWrapper.readDouble(2));
} else {
return Vector3d.INVALID;
}
}
@SupportedVersions(ranges = {ServerVersion.v_1_8, ServerVersion.ERROR})
public void setTarget(Vector3d target) throws UnsupportedOperationException {
if (PacketEvents.get().getServerUtils().getVersion() == ServerVersion.v_1_7_10) {
throwUnsupportedOperation();
}
//We are certain we are on a 1.8 (or higher) server.
if (getAction() == EntityUseAction.INTERACT_AT) {
Object vec3DObj = NMSUtils.generateVec3D(target.x, target.y, target.z);
write(NMSUtils.vec3DClass, 0, vec3DObj);
}
return Vector3d.INVALID;
}
/**
* Get the associated action.
*
* @return Get EntityUseAction
*/
public EntityUseAction getAction() {
if (action == null) {
final Object useActionEnum = readObject(0, enumEntityUseActionClass);
Enum<?> useActionEnum = readEnumConstant(0, enumEntityUseActionClass);
if (useActionEnum == null) {
//ur spigot sucks bruh, unfortunately some spigots have such a bug?
//This happens on some weird spigots apparently? Not sure why this field is null.
return EntityUseAction.INTERACT;
}
return action = EntityUseAction.valueOf(useActionEnum.toString());
return action = EntityUseAction.valueOf(useActionEnum.name());
}
return action;
}
public void setAction(EntityUseAction action) {
this.action = action;
Enum<?> enumConst = EnumUtil.valueOf(enumEntityUseActionClass, action.name());
writeEnumConstant(0, enumConst);
}
public Hand getHand() {
if ((getAction() == EntityUseAction.INTERACT || getAction() == EntityUseAction.INTERACT_AT)
&& PacketEvents.get().getServerUtils().getVersion().isNewerThan(ServerVersion.v_1_8_8)) {
@@ -129,6 +154,16 @@ public final class WrappedPacketInUseEntity extends WrappedPacket {
return Hand.MAIN_HAND;
}
@SupportedVersions(ranges = {ServerVersion.v_1_9, ServerVersion.ERROR})
public void setHand(Hand hand) throws UnsupportedOperationException {
if (PacketEvents.get().getServerUtils().getVersion().isOlderThan(ServerVersion.v_1_9)) {
throwUnsupportedOperation();
} else if (getAction() == EntityUseAction.INTERACT || getAction() == EntityUseAction.INTERACT_AT) {
Enum<?> enumConst = EnumUtil.valueOf(enumHandClass, hand.name());
writeEnumConstant(0, enumConst);
}
}
public enum EntityUseAction {
INTERACT, INTERACT_AT, ATTACK
}
@@ -36,19 +36,39 @@ public class WrappedPacketInVehicleMove extends WrappedPacket {
return readDouble(0);
}
public void setX(double x) {
writeDouble(0, x);
}
public double getY() {
return readDouble(1);
}
public void setY(double y) {
writeDouble(1, y);
}
public double getZ() {
return readDouble(2);
}
public void setZ(double z) {
writeDouble(2, z);
}
public float getYaw() {
return readFloat(0);
}
public void setYaw(float yaw) {
writeFloat(0, yaw);
}
public float getPitch() {
return readFloat(1);
}
public void setPitch(float pitch) {
writeFloat(1, pitch);
}
}
@@ -27,6 +27,7 @@ package io.github.retrooper.packetevents.packetwrappers.play.in.windowclick;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.enums.EnumUtil;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import org.bukkit.inventory.ItemStack;
@@ -36,9 +37,8 @@ import java.util.Arrays;
import java.util.HashMap;
public class WrappedPacketInWindowClick extends WrappedPacket {
private static final HashMap<String, Integer> INV_CLICK_TYPE_CACHE = new HashMap<>();
private static final HashMap<Integer, ArrayList<WindowClickType>> WINDOW_CLICK_TYPE_CACHE = new HashMap<>();
private static Class<?> invClickTypeClass;
private static Class<? extends Enum<?>> invClickTypeClass;
private static boolean isClickModePrimitive;
@@ -46,18 +46,16 @@ public class WrappedPacketInWindowClick extends WrappedPacket {
super(packet);
}
private static ArrayList<WindowClickType> getArrayListOfWindowClickTypes(WindowClickType... types) {
ArrayList<WindowClickType> arrayList = new ArrayList<>(types.length);
arrayList.addAll(Arrays.asList(types));
return arrayList;
}
@Override
protected void load() {
Class<?> packetClass = PacketTypeClasses.Play.Client.WINDOW_CLICK;
invClickTypeClass = NMSUtils.getNMSClassWithoutException("InventoryClickType");
INV_CLICK_TYPE_CACHE.put("PICKUP", 0);
INV_CLICK_TYPE_CACHE.put("QUICK_MOVE", 1);
INV_CLICK_TYPE_CACHE.put("SWAP", 2);
INV_CLICK_TYPE_CACHE.put("CLONE", 3);
INV_CLICK_TYPE_CACHE.put("THROW", 4);
INV_CLICK_TYPE_CACHE.put("QUICK_CRAFT", 5);
INV_CLICK_TYPE_CACHE.put("PICKUP_ALL", 6);
invClickTypeClass = NMSUtils.getNMSEnumClassWithoutException("InventoryClickType");
//MODE 0
WINDOW_CLICK_TYPE_CACHE.put(0, getArrayListOfWindowClickTypes(WindowClickType.LEFT_MOUSE_CLICK,
@@ -100,56 +98,41 @@ public class WrappedPacketInWindowClick extends WrappedPacket {
WINDOW_CLICK_TYPE_CACHE.put(6, getArrayListOfWindowClickTypes(WindowClickType.DOUBLE_CLICK));
isClickModePrimitive = Reflection.getField(packetClass, int.class, 3) != null;
invClickTypeClass = NMSUtils.getNMSClassWithoutException("InventoryClickType");
}
private static ArrayList<WindowClickType> getArrayListOfWindowClickTypes(WindowClickType... types) {
ArrayList<WindowClickType> arrayList = new ArrayList<WindowClickType>(types.length);
arrayList.addAll(Arrays.asList(types));
return arrayList;
}
/**
* Get the Window ID.
*
* @return Get Window ID
*/
public int getWindowId() {
return readInt(0);
}
/**
* Get the Window slot.
*
* @return Get Window Slot
*/
public void setWindowId(int windowID) {
writeInt(0, windowID);
}
public int getWindowSlot() {
return readInt(1);
}
/**
* Get the Window button.
*
* @return Get Window Button
*/
public void setWindowSlot(int slot) {
writeInt(1, slot);
}
public int getWindowButton() {
return readInt(2);
}
/**
* Get the action number.
*
* @return Get Action Number
*/
public void setWindowButton(int button) {
writeInt(2, button);
}
public short getActionNumber() {
return readShort(0);
}
/**
* Get the window click type.
*
* @return Get Window Click Type
*/
public void setActionNumber(short actionNumber) {
writeShort(0, actionNumber);
}
@Deprecated
public WindowClickType getWindowClickType() {
int mode = getMode();
if (WINDOW_CLICK_TYPE_CACHE.get(mode) == null) {
@@ -173,16 +156,21 @@ public class WrappedPacketInWindowClick extends WrappedPacket {
return WINDOW_CLICK_TYPE_CACHE.get(mode).get(windowButton);
}
/**
* Get the Window mode.
*
* @return Get Window Mode.
*/
public int getMode() {
if (isClickModePrimitive) {
return readInt(3);
} else {
return INV_CLICK_TYPE_CACHE.get(readObject(5, invClickTypeClass).toString());
Enum<?> enumConst = readEnumConstant(0, invClickTypeClass);
return enumConst.ordinal();
}
}
public void setMode(int mode) {
if (isClickModePrimitive) {
writeInt(3, mode);
} else {
Enum<?> enumConst = EnumUtil.valueByIndex(invClickTypeClass, mode);
writeEnumConstant(0, enumConst);
}
}
@@ -191,11 +179,17 @@ public class WrappedPacketInWindowClick extends WrappedPacket {
*
* @return Get Clicked ItemStack
*/
public ItemStack getClickedItem() {
public ItemStack getClickedItemStack() {
Object nmsItemStack = readObject(0, NMSUtils.nmsItemStackClass);
return NMSUtils.toBukkitItemStack(nmsItemStack);
}
public void setClickedItemStack(ItemStack stack) {
Object nmsItemStack = NMSUtils.toNMSItemStack(stack);
write(NMSUtils.nmsItemStackClass, 0, nmsItemStack);
}
@Deprecated
public enum WindowClickType {
LEFT_MOUSE_CLICK, RIGHT_MOUSE_CLICK,
SHIFT_LEFT_MOUSE_CLICK, SHIFT_RIGHT_MOUSE_CLICK,
@@ -1,66 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 retrooper
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.retrooper.packetevents.packetwrappers.play.out.abilities;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
final class PlayerAbilitiesUtils {
public static Class<?> playerAbilitiesClass;
public static Constructor<?> playerAbilitiesConstructor;
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 {
if (playerAbilitiesClass == null) {
try {
playerAbilitiesClass = NMSUtils.getNMSClass("PlayerAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
if (playerAbilitiesConstructor == null && playerAbilitiesClass != null) {
try {
playerAbilitiesConstructor = playerAbilitiesClass.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
final Object instance = Objects.requireNonNull(playerAbilitiesConstructor).newInstance();
WrappedPacket wrapper = new WrappedPacket(new NMSPacket(instance));
wrapper.writeBoolean(0, isVulnerable);
wrapper.writeBoolean(1, isFlying);
wrapper.writeBoolean(2, allowFlight);
wrapper.writeBoolean(3, canBuildInstantly);
wrapper.writeFloat(0, flySpeed);
wrapper.writeFloat(1, walkSpeed);
return instance;
}
}
@@ -28,12 +28,14 @@ import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public final class WrappedPacketOutAbilities extends WrappedPacket implements SendableWrapper {
private static Constructor<?> packetConstructor;
private static Constructor<?> packetConstructor, playerAbilitiesConstructor;
private static Class<?> playerAbilitiesClass;
private boolean vulnerable, flying, allowFlight, instantBuild;
private float flySpeed, walkSpeed;
@@ -54,19 +56,27 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
@Override
protected void load() {
Class<?> packetClass = PacketTypeClasses.Play.Server.ABILITIES;
try {
playerAbilitiesClass = NMSUtils.getNMSClass("PlayerAbilities");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (playerAbilitiesClass != null) {
try {
playerAbilitiesConstructor = playerAbilitiesClass.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
try {
packetConstructor = packetClass.getConstructor(PlayerAbilitiesUtils.playerAbilitiesClass);
packetConstructor = packetClass.getConstructor(playerAbilitiesClass);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* Should the client be vulnerable?
*
* @return Is Vulnerable
*/
public boolean isVulnerable() {
if (packet != null) {
return readBoolean(0);
@@ -75,11 +85,14 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
/**
* Should the client be flying?
*
* @return Is Flying
*/
public void setVulnerable(boolean isVulnerable) {
if (packet != null) {
writeBoolean(0, isVulnerable);
} else {
this.vulnerable = isVulnerable;
}
}
public boolean isFlying() {
if (packet != null) {
return readBoolean(1);
@@ -88,11 +101,14 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
/**
* Should the client be allowed to fly?
*
* @return Is Allowed To Fly
*/
public void setFlying(boolean isFlying) {
if (packet != null) {
writeBoolean(1, isFlying);
} else {
this.flying = isFlying;
}
}
public boolean isFlightAllowed() {
if (packet != null) {
return readBoolean(2);
@@ -101,12 +117,15 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
/**
* Should the client be able to build instantly?
*
* @return Is Allowed To Build Instantly
*/
public boolean canInstantlyBuild() {
public void setFlightAllowed(boolean isFlightAllowed) {
if (packet != null) {
writeBoolean(2, isFlightAllowed);
} else {
this.allowFlight = isFlightAllowed;
}
}
public boolean canBuildInstantly() {
if (packet != null) {
return readBoolean(3);
} else {
@@ -114,11 +133,14 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
/**
* Get the client's defined fly speed.
*
* @return Get Fly Speed
*/
public void setCanBuildInstantly(boolean canBuildInstantly) {
if (packet != null) {
writeBoolean(3, canBuildInstantly);
} else {
this.instantBuild = canBuildInstantly;
}
}
public float getFlySpeed() {
if (packet != null) {
return readFloat(0);
@@ -127,11 +149,14 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
/**
* Get the client's defined walk speed.
*
* @return Get Walk Speed
*/
public void setFlySpeed(float flySpeed) {
if (packet != null) {
writeFloat(0, flySpeed);
} else {
this.flySpeed = flySpeed;
}
}
public float getWalkSpeed() {
if (packet != null) {
return readFloat(1);
@@ -140,10 +165,35 @@ public final class WrappedPacketOutAbilities extends WrappedPacket implements Se
}
}
public void setWalkSpeed(float walkSpeed) {
if (packet != null) {
writeFloat(1, walkSpeed);
} else {
this.walkSpeed = walkSpeed;
}
}
private Object getPlayerAbilities(boolean vulnerable, boolean flying, boolean flightAllowed, boolean canBuildInstantly, float flySpeed, float walkSpeed) {
Object instance = null;
try {
instance = playerAbilitiesConstructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
WrappedPacket wrapper = new WrappedPacket(new NMSPacket(instance));
wrapper.writeBoolean(0, vulnerable);
wrapper.writeBoolean(1, flying);
wrapper.writeBoolean(2, flightAllowed);
wrapper.writeBoolean(3, canBuildInstantly);
wrapper.writeFloat(0, flySpeed);
wrapper.writeFloat(1, walkSpeed);
return instance;
}
@Override
public Object asNMSPacket() {
try {
return packetConstructor.newInstance(PlayerAbilitiesUtils.getPlayerAbilities(isVulnerable(), isFlying(), isFlightAllowed(), canInstantlyBuild(), getFlySpeed(), getWalkSpeed()));
return packetConstructor.newInstance(getPlayerAbilities(isVulnerable(), isFlying(), isFlightAllowed(), canBuildInstantly(), getFlySpeed(), getWalkSpeed()));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
@@ -29,7 +29,9 @@ import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -67,50 +69,63 @@ public final class WrappedPacketOutAnimation extends WrappedPacket implements Se
}
}
/**
* Lookup the associated entity by the ID that was sent in the packet.
*
* @return Entity
*/
@Nullable
public Entity getEntity() {
if (entity != null) {
return entity;
}
return entity = NMSUtils.getEntityById(getEntityId());
return getEntity(null);
}
@Nullable
public Entity getEntity(@Nullable World world) {
if (entity == null) {
entity = NMSUtils.getEntityById(world, getEntityId());
}
return entity;
}
public void setEntity(Entity entity) {
setEntityId(entity.getEntityId());
this.entity = entity;
}
/**
* Get the ID of the entity.
* If you do not want to use {@link #getEntity()},
* you lookup the entity by yourself with this entity ID.
*
* @return Entity ID
*/
public int getEntityId() {
if (entityID == -1) {
entityID = readInt(0);
if (packet != null) {
if (entityID == -1) {
entityID = readInt(0);
}
}
return entityID;
}
/**
* Get the entity animation type.
*
* @return Get Entity Animation Type
*/
public void setEntityId(int entityID) {
if (packet != null) {
writeInt(0, this.entityID = entityID);
} else {
this.entityID = entityID;
}
this.entity = null;
}
public EntityAnimationType getAnimationType() {
if (packet != null) {
byte id = (byte)readInt(1);
byte id = (byte) readInt(1);
return EntityAnimationType.getById(id);
} else {
return type;
}
}
public void setAnimationType(EntityAnimationType type) {
if (packet != null) {
writeInt(1, type.ordinal());
} else {
this.type = type;
}
}
@Override
public Object asNMSPacket() {
final Object nmsEntity = NMSUtils.getNMSEntity(getEntity());
final int index = getAnimationType().id;
final int index = getAnimationType().ordinal();
try {
return animationConstructor.newInstance(nmsEntity, index);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
@@ -120,17 +135,11 @@ public final class WrappedPacketOutAnimation extends WrappedPacket implements Se
}
public enum EntityAnimationType {
SWING_MAIN_ARM((byte) 0), TAKE_DAMAGE((byte) 1), LEAVE_BED((byte) 2),
SWING_OFFHAND((byte) 3), CRITICAL_EFFECT((byte) 4), MAGIC_CRITICAL_EFFECT((byte) 5);
SWING_MAIN_ARM, TAKE_DAMAGE, LEAVE_BED,
SWING_OFFHAND, CRITICAL_EFFECT, MAGIC_CRITICAL_EFFECT;
final byte id;
EntityAnimationType() {
EntityAnimationType(byte id) {
this.id = id;
}
public byte getId() {
return id;
}
public static EntityAnimationType getById(byte id) {
@@ -0,0 +1,153 @@
package io.github.retrooper.packetevents.packetwrappers.play.out.blockbreakanimation;
import io.github.retrooper.packetevents.packettype.PacketTypeClasses;
import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//TODO test
public class WrappedPacketOutBlockBreakAnimation extends WrappedPacket implements SendableWrapper {
private static Constructor<?> packetConstructor;
private int entityID;
private Entity entity;
private Vector3i blockPosition;
private int destroyStage;
public WrappedPacketOutBlockBreakAnimation(NMSPacket packet) {
super(packet);
}
public WrappedPacketOutBlockBreakAnimation(int entityID, Vector3i blockPosition, int destroyStage) {
this.entityID = entityID;
this.blockPosition = blockPosition;
this.destroyStage = destroyStage;
}
public WrappedPacketOutBlockBreakAnimation(Entity entity, Vector3i blockPosition, int destroyStage) {
this.entityID = entity.getEntityId();
this.entity = entity;
this.blockPosition = blockPosition;
this.destroyStage = destroyStage;
}
@Override
protected void load() {
try {
packetConstructor = PacketTypeClasses.Play.Server.BLOCK_BREAK_ANIMATION.getConstructor(int.class, int.class, int.class, int.class, int.class);
} catch (NoSuchMethodException e) {
try {
packetConstructor = PacketTypeClasses.Play.Server.BLOCK_BREAK_ANIMATION.getConstructor(int.class, NMSUtils.blockPosClass, int.class);
} catch (NoSuchMethodException e2) {
e2.printStackTrace();
}
}
}
public int getEntityId() {
if (entityID != -1) {
return entityID;
}
return entityID = readInt(0);
}
public void setEntityId(int entityID) {
if (packet != null) {
writeInt(0, this.entityID = entityID);
} else {
this.entityID = entityID;
}
this.entity = null;
}
@Nullable
public Entity getEntity() {
return getEntity(null);
}
@Nullable
public Entity getEntity(@Nullable World world) {
if (entity == null) {
entity = NMSUtils.getEntityById(world, getEntityId());
}
return entity;
}
public void setEntity(Entity entity) {
setEntityId(entity.getEntityId());
this.entity = entity;
}
public Vector3i getBlockPosition() {
if (packet != null) {
Vector3i blockPosition = new Vector3i();
if (version.isOlderThan(ServerVersion.v_1_8)) {
this.blockPosition.x = readInt(1);
this.blockPosition.y = readInt(2);
this.blockPosition.z = readInt(3);
} else {
Object blockPos = readObject(0, NMSUtils.blockPosClass);
this.blockPosition = NMSUtils.readBlockPos(blockPos);
}
return blockPosition;
} else {
return this.blockPosition;
}
}
public void setBlockPosition(Vector3i blockPosition) {
if (packet != null) {
if (version.isOlderThan(ServerVersion.v_1_8)) {
writeInt(1, blockPosition.x);
writeInt(2, blockPosition.y);
writeInt(3, blockPosition.z);
} else {
Object blockPos = NMSUtils.generateNMSBlockPos(blockPosition.x, blockPosition.y, blockPosition.z);
write(NMSUtils.blockPosClass, 0, blockPos);
}
} else {
this.blockPosition = blockPosition;
}
}
public int getDestroyStage() {
if (packet != null) {
int index = version.isOlderThan(ServerVersion.v_1_8) ? 4 : 1;
return readInt(index);
} else {
return this.destroyStage;
}
}
public void setDestroyStage(int destroyStage) {
if (packet != null) {
int index = version.isOlderThan(ServerVersion.v_1_8) ? 4 : 1;
writeInt(index, destroyStage);
} else {
this.destroyStage = destroyStage;
}
}
@Override
public Object asNMSPacket() {
Vector3i blockPosition = getBlockPosition();
try {
if (version.isOlderThan(ServerVersion.v_1_8)) {
return packetConstructor.newInstance(getEntityId(), blockPosition.x, blockPosition.y, blockPosition.z, getDestroyStage());
} else {
Object nmsBlockPos = NMSUtils.generateNMSBlockPos(blockPosition.x, blockPosition.y, blockPosition.z);
return packetConstructor.newInstance(getEntityId(), nmsBlockPos, getDestroyStage());
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
@@ -31,33 +31,37 @@ import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.server.ServerVersion;
import io.github.retrooper.packetevents.utils.vector.Vector3i;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//TODO TEST ON 1.7.10
public class WrappedPacketOutBlockChange extends WrappedPacket implements SendableWrapper {
private static Constructor<?> packetConstructor;
private static Method getBlockIdMethodCache = null;
private static Method getNMSBlockMethodCache = null;
private static Method getNMSWorldTypeMethodCache = null;
private Object blockPosObj = null;
private int blockPosX;
private int blockPosY;
private int blockPosZ;
private World world;
private Material material;
public WrappedPacketOutBlockChange(NMSPacket packet) {
super(packet);
}
public WrappedPacketOutBlockChange(World world, int blockPosX, int blockPosY, int blockPosZ) {
this.blockPosX = blockPosX;
this.blockPosY = blockPosY;
this.blockPosZ = blockPosZ;
public WrappedPacketOutBlockChange(World world, Vector3i blockPos) {
this.blockPosX = blockPos.x;
this.blockPosY = blockPos.y;
this.blockPosZ = blockPos.z;
this.world = world;
}
@@ -68,13 +72,19 @@ public class WrappedPacketOutBlockChange extends WrappedPacket implements Sendab
this.world = location.getWorld();
}
public WrappedPacketOutBlockChange(World world, Vector3i blockPos, Material material) {
this(world, blockPos);
this.material = material;
}
public WrappedPacketOutBlockChange(Location location, Material material) {
this(location);
this.material = material;
}
@Override
protected void load() {
getBlockIdMethodCache = Reflection.getMethod(NMSUtils.blockClass, "getCombinedId", int.class, NMSUtils.iBlockDataClass);
if (getBlockIdMethodCache == null) {
//1.7.10
getBlockIdMethodCache = Reflection.getMethod(NMSUtils.blockClass, "getId", int.class, NMSUtils.blockClass);
}
getNMSBlockMethodCache = Reflection.getMethod(NMSUtils.iBlockDataClass, "getBlock", 0);
getNMSWorldTypeMethodCache = Reflection.getMethod(NMSUtils.nmsWorldClass, "getType", 0);
if (version.equals(ServerVersion.v_1_7_10)) {
try {
@@ -91,108 +101,123 @@ public class WrappedPacketOutBlockChange extends WrappedPacket implements Sendab
}
}
public int getBlockX() {
public Vector3i getBlockPosition() {
int x = 0;
int y = 0;
int z = 0;
if (packet != null) {
if (version.isOlderThan(ServerVersion.v_1_8)) {
return readInt(0);
x = readInt(0);
y = readInt(1);
z = readInt(2);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj);
x = (int) NMSUtils.getBlockPosX.invoke(blockPosObj);
y = (int) NMSUtils.getBlockPosY.invoke(blockPosObj);
z = (int) NMSUtils.getBlockPosZ.invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
} else {
return blockPosX;
x = blockPosX;
y = blockPosY;
z = blockPosZ;
}
return new Vector3i(x, y, z);
}
public int getBlockY() {
public void setBlockPosition(Vector3i blockPos) {
if (packet != null) {
if (version.equals(ServerVersion.v_1_7_10)) {
return readInt(1);
if (version.isOlderThan(ServerVersion.v_1_8)) {
writeInt(0, blockPos.x);
writeInt(1, blockPos.y);
writeInt(2, blockPos.z);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
}
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
blockPosObj = NMSUtils.generateNMSBlockPos(blockPos.x, blockPos.y, blockPos.z);
write(NMSUtils.blockPosClass, 0, blockPosObj);
}
} else {
return blockPosY;
blockPosX = blockPos.x;
blockPosY = blockPos.y;
blockPosZ = blockPos.z;
}
}
public int getBlockZ() {
public Material getMaterial() {
if (packet != null) {
if (version.equals(ServerVersion.v_1_7_10)) {
return readInt(2);
Object nmsBlock = null;
if (version.isOlderThan(ServerVersion.v_1_8)) {
nmsBlock = readObject(0, NMSUtils.blockClass);
} else {
if (blockPosObj == null) {
blockPosObj = readObject(0, NMSUtils.blockPosClass);
}
Object iBlockDataObj = readObject(0, NMSUtils.iBlockDataClass);
try {
return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj);
nmsBlock = getNMSBlockMethodCache.invoke(iBlockDataObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
return NMSUtils.getMaterialFromNMSBlock(nmsBlock);
} else {
return blockPosZ;
return material;
}
}
public int getBlockId() {
if (version.isOlderThan(ServerVersion.v_1_8)) {
Object nmsBlock = readObject(0, NMSUtils.blockClass);
try {
return (int) getBlockIdMethodCache.invoke(null, nmsBlock);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
public void setMaterial(Material material) {
if (packet != null) {
Object nmsBlock = NMSUtils.getNMSBlockFromMaterial(material);
if (version.isOlderThan(ServerVersion.v_1_8)) {
write(NMSUtils.blockClass, 0, nmsBlock);
} else {
WrappedPacket nmsBlockWrapper = new WrappedPacket(new NMSPacket(nmsBlock), NMSUtils.blockClass);
Object iBlockData = nmsBlockWrapper.readObject(0, NMSUtils.iBlockDataClass);
write(NMSUtils.iBlockDataClass, 0, iBlockData);
}
} else {
Object iBlockDataObj = readObject(0, NMSUtils.iBlockDataClass);
try {
return (int) getBlockIdMethodCache.invoke(null, iBlockDataObj);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
this.material = material;
}
return -1;
}
@Override
public Object asNMSPacket() {
Object nmsPacket = null;
WrappedPacketOutBlockChange blockChange;
Vector3i blockPosition = getBlockPosition();
if (version.isOlderThan(ServerVersion.v_1_8)) {
try {
Object nmsWorld = NMSUtils.convertBukkitWorldToNMSWorld(world);
return packetConstructor.newInstance(getBlockX(), getBlockY(), getBlockZ(), nmsWorld);
nmsPacket = packetConstructor.newInstance(blockPosition.x, blockPosition.y, blockPosition.z, nmsWorld);
blockChange = new WrappedPacketOutBlockChange(new NMSPacket(nmsPacket));
if (material != null) {
blockChange.setMaterial(material);
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} else {
try {
Object packetInstance = packetConstructor.newInstance();
WrappedPacket packetWrapper = new WrappedPacket(new NMSPacket(packetInstance));
Object nmsBlockPos = NMSUtils.generateNMSBlockPos(getBlockX(), getBlockY(), getBlockZ());
Object nmsWorld = NMSUtils.convertBukkitWorldToNMSWorld(world);
Object nmsBlockData = getNMSWorldTypeMethodCache.invoke(nmsWorld, nmsBlockPos);
packetWrapper.write(NMSUtils.blockPosClass, 0, nmsBlockPos);
packetWrapper.write(NMSUtils.iBlockDataClass, 0, nmsBlockData);
return packetInstance;
nmsPacket = packetConstructor.newInstance();
blockChange = new WrappedPacketOutBlockChange(new NMSPacket(nmsPacket));
if (material != null) {
blockChange.setMaterial(material);
} else {
Object nmsBlockPos = NMSUtils.generateNMSBlockPos(blockPosition.x, blockPosition.y, blockPosition.z);
Object nmsWorld = NMSUtils.convertBukkitWorldToNMSWorld(world);
Object nmsBlockData = getNMSWorldTypeMethodCache.invoke(nmsWorld, nmsBlockPos);
blockChange.write(NMSUtils.iBlockDataClass, 0, nmsBlockData);
}
blockChange.setBlockPosition(blockPosition);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
return null;
return nmsPacket;
}
}
@@ -61,6 +61,14 @@ public class WrappedPacketOutCamera extends WrappedPacket implements SendableWra
return cameraID;
}
public void setCameraId(int cameraID) {
if (packet != null) {
writeInt(0, cameraID);
} else {
this.cameraID = cameraID;
}
}
@Override
public Object asNMSPacket() {
Object packetInstance = null;
@@ -30,22 +30,17 @@ import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.reflection.SubclassUtil;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.UUID;
public final class WrappedPacketOutChat extends WrappedPacket implements SendableWrapper {
private static final HashMap<ChatPosition, Byte> cachedChatPositions = new HashMap<>();
private static final HashMap<Byte, ChatPosition> cachedChatPositionIntegers = new HashMap<>();
private static final HashMap<String, Byte> cachedChatMessageTypeIntegers = new HashMap<>();
private static Constructor<?> chatClassConstructor;
private static Class<?> packetClass, iChatBaseComponentClass, chatSerializerClass, chatMessageTypeEnum;
private static Class<?> packetClass, iChatBaseComponentClass, chatMessageTypeEnum;
private static Method chatMessageTypeCreatorMethod;
//0 = IChatBaseComponent, Byte
//1 = IChatBaseComponent, Int
@@ -64,6 +59,9 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
this(message, ChatPosition.CHAT, uuid, isJson);
}
public WrappedPacketOutChat(BaseComponent component, UUID uuid) {
this(component, ChatPosition.CHAT, uuid);
}
public WrappedPacketOutChat(BaseComponent component, ChatPosition pos, UUID uuid) {
this(ComponentSerializer.toString(component), pos, uuid, true);
@@ -71,7 +69,7 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
public WrappedPacketOutChat(String message, ChatPosition chatPosition, UUID uuid, boolean isJson) {
this.uuid = uuid;
this.message = isJson ? message : fromStringToJSON(message);
this.message = isJson ? message : NMSUtils.fromStringToJSON(message);
this.chatPosition = chatPosition;
}
@@ -84,15 +82,6 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
e.printStackTrace();
}
//In 1.8.3+ the ChatSerializer class is declared in the IChatBaseComponent class, so we have to handle that
try {
chatSerializerClass = NMSUtils.getNMSClass("ChatSerializer");
} catch (ClassNotFoundException e) {
//That is fine, it is probably a subclass
chatSerializerClass = SubclassUtil.getSubClass(iChatBaseComponentClass, "ChatSerializer");
}
boolean isVeryOutdated = false;
try {
chatMessageTypeEnum = NMSUtils.getNMSClass("ChatMessageType");
@@ -126,84 +115,62 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
chatClassConstructor = packetClass.getConstructor(iChatBaseComponentClass, int.class);
constructorMode = 1;
} catch (NoSuchMethodException e2) {
e2.printStackTrace();
try {
//Some weird 1.7.10 spigots remove that int parameter for no reason, I won't keep adding support for any more spigots and might stop
//accepting pull requests for support for spigots breaking things that normal spigot has.
chatClassConstructor = packetClass.getConstructor(iChatBaseComponentClass);
constructorMode = -1;
} catch (NoSuchMethodException noSuchMethodException) {
noSuchMethodException.printStackTrace();
}
}
}
}
cachedChatPositions.put(ChatPosition.CHAT, (byte) 0);
cachedChatPositions.put(ChatPosition.SYSTEM_MESSAGE, (byte) 1);
cachedChatPositions.put(ChatPosition.GAME_INFO, (byte) 2);
cachedChatPositionIntegers.put((byte) 0, ChatPosition.CHAT);
cachedChatPositionIntegers.put((byte) 1, ChatPosition.SYSTEM_MESSAGE);
cachedChatPositionIntegers.put((byte) 2, ChatPosition.GAME_INFO);
cachedChatMessageTypeIntegers.put("CHAT", (byte) 0);
cachedChatMessageTypeIntegers.put("SYSTEM", (byte) 1);
cachedChatMessageTypeIntegers.put("GAME_INFO", (byte) 2);
}
public static String fromStringToJSON(String message) {
return "{\"text\": \"" + message + "\"}";
}
public static String toStringFromIChatBaseComponent(Object obj) {
try {
return Reflection.getMethod(iChatBaseComponentClass, String.class, 0).invoke(obj).toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static Object toIChatBaseComponent(String msg) {
try {
return Reflection.getMethod(chatSerializerClass, 0, String.class).invoke(null, msg);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public Object asNMSPacket() {
int integerChatPos = cachedChatPositions.get(getChatPosition());
byte chatPos = (byte) getChatPosition().ordinal();
Object chatMessageTypeInstance = null;
if (chatMessageTypeEnum != null) {
try {
chatMessageTypeInstance = chatMessageTypeCreatorMethod.invoke(null, (byte) integerChatPos);
chatMessageTypeInstance = chatMessageTypeCreatorMethod.invoke(null, chatPos);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
switch (constructorMode) {
case -1:
try {
return chatClassConstructor.newInstance(NMSUtils.generateIChatBaseComponent(getMessage()));
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
case 0:
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(getMessage()), (byte) integerChatPos);
return chatClassConstructor.newInstance(NMSUtils.generateIChatBaseComponent(getMessage()), chatPos);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
break;
case 1:
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(getMessage()), integerChatPos);
return chatClassConstructor.newInstance(NMSUtils.generateIChatBaseComponent(getMessage()), (int) chatPos);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
break;
case 2:
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(getMessage()), chatMessageTypeInstance);
return chatClassConstructor.newInstance(NMSUtils.generateIChatBaseComponent(getMessage()), chatMessageTypeInstance);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
break;
case 3:
try {
return chatClassConstructor.newInstance(toIChatBaseComponent(getMessage()), chatMessageTypeInstance, uuid);
return chatClassConstructor.newInstance(NMSUtils.generateIChatBaseComponent(getMessage()), chatMessageTypeInstance, uuid);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
@@ -220,14 +187,7 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
public String getMessage() {
if (packet != null) {
final Object iChatBaseObj = readObject(0, iChatBaseComponentClass);
try {
Object contentString = Reflection.getMethod(iChatBaseComponentClass, String.class, 0).invoke(iChatBaseObj);
return contentString.toString();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
return NMSUtils.readIChatBaseComponent(iChatBaseObj);
} else {
return message;
}
@@ -243,27 +203,41 @@ public final class WrappedPacketOutChat extends WrappedPacket implements Sendabl
*/
public ChatPosition getChatPosition() {
if (packet != null) {
byte chatPosInteger = 0;
byte chatPositionValue;
switch (constructorMode) {
case -1:
chatPositionValue = (byte) ChatPosition.CHAT.ordinal();
break;
case 0:
chatPosInteger = readByte(0);
chatPositionValue = readByte(0);
break;
case 1:
chatPosInteger = (byte) readInt(0);
chatPositionValue = (byte) readInt(0);
break;
case 2:
case 3:
Object chatTypeEnumInstance = readObject(0, chatMessageTypeEnum);
chatPosInteger = cachedChatMessageTypeIntegers.get(chatTypeEnumInstance.toString());
return ChatPosition.valueOf(chatTypeEnumInstance.toString());
default:
chatPositionValue = 0;
break;
}
return cachedChatPositionIntegers.get(chatPosInteger);
return ChatPosition.getByPositionValue(chatPositionValue);
} else {
return chatPosition;
}
}
public enum ChatPosition {
CHAT, SYSTEM_MESSAGE, GAME_INFO
CHAT,
SYSTEM_MESSAGE,
GAME_INFO;
ChatPosition() {
}
public static ChatPosition getByPositionValue(byte position) {
return values()[position];
}
}
}
@@ -33,9 +33,16 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class WrappedPacketOutCloseWindow extends WrappedPacket implements SendableWrapper {
private static Constructor<?> constructor;
private int windowID;
private static Constructor<?> constructor;
public WrappedPacketOutCloseWindow(NMSPacket packet) {
super(packet);
}
public WrappedPacketOutCloseWindow(int windowID) {
this.windowID = windowID;
}
@Override
protected void load() {
@@ -46,14 +53,6 @@ public class WrappedPacketOutCloseWindow extends WrappedPacket implements Sendab
}
}
public WrappedPacketOutCloseWindow(NMSPacket packet) {
super(packet);
}
public WrappedPacketOutCloseWindow(int windowID) {
this.windowID = windowID;
}
public int getWindowId() {
if (packet != null) {
return readInt(0);
@@ -61,6 +60,14 @@ public class WrappedPacketOutCloseWindow extends WrappedPacket implements Sendab
return windowID;
}
public void setWindowId(int windowID) {
if (packet != null) {
writeInt(0, windowID);
} else {
this.windowID = windowID;
}
}
@Override
public Object asNMSPacket() {
try {
@@ -122,6 +122,21 @@ public class WrappedPacketOutCustomPayload extends WrappedPacket implements Send
return tag;
}
public void setTag(String tag) {
if (packet != null) {
switch (constructorMode) {
case 0:
case 1:
writeString(0, tag);
case 2:
Object minecraftKey = NMSUtils.generateMinecraftKey(tag);
write(NMSUtils.minecraftKeyClass, minecraftKeyIndexInClass, minecraftKey);
}
} else {
this.tag = tag;
}
}
public byte[] getData() {
if (packet != null) {
switch (constructorMode) {
@@ -141,6 +156,25 @@ public class WrappedPacketOutCustomPayload extends WrappedPacket implements Send
return data;
}
public void setData(byte[] data) {
if (packet != null) {
switch (constructorMode) {
case 0:
writeByteArray(0, data);
case 1:
case 2:
Object dataSerializer = readObject(0, packetDataSerializerClass);
WrappedPacket dataSerializerWrapper = new WrappedPacket(new NMSPacket(dataSerializer));
Object byteBuf = dataSerializerWrapper.readObject(0, byteBufClass);
PacketEvents.get().getByteBufUtil().setBytes(byteBuf, data);
}
} else {
this.data = data;
}
}
@Override
public Object asNMSPacket() {
byte[] data = getData();
@@ -30,8 +30,9 @@ import io.github.retrooper.packetevents.packetwrappers.SendableWrapper;
import io.github.retrooper.packetevents.packetwrappers.WrappedPacket;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import io.github.retrooper.packetevents.utils.reflection.Reflection;
import io.github.retrooper.packetevents.utils.vector.Vector3d;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -66,9 +67,6 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
this.pitch = pitch;
this.yaw = yaw;
this.onGround = onGround;
//Get entity player then pass it into the packetplayoutnamedentityspawn!
// net.minecraft.server.v1_7_R4.EntityPlayer cp = null;
//net.minecraft.server.v1_7_R4.PacketPlayOutNamedEntitySpawn es = new net.minecraft.server.v1_7_R4.PacketPlayOutNamedEntitySpawn(cp);
}
public WrappedPacketOutEntity(Entity entity, double deltaX, double deltaY, double deltaZ,
@@ -104,11 +102,6 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
}
/**
* Get the pitch.
*
* @return Get Byte Pitch
*/
public byte getPitch() {
if (packet != null) {
return readByte(pitchByteIndex);
@@ -117,11 +110,14 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
}
}
/**
* Get the Yaw.
*
* @return Get Byte Yaw
*/
public void setPitch(byte pitch) {
if (packet != null) {
writeByte(pitchByteIndex, pitch);
} else {
this.pitch = pitch;
}
}
public byte getYaw() {
if (packet != null) {
return readByte(yawByteIndex);
@@ -130,11 +126,12 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
}
}
/**
* Get the Delta X
*
* @return Delta X
*/
public void setYaw(byte yaw) {
if (packet != null) {
writeByte(yawByteIndex, yaw);
}
}
public double getDeltaX() {
if (packet != null) {
switch (mode) {
@@ -144,18 +141,33 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
return readInt(1) / dXYZDivisor;
case 2:
return readShort(0) / dXYZDivisor;
default:
return -1;
}
} else {
return deltaX;
}
return 0.0;
}
/**
* Get the Delta Y
*
* @return Delta Y
*/
public void setDeltaX(double deltaX) {
if (packet != null) {
int dx = (int) (deltaX * dXYZDivisor);
switch (mode) {
case 0:
writeByte(0, (byte) dx);
break;
case 1:
writeInt(1, dx);
break;
case 2:
writeShort(0, (short) dx);
break;
}
} else {
this.deltaX = deltaX;
}
}
public double getDeltaY() {
if (packet != null) {
switch (mode) {
@@ -165,18 +177,33 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
return readInt(2) / dXYZDivisor;
case 2:
return readShort(1) / dXYZDivisor;
default:
return -1;
}
} else {
return deltaY;
}
return 0.0;
}
/**
* Get the Delta Z
*
* @return Delta Z
*/
public void setDeltaY(double deltaY) {
if (packet != null) {
int dy = (int) (deltaY * dXYZDivisor);
switch (mode) {
case 0:
writeByte(1, (byte) dy);
break;
case 1:
writeInt(2, dy);
break;
case 2:
writeShort(1, (short) dy);
break;
}
} else {
this.deltaY = deltaY;
}
}
public double getDeltaZ() {
if (packet != null) {
switch (mode) {
@@ -186,36 +213,48 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
return readInt(3) / dXYZDivisor;
case 2:
return readShort(2) / dXYZDivisor;
default:
return -1;
}
} else {
return deltaZ;
}
return 0.0;
}
public Vector3d getDeltaPosition() {
return new Vector3d(getDeltaX(), getDeltaY(), getDeltaZ());
}
/**
* Lookup the associated entity by the ID that was sent in the packet.
*
* @return Entity
*/
public Entity getEntity() {
if (entity != null) {
return entity;
public void setDeltaZ(double deltaZ) {
int dz = (int) (deltaZ * dXYZDivisor);
if (packet != null) {
switch (mode) {
case 0:
writeByte(2, (byte) dz);
case 1:
writeInt(3, dz);
case 2:
writeShort(2, (short) dz);
}
} else {
this.deltaZ = deltaZ;
}
return entity = NMSUtils.getEntityById(getEntityId());
}
/**
* Get the ID of the entity.
* If you do not want to use {@link #getEntity()},
* you lookup the entity by yourself with this entity ID.
*
* @return Entity ID
*/
@Nullable
public Entity getEntity() {
return getEntity(null);
}
@Nullable
public Entity getEntity(@Nullable World world) {
if (entity == null) {
entity = NMSUtils.getEntityById(world, getEntityId());
}
return entity;
}
public void setEntity(Entity entity) {
setEntityId(entity.getEntityId());
this.entity = entity;
}
public int getEntityId() {
if (entityID != -1) {
return entityID;
@@ -224,11 +263,15 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
}
}
/**
* Is the entity on the ground?
*
* @return On Ground
*/
public void setEntityId(int entityID) {
if (packet != null) {
writeInt(0, this.entityID = entityID);
} else {
this.entityID = entityID;
}
this.entity = null;
}
public boolean isOnGround() {
if (packet != null) {
return readBoolean(0);
@@ -237,26 +280,37 @@ public class WrappedPacketOutEntity extends WrappedPacket implements SendableWra
}
}
public void setOnGround(boolean onGround) {
if (packet != null) {
writeBoolean(0, onGround);
} else {
this.onGround = onGround;
}
}
@Override
public Object asNMSPacket() {
try {
Object packetInstance = entityPacketConstructor.newInstance(entityID);
int dx = (int) (getDeltaX() * dXYZDivisor);
int dy = (int) (getDeltaY() * dXYZDivisor);
int dz = (int) (getDeltaZ() * dXYZDivisor);
WrappedPacket wrapper = new WrappedPacket(new NMSPacket(packetInstance));
switch (mode) {
case 0:
wrapper.writeByte(0, (byte) (deltaX * dXYZDivisor));
wrapper.writeByte(1, (byte) (deltaY * dXYZDivisor));
wrapper.writeByte(2, (byte) (deltaZ * dXYZDivisor));
wrapper.writeByte(0, (byte) dx);
wrapper.writeByte(1, (byte) dy);
wrapper.writeByte(2, (byte) dz);
break;
case 1:
wrapper.writeInt(1, (int) (deltaX * dXYZDivisor));
wrapper.writeInt(2, (int) (deltaY * dXYZDivisor));
wrapper.writeInt(3, (int) (deltaZ * dXYZDivisor));
wrapper.writeInt(1, dx);
wrapper.writeInt(2, dy);
wrapper.writeInt(3, dz);
break;
case 2:
wrapper.writeShort(0, (short) (deltaX * dXYZDivisor));
wrapper.writeShort(1, (short) (deltaY * dXYZDivisor));
wrapper.writeShort(2, (short) (deltaZ * dXYZDivisor));
wrapper.writeShort(0, (short) dx);
wrapper.writeShort(1, (short) dy);
wrapper.writeShort(2, (short) dz);
break;
}
wrapper.writeByte(yawByteIndex, yaw);
@@ -0,0 +1,200 @@
package io.github.retrooper.packetevents.packetwrappers.play.out.entitymetadata;
import io.github.retrooper.packetevents.utils.nms.NMSUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Supplier;
/**
* A data watcher object.
* This class is only present in 1.9+.
*
* @author SteelPhoenix
*/
// TODO: hashCode, equals, toString
public abstract class DataWatcherObject {
// Look at this beautiful code
// That's what you get for not including proper reflection helpers
// retrooper edit: lol
// TODO: Move to static initializer
public static final Class<?> TYPE = ((Supplier<Class<?>>) (() -> {
try {
return NMSUtils.getNMSClass("DataWatcherObject");
} catch (ClassNotFoundException exception) {
return null;
}
})).get();
private static final Constructor<?> CONSTRUCTOR = isPresent() ? TYPE.getConstructors()[0] : null;
/**
* Get if the wrapped class exists.
*
* @return if the wrapped class is present.
*/
public static boolean isPresent() {
return TYPE != null;
}
/**
* Create a data watcher object for an index and serializer.
*
* @param index Target index.
* @param serializer Target serializer or null.
* @return the data watcher object.
*/
public static DataWatcherObject fromIndex(int index, WrappedDataWatcherSerializer serializer) {
if (isPresent()) {
try {
return new WrappedDataWatcherObject(CONSTRUCTOR.newInstance(index, serializer == null ? null : serializer.getRaw()));
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) {
throw new RuntimeException("Could not invoke constructor", exception);
}
}
return new DummyDataWatcherObject(index);
}
/**
* Create a data watcher object from handle.
* Note that this method only works if the DataWatcherObject exists.
*
* @param handle Target handle.
* @return the data watcher object.
*/
public static DataWatcherObject fromHandle(Object handle) {
if (!isPresent()) {
throw new UnsupportedOperationException("Handle type is not present");
}
return new WrappedDataWatcherObject(handle);
}
/**
* Get the entry's index.
*
* @return the index.
*/
public abstract int getIndex();
/**
* Get the object's serializer.
*
* @return the serializer.
*/
public abstract WrappedDataWatcherSerializer getSerializer();
/**
* Get the wrapper handle.
*
* @return the handle.
*/
public abstract Object getRaw();
/**
* A DataWatcherObject.
*
* @author SteelPhoenix
*/
private static class WrappedDataWatcherObject extends DataWatcherObject {
private static final Field INDEX;
private static final Field SERIALIZER;
static {
Field index = null;
Field serializer = null;
if (isPresent()) {
// Look for first fields of the correct type
for (Field f : TYPE.getDeclaredFields()) {
if (index == null && f.getType() == Integer.TYPE) {
index = f;
}
if (serializer == null && f.getType() == WrappedDataWatcherSerializer.TYPE) {
serializer = f;
}
}
}
INDEX = index;
SERIALIZER = serializer;
}
private final Object raw;
private WrappedDataWatcherObject(Object nms) {
if (nms == null) {
throw new NullPointerException("Object cannot be null");
}
if (!TYPE.isAssignableFrom(nms.getClass())) {
throw new IllegalArgumentException("Object is not of type " + TYPE);
}
this.raw = nms;
}
@Override
public int getIndex() {
try {
return (int) INDEX.get(raw);
} catch (IllegalArgumentException | IllegalAccessException exception) {
throw new RuntimeException("Could not read field value", exception);
}
}
@Override
public WrappedDataWatcherSerializer getSerializer() {
Object handle;
try {
handle = SERIALIZER.get(raw);
} catch (IllegalArgumentException | IllegalAccessException exception) {
throw new RuntimeException("Could not read field value", exception);
}
// Not set
if (handle == null) {
return null;
}
// TODO: Get from known serializers
// Unknown handle type
return WrappedDataWatcherSerializer.of(handle, null, false);
}
@Override
public Object getRaw() {
return raw;
}
}
/**
* A 1.7-1.8 compat dummy object.
* This only stores an index.
*
* @author SteelPhoenix
*/
public static class DummyDataWatcherObject extends DataWatcherObject {
private final int index;
public DummyDataWatcherObject(int index) {
this.index = index;
}
@Override
public int getIndex() {
return index;
}
@Override
public WrappedDataWatcherSerializer getSerializer() {
throw new UnsupportedOperationException();
}
@Override
public Object getRaw() {
throw new UnsupportedOperationException();
}
}
}
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff. Show more