Compare commits

...
99 Commits
Author SHA1 Message Date
Dan Mulloy ab0faab396 Update version to 4.6.0 for release 2021-02-15 13:59:14 -05:00
Dan Mulloy 765fd9e987 Broaden throwable for misbehaving metrics plugins (#1078)
Fixes #1078
2021-02-03 09:39:04 -05:00
Dan Mulloy 8f7b530613 Tested with 1.16.5 2021-01-24 17:14:32 -05:00
Dan Mulloy 30fe81d366 Move getPacketDescription from the command to HexDumper as a public API
Related to filoghost/HolographicDisplays#385
2021-01-04 13:35:58 -05:00
PimvanderLoos b54dd49426 Replace CGLib with ByteBuddy (#984)
- The gclib dependency in the EnchancerFactory has been removed. All classes that used the actual factory part of it have been updated to use bytebuddy instead. This class will have to be removed at some point, but at the moment it is still used for accessing its class loader.
- Renamed EnhancerFactory to ByteBuddyFactory. All ByteBuddy actions should go through this now. Every subclass created here implements the ByteBuddyGenerated interface. This makes it possible to recognize classes generated using ByteBuddy (by default, it doesn't leave such a trace).
- Removed the method DefaultInstances#forEnhancer(Enhancer). This method isn't used anywhere; the last trace of usage of the method I could find was in 2013 (in the NetworkServerInjector). External plugins (I couldn't find any that used it), they should really have their own implementation, given that they already require an instance of an Enchancer. As such, I feel it is safe to remove rather than update it.
2021-01-04 00:24:34 -05:00
Dan Mulloy 26274fed52 ✨backwards compat✨ 2021-01-02 23:59:13 -05:00
Jan Lindner aa555f792e add sound converter for versions greater 1.16.4 (#1027) 2021-01-02 23:55:23 -05:00
Dan Mulloy 0d50905754 Merge branch 'master' of https://github.com/dmulloy2/ProtocolLib 2021-01-02 23:19:28 -05:00
Dan Mulloy b7132196fb Fix ChatExtensions exception
Fixes #1045
2021-01-02 23:19:20 -05:00
Dan Mulloy eb54b99856 Update funding info 2020-12-30 15:31:39 -06:00
Dan Mulloy 13b3d8679d Update ChatExtensions for 1.16.4
Fixes #1044
2020-12-27 00:04:11 -05:00
dependabot[bot] 1545a88e06 Bump junit from 4.13 to 4.13.1 (#989)
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-23 14:59:46 -05:00
Thibaut Gautier 2b22999b94 Fixed "protocollib_encoder" attempting to read non-nms packets (#959)
Added an override to the encoder as such would process any non-wire packets as NMS packets. In the case of the use of different APIs, such as Artemis Packet API, such would cause a conflict and would spit out casting exceptions. It is quite easy to resolve by exclusively accepting Wire Packets and packets which are assignable to the packet class. This solves the issue and tada happy ending.
2020-12-23 14:59:07 -05:00
PimvanderLoosandMark Vainomaa bbb053aa4e Fix Java 15 (#1025)
- Implemented a fix for the incompatibility with Java 15. This incompatibility was caused by the fact that the lambda generated in the NMS.NetworkManager is a hidden class in J15. Starting in Java 15, final fields in hidden classes can no longer be modified regardless of the 'accessible' flag. (see https://openjdk.java.net/jeps/371 "Using a hidden class", point 3). To circumvent this issue, this retrieves the data from the existing fields in the hidden class (a runnable or a callable) other than the packet. It then retrieves the constructor of the hidden class and instantiates it using the previously-retrieved data and the modified packet instance (this code is only used if the packet instance changed).
- Introduced a new ObjectReconstructor class that does all the fields/constructor discovering/accessing etc. The Runnable and Callable methods each get one instance of this class so that we can avoid having to get the fields/constructors and set them accessible every time we want to replace a packet.

Co-authored-by: Mark Vainomaa <mikroskeem@mikroskeem.eu>
2020-12-23 14:57:16 -05:00
Aurora 7bac4ec634 Update to 1.16.4 (#1007) 2020-11-09 15:11:48 -05:00
Dan Mulloy 4bc9e8b7b7 Fix a class cast exception with array wrappers
(Kinda surprised there isn't an issue to link here)
2020-10-19 16:30:42 -04:00
Dan Mulloy f381f0a2f7 Fix multi block change cloning
Fixes #990
2020-10-19 11:04:11 -04:00
Dan Mulloy 553e4b6813 Update to 1.16.3 2020-10-19 11:02:32 -04:00
Dan Mulloy bdaa843f2d Update to 1.16.2
Fixes #941
Fixes #944
Fixes #934
2020-08-24 13:19:49 -04:00
Dan Mulloy 13f5c14599 Fix bytecode breakage with StructureModifier
Fixes #935
2020-08-24 11:40:47 -04:00
Dan Mulloy b871eb3d54 Fix data watcher cloning in 1.16
Fixes #925
2020-08-06 14:26:27 -04:00
Dan Mulloy 12e3a895b3 Remove stray debug 2020-08-06 13:54:16 -04:00
Dan Mulloy 8c5fbe3298 Merge remote-tracking branch 'origin/master' 2020-08-04 19:04:22 -04:00
Dan Mulloy 0512215007 Add modifiers for world keys and moving block position
Fixes #906
Fixes #927
2020-08-04 19:03:59 -04:00
Tarrant 80f4c7b9a7 Support for MerchantRecipeList (#894)
* Added support for MerchantRecipeList found in OPEN_WINDOW_MERCHANT of MC versions 1.13+
2020-07-26 19:33:05 -04:00
Dan Mulloy 8c51b175c4 Merge remote-tracking branch 'origin/master' 2020-07-05 23:47:25 -04:00
Dan Mulloy 8d991ad5a7 Support dimensions in 1.16 (#893)
Fixes #893
2020-07-05 23:47:06 -04:00
Dan Mulloy f19bfc613e Add support for entity equipment pair in 1.16 2020-07-05 13:29:52 -04:00
Lewys Davies a7e702899a Implementing EntityPose (#873) 2020-06-30 23:18:04 -04:00
RERERE deb192b04d Support 1.16 PlayerDigType enumeration variable change (#889) 2020-06-30 23:16:52 -04:00
Dan Mulloy fbe46f7bac Force all chat packets to have empty UUID 2020-06-29 20:35:41 -04:00
Dan Mulloy fd93c1c553 Try to fix reload, give more detailed error (#874)
Fixes #874
2020-06-28 17:06:12 -04:00
Dan Mulloy b2f6a56843 Update tile entities for 1.16 (#880)
Ended up being simpler than I thought
2020-06-28 17:02:22 -04:00
Dan Mulloy 658da31d46 Initial support for 1.16 (#880)
Note that things may not initially work as expected. There are known issues (see #880) relating to dimensions, chat packets, and tile entities. There were also internal changes to attributes, though hopefully those are fixed. As always, report issues on GitHub.
2020-06-28 15:59:30 -04:00
xxDark 77feaa857e Cache perm gen bean (#876) 2020-06-23 16:26:30 -04:00
Niklas Seyfarth 5f204d798c StreamSerializer: Reduce duplicated code and allow (de)serialization to and from byte arrays. (#875) 2020-06-21 12:58:37 -04:00
NewbieOrange 5183bd53b5 Add MCP packet names for Bukkit-Forge hybrid server (#862)
Adds mappings to support MCP packets

Fixes #858
2020-06-16 13:35:38 -04:00
Dan Mulloy fdd30a7b87 Remove a bunch of legacy (<1.8) code
Shouldn't break any servers running 1.8+, but this version is all about code cleanup baby
2020-06-06 15:13:29 -04:00
Dan Mulloy 7ac4ac696f Fold BukkitExecutors into ProtocolLib (#721)
Fixes #721
2020-06-06 13:49:26 -04:00
Dan Mulloy c203fda391 Update version to 4.6.0-SNAPSHOT for development 2020-06-06 13:38:11 -04:00
Dan Mulloy 9a65ddbc43 Update version to 4.5.1 for release 2020-06-03 21:21:31 -04:00
Dan Mulloy 54c252a354 Improve WrappedDataWatcher hasIndex performance
Use the map to check for indices instead of getting the object

Addresses #850
2020-05-27 21:56:22 -04:00
Dan Mulloy e92abda187 Try to fix forge compatibility
Addresses #825
2020-05-27 21:41:56 -04:00
Dan Mulloy 3f7b7f4bb3 Make sure all Spigot forks are included for updater purposes
Addresses #835
2020-05-24 20:35:25 -04:00
Andrew Steinborn 944b3f8280 Cache PacketType#hashCode (#818)
This should result in a general performance improvement, as PacketType is often used as map keys across ProtocolLib.
2020-05-24 15:45:42 -04:00
Dan Mulloy b04fca8324 Fix packet interception error with ViaVersion (#724)
Thanks to @KennyTV and @MedicOP for their help in tracking this bug down. Essentially the decode method we were using could be different depending on when the player logged in, which clashed with PL's static handling of it.

Fixes #724
Fixes #791
Fixes #803
Fixes #811
Fixes #813
Fixes #819
...and probably some others...
2020-05-24 15:29:01 -04:00
Dan Mulloy bfa0eee91e Fix direction and dimension enums 2020-05-03 15:59:55 -04:00
Dan Mulloy 308e3d3417 Update to 1.15.2 2020-05-03 13:15:42 -04:00
Dan Mulloy 42e48aa9b8 Be more specific in which sendPacket method we choose
Related to PaperMC/Paper#3282
2020-05-03 12:44:59 -04:00
Dan Mulloy 18d0193288 Merge remote-tracking branch 'origin/master' 2020-04-30 18:47:06 -04:00
Dan Mulloy 0ab552a6ef Fix issue with legacy block data (#809)
Fixes #809
2020-04-30 18:46:29 -04:00
Dan Mulloy d9fef94bc6 Merge pull request #782 from bergerkiller/master
Replace ThreadLocal scheduleProcessPackets with queue, fixes #763
2020-03-03 09:15:24 -05:00
Irmo van den Berge ab5fb40f8f Replace ThreadLocal scheduleProcessPackets with queue, fixes #763 2020-03-01 13:07:14 +01:00
libraryaddict 4baa4aa724 AttributeModifier$Operation only to be fetched if viable (#773) 2020-02-18 16:12:24 -05:00
Dan Mulloy 7a4a285935 Add some more useful metrics 2020-01-11 13:49:36 -05:00
Dan Mulloy f38c393d6f Use new Spigot updater
Fixes #746
2020-01-11 13:47:49 -05:00
Dan Mulloy 22c2a4abcc Fix compatibility with CraftBukkit
Still recommend Spigot, for what it's worth
2019-12-26 11:39:23 -05:00
Dan Mulloy 1a434e9ea2 Try changing visibility on ProtocolLogger#init
Addresses #740
2019-12-26 11:34:33 -05:00
Dan Mulloy 25a5f1d07f Update version to 4.5.1-SNAPSHOT 2019-12-26 11:28:14 -05:00
Dan Mulloy e2f949c3a7 Update version to 4.5.0 for release 2019-12-24 15:00:28 -05:00
Dan Mulloy c3c59337ff Update to 1.15.1 2019-12-24 14:57:15 -05:00
Dan Mulloy fdf315ba7d Update URLs to HTTPS
Finally got around to it on my ci and nexus
2019-12-24 14:23:05 -05:00
Dan Mulloy f46bd56e8f Fix name of deprecated BED packet
Might fix compatibility with 1.8
2019-12-17 19:42:31 -05:00
Dan Mulloy b1efed0c0c Fix get entity by ID with Citizens and HD
Fixes #729
Addresses filoghost/HolographicDisplays#135
2019-12-17 19:41:53 -05:00
MiniDigger 524fb1ba68 Support multiple EntityTrackerEntryImpls, fixes #732 (#733) 2019-12-16 12:32:26 -05:00
Dan Mulloy 3ff2ccf1b3 Add build number back to version, update maven plugins 2019-12-15 14:50:39 -05:00
Dan Mulloy e915fd0f9a Fix player action enum compatibility with 1.14 and below 2019-12-15 14:50:02 -05:00
Dan Mulloy 6f8b2377b1 Fix last wrapped exception
Fixes #711
2019-12-14 16:54:18 -05:00
Dan Mulloy c893a3f11e Fix player action enum in 1.15 2019-12-13 18:57:21 -05:00
Dan Mulloy 73c71e0198 Update to Minecraft 1.15 2019-12-13 16:19:40 -05:00
Dan Mulloy bb305fdaad Remove legacy packet id 2019-12-11 21:37:21 -05:00
Dan Mulloy b4d4eb29af Fix error filtering reports
Fixes #718
2019-12-05 17:02:16 -05:00
Dan Mulloy 6d9fe45fb4 Tested with 1.14.4 2019-12-05 16:49:22 -05:00
Dan Mulloy b292c8485b Disable strict JavaDoc checking 2019-12-05 16:48:56 -05:00
Dan Mulloy 1e4da2c93b Update issue templates 2019-11-13 12:12:03 -05:00
Dan Mulloy 84e31d032b Fix villager data breaking versions below 1.14 2019-10-30 12:02:15 -04:00
Dan Mulloy 59c8c8c9db Fix WrappedBlockData deep clone
Fixes #661
Fixes #662
2019-10-29 17:01:15 -04:00
Dan Mulloy a76ceb94cc Add modifier for EntityTypes
Fixes #710
2019-10-29 16:52:30 -04:00
Dan Mulloy 9a108af219 Update dependency info in readme 2019-10-24 09:57:58 -04:00
Dan Mulloy 2f09dec1c5 Move back to Maven
Didn't really gain anything from Gradle but it broke some stuff
2019-10-23 19:17:37 -04:00
Felix Klauke 4c43f125f9 Use gradle wrapper instead of travis ci's inbuilt gradle version. (#706)
At the moment travis will always use its pre installed gradle version what leads to unpredictable build behaviour as the version can change at any time. As this project has already defined a gradle wrapper the CI should use it.
2019-10-16 12:07:07 -04:00
Dan Mulloy b5cff49358 Update issue templates 2019-10-01 17:28:36 -04:00
Dan Mulloy 446a1c8326 Clone OptionalInts too 2019-08-07 11:38:50 -04:00
Dan Mulloy 8c20d19339 Try to fix a null pointer
Addresses #665
2019-08-07 11:34:59 -04:00
Dan Mulloy 9dfffe5366 Move Travis to openjdk8
Something about Oracle and licenses. Who woulda thought
2019-08-03 13:10:20 -04:00
Dan Mulloy 19dd81ce94 Fix EntityUtilities updateEntity on 1.14
Fixes #651
2019-08-03 13:03:16 -04:00
Dan Mulloy dd9eac3d6d Add wrapper for villager data
Fixes #637
2019-08-03 12:35:58 -04:00
Dan Mulloy 916434251d Add support for long array nbt type
Fixes #648
2019-08-03 11:46:58 -04:00
Dan Mulloy 62e8d82e16 Update to 1.14.4, add BLOCK_BREAK packet
Also called Acknowledge Player Digging, but we go by what Spigot calls it
2019-08-03 11:41:55 -04:00
BuildTools d7be712959 Add api-version, tweak version pattern to support pre releases
Fixes #638
Fixes #636
Fixes #626
2019-06-23 15:28:40 +02:00
Dan Mulloy 490b1dadcb Fix logger debug when testing 2019-05-07 23:11:05 -04:00
Dan Mulloy 502c5960e3 Fix join/quit exceptions when wrappers break 2019-05-07 22:58:59 -04:00
Dan Mulloy d297e373b4 Specifically clone Spigot's chat components to fix stack overflow
Addresses #601
2019-05-06 20:32:20 -04:00
Dan Mulloy 9f5d95f617 Hopefully fix chat cloning issues
Addresses #601
2019-05-06 16:20:19 -04:00
Dan Mulloy b14b4cc345 Fix entity tracking in 1.14
Fixes #600
2019-05-04 22:41:32 -04:00
Dan Mulloy e41bb8753b Backport some fixes, fix version in plugin yml
Still need to get automatic filtering fixed
2019-05-04 01:45:41 -04:00
Dan Mulloy 565f169e94 Update to Minecraft 1.14 2019-05-03 23:22:44 -04:00
Dan Mulloy 7b7449ee15 Migrate to Gradle with a single module 2018-09-22 17:48:06 -04:00
Dan Mulloy 95bb026fa9 Update parent pom version
Can't wait to move to gradle with just one build file
2018-09-22 17:27:53 -04:00
347 changed files with 7050 additions and 12881 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
github: [dmulloy2]
custom: ["https://paypal.me/dmulloy2"]
+29
View File
@@ -0,0 +1,29 @@
---
name: API question
about: Questions related to using ProtocolLib in your plugins
title: ''
labels: ''
assignees: ''
---
**Make sure you're doing the following**
- [ ] You're using the latest build for your server version
- [ ] This isn't an issue caused by another plugin
- [ ] You've checked for duplicate issues
- [ ] You didn't use `/reload`
**Describe the question**
A clear and concise description of what your question is.
**API method(s) used**
List what API method(s) you're using
**Expected behavior**
A clear and concise description of what you expected to happen.
**Code**
If applicable, add relevant code from your project
**Additional context**
Add any other context about the problem here.
+30
View File
@@ -0,0 +1,30 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Version Info**
Provide your ProtocolLib install info with `/protocol dump` through pastebin.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+4
View File
@@ -175,4 +175,8 @@ pip-log.txt
.DS_Store
# Log4J files
logs/
.gradle/
build/
logs/
+2 -3
View File
@@ -1,12 +1,11 @@
language: java
jdk:
- oraclejdk8
- openjdk8
script: mvn clean test
#before_install: cd ProtocolLib
install: true
notifications:
email:
recipients:
- "dmulloy2@live.com"
on_success: change
on_failure: always
on_failure: always
+169 -169
View File
@@ -1,169 +1,169 @@
# ProtocolLib [![Travis Status](https://travis-ci.org/dmulloy2/ProtocolLib.svg?branch=master)](https://travis-ci.org/dmulloy2/ProtocolLib)
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modify Minecraft directly. A common technique is to modify incoming
and outgoing [packets](http://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](http://www.spigotmc.org/).
### Resources
* [Resource Page](http://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](http://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](http://ci.dmulloy2.net/job/ProtocolLib/javadoc)
### Compilation
ProtocolLib is built with Maven and requires Spigot and SpigotAPI, which can be found [here](http://www.spigotmc.org/wiki/buildtools/).
### A new API
__ProtocolLib__ attempts to solve this problem by providing a event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
You no longer have to reference CraftBukkit!
### Using ProtocolLib
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
as a dependency or soft dependency to your plugin.yml file like any other plugin:
````yml
depend: [ProtocolLib]
````
You can also add ProtocolLib as a Maven dependency:
````xml
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
...
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "http://repo.dmulloy2.net/nexus/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.4.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
private ProtocolManager protocolManager;
public void onLoad() {
protocolManager = ProtocolLibrary.getProtocolManager();
}
````
To listen for packets sent by the server to a client, add a server-side listener:
````java
// Disable all sound effects
protocolManager.addPacketListener(
new PacketAdapter(this, ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
@Override
public void onPacketSending(PacketEvent event) {
// Item packets (id: 0x29)
if (event.getPacketType() ==
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
event.setCancelled(true);
}
}
});
````
It's also possible to read and modify the content of these packets. For instance, you can create a global
censor by listening for Packet3Chat events:
````java
// Censor
protocolManager.addPacketListener(new PacketAdapter(this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT) {
@Override
public void onPacketReceiving(PacketEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
if (message.contains("shit")
|| message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
}
}
});
````
### Sending packets
Normally, you might have to do something ugly like the following:
````java
Packet60Explosion fakeExplosion = new Packet60Explosion();
fakeExplosion.a = player.getLocation().getX();
fakeExplosion.b = player.getLocation().getY();
fakeExplosion.c = player.getLocation().getZ();
fakeExplosion.d = 3.0F;
fakeExplosion.e = new ArrayList<Object>();
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(fakeExplosion);
````
But with ProtocolLib, you can turn that into something more manageable. Notice that
you don't have to create an ArrayList with this version:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles().
write(0, player.getLocation().getX()).
write(1, player.getLocation().getY()).
write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);
try {
protocolManager.sendServerPacket(player, fakeExplosion);
} catch (InvocationTargetException e) {
throw new RuntimeException(
"Cannot send packet " + fakeExplosion, e);
}
````
### Compatibility
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
result is quite flexible. Aside from netty package changes, it should be resilient against future changes.
It's likely that I won't have to update ProtocolLib for anything but bug fixes and new features.
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
# ProtocolLib [![Travis Status](https://travis-ci.org/dmulloy2/ProtocolLib.svg?branch=master)](https://travis-ci.org/dmulloy2/ProtocolLib)
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modify Minecraft directly. A common technique is to modify incoming
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
### Resources
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc)
### Compilation
ProtocolLib is built with Maven and requires Spigot and SpigotAPI, which can be found [here](https://www.spigotmc.org/wiki/buildtools/).
### A new API
__ProtocolLib__ attempts to solve this problem by providing a event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
You no longer have to reference CraftBukkit!
### Using ProtocolLib
To use this library, first add ProtocolLib.jar to your Java build path. Then, add ProtocolLib
as a dependency or soft dependency to your plugin.yml file like any other plugin:
````yml
depend: [ProtocolLib]
````
You can also add ProtocolLib as a Maven dependency:
````xml
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
...
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.6.0</version>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.6.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
private ProtocolManager protocolManager;
public void onLoad() {
protocolManager = ProtocolLibrary.getProtocolManager();
}
````
To listen for packets sent by the server to a client, add a server-side listener:
````java
// Disable all sound effects
protocolManager.addPacketListener(
new PacketAdapter(this, ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
@Override
public void onPacketSending(PacketEvent event) {
// Item packets (id: 0x29)
if (event.getPacketType() ==
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
event.setCancelled(true);
}
}
});
````
It's also possible to read and modify the content of these packets. For instance, you can create a global
censor by listening for Packet3Chat events:
````java
// Censor
protocolManager.addPacketListener(new PacketAdapter(this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT) {
@Override
public void onPacketReceiving(PacketEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
if (message.contains("shit")
|| message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
}
}
});
````
### Sending packets
Normally, you might have to do something ugly like the following:
````java
Packet60Explosion fakeExplosion = new Packet60Explosion();
fakeExplosion.a = player.getLocation().getX();
fakeExplosion.b = player.getLocation().getY();
fakeExplosion.c = player.getLocation().getZ();
fakeExplosion.d = 3.0F;
fakeExplosion.e = new ArrayList<Object>();
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(fakeExplosion);
````
But with ProtocolLib, you can turn that into something more manageable. Notice that
you don't have to create an ArrayList with this version:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles().
write(0, player.getLocation().getX()).
write(1, player.getLocation().getY()).
write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);
try {
protocolManager.sendServerPacket(player, fakeExplosion);
} catch (InvocationTargetException e) {
throw new RuntimeException(
"Cannot send packet " + fakeExplosion, e);
}
````
### Compatibility
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
result is quite flexible. Aside from netty package changes, it should be resilient against future changes.
It's likely that I won't have to update ProtocolLib for anything but bug fixes and new features.
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
File renamed without changes.
-193
View File
@@ -1,193 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>4.4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
<packaging>jar</packaging>
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>ProtocolLib</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
</project>
@@ -1,300 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import com.comphenix.protocol.reflect.IntEnum;
/**
* List of known packet IDs since 1.3.2.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public final class Packets {
/**
* The highest possible packet ID. It's unlikely that this value will ever change.
*/
public static final int MAXIMUM_PACKET_ID = 255;
/**
* The maximum number of unique packet IDs. It's unlikely this will ever change.
*/
public static final int PACKET_COUNT = 256;
/**
* List of packets sent only by the server.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public static final class Server extends IntEnum {
/**
* The singleton instance. Can also be retrieved from the parent class.
*/
private static Server INSTANCE = new Server();
public static final int KEEP_ALIVE = 0;
public static final int LOGIN = 1;
public static final int CHAT = 3;
public static final int UPDATE_TIME = 4;
public static final int ENTITY_EQUIPMENT = 5;
public static final int SPAWN_POSITION = 6;
public static final int UPDATE_HEALTH = 8;
public static final int RESPAWN = 9;
public static final int FLYING = 10;
public static final int PLAYER_POSITION = 11;
public static final int PLAYER_LOOK = 12;
public static final int PLAYER_LOOK_MOVE = 13;
/**
* Made bi-directional in 1.4.6.
*/
public static final int BLOCK_ITEM_SWITCH = 16;
public static final int ENTITY_LOCATION_ACTION = 17;
public static final int ARM_ANIMATION = 18;
public static final int NAMED_ENTITY_SPAWN = 20;
/**
* Removed in 1.4.6 and replaced with VEHICLE_SPAWN.
* @see <a href="http://www.wiki.vg/Protocol_History#2012-12-20">Protocol History - MinecraftCoalition</a>
*/
@Deprecated()
public static final int PICKUP_SPAWN = 21;
public static final int COLLECT = 22;
public static final int VEHICLE_SPAWN = 23;
public static final int MOB_SPAWN = 24;
public static final int ENTITY_PAINTING = 25;
public static final int ADD_EXP_ORB = 26;
public static final int ENTITY_VELOCITY = 28;
public static final int DESTROY_ENTITY = 29;
public static final int ENTITY = 30;
public static final int REL_ENTITY_MOVE = 31;
public static final int ENTITY_LOOK = 32;
public static final int REL_ENTITY_MOVE_LOOK = 33;
public static final int ENTITY_TELEPORT = 34;
public static final int ENTITY_HEAD_ROTATION = 35;
public static final int ENTITY_STATUS = 38;
public static final int ATTACH_ENTITY = 39;
/**
* Sent when an entities DataWatcher is updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int ENTITY_METADATA = 40;
public static final int MOB_EFFECT = 41;
public static final int REMOVE_MOB_EFFECT = 42;
public static final int SET_EXPERIENCE = 43;
public static final int UPDATE_ATTRIBUTES = 44;
public static final int MAP_CHUNK = 51;
public static final int MULTI_BLOCK_CHANGE = 52;
public static final int BLOCK_CHANGE = 53;
public static final int PLAY_NOTE_BLOCK = 54;
public static final int BLOCK_BREAK_ANIMATION = 55;
public static final int MAP_CHUNK_BULK = 56;
public static final int EXPLOSION = 60;
public static final int WORLD_EVENT = 61;
public static final int NAMED_SOUND_EFFECT = 62;
public static final int WORLD_PARTICLES = 63;
public static final int BED = 70;
public static final int WEATHER = 71;
public static final int OPEN_WINDOW = 100;
public static final int CLOSE_WINDOW = 101;
public static final int SET_SLOT = 103;
public static final int WINDOW_ITEMS = 104;
public static final int CRAFT_PROGRESS_BAR = 105;
public static final int TRANSACTION = 106;
public static final int SET_CREATIVE_SLOT = 107;
public static final int UPDATE_SIGN = 130;
public static final int ITEM_DATA = 131;
/**
* Sent the first time a tile entity (chest inventory, etc.) is withing range of the player, or has been updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int TILE_ENTITY_DATA = 132;
public static final int OPEN_TILE_ENTITY = 133;
public static final int STATISTIC = 200;
public static final int PLAYER_INFO = 201;
public static final int ABILITIES = 202;
public static final int TAB_COMPLETE = 203;
public static final int SCOREBOARD_OBJECTIVE = 206;
public static final int UPDATE_SCORE = 207;
public static final int DISPLAY_SCOREBOARD = 208;
public static final int TEAMS = 209;
public static final int CUSTOM_PAYLOAD = 250;
public static final int KEY_RESPONSE = 252;
public static final int KEY_REQUEST = 253;
public static final int KICK_DISCONNECT = 255;
/**
* This packet was introduced in 1.7.2.
*/
public static final int PING_TIME = 230;
/**
* This packet was introduced in 1.7.2.
*/
public static final int LOGIN_SUCCESS = 232;
/**
* A registry that parses between names and packet IDs.
* @return The current server registry.
*/
public static Server getRegistry() {
return INSTANCE;
}
// We only allow a single instance of this class
private Server() {
super();
}
}
/**
* List of packets sent by the client.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public static class Client extends IntEnum {
/**
* The singleton instance. Can also be retrieved from the parent class.
*/
private static Client INSTANCE = new Client();
public static final int KEEP_ALIVE = 0;
public static final int LOGIN = 1;
public static final int HANDSHAKE = 2;
public static final int CHAT = 3;
public static final int USE_ENTITY = 7;
/**
* Since 1.3.1, the client no longer sends a respawn packet. Moved to CLIENT_COMMAND.
*/
@Deprecated
public static final int RESPAWN = 9;
public static final int FLYING = 10;
public static final int PLAYER_POSITION = 11;
public static final int PLAYER_LOOK = 12;
public static final int PLAYER_LOOK_MOVE = 13;
public static final int BLOCK_DIG = 14;
public static final int PLACE = 15;
public static final int BLOCK_ITEM_SWITCH = 16;
public static final int ARM_ANIMATION = 18;
public static final int ENTITY_ACTION = 19;
public static final int PLAYER_INPUT = 27;
public static final int CLOSE_WINDOW = 101;
public static final int WINDOW_CLICK = 102;
public static final int TRANSACTION = 106;
public static final int SET_CREATIVE_SLOT = 107;
public static final int BUTTON_CLICK = 108;
public static final int UPDATE_SIGN = 130;
public static final int ABILITIES = 202;
public static final int TAB_COMPLETE = 203;
public static final int LOCALE_AND_VIEW_DISTANCE = 204;
public static final int CLIENT_COMMAND = 205;
public static final int CUSTOM_PAYLOAD = 250;
public static final int KEY_RESPONSE = 252;
public static final int GET_INFO = 254;
public static final int KICK_DISCONNECT = 255;
/**
* This packet was introduced in 1.7.2.
*/
public static final int PING_TIME = 230;
/**
* This packet was introduced in 1.7.2.
*/
public static final int LOGIN_START = 231;
/**
* A registry that parses between names and packet IDs.
* @return The current client registry.
*/
public static Client getRegistry() {
return INSTANCE;
}
// Like above
private Client() {
super();
}
}
/**
* A registry that parses between names and packet IDs.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @return The current client registry.
*/
@Deprecated
public static Server getServerRegistry() {
return Server.getRegistry();
}
/**
* A registry that parses between names and packet IDs.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @return The current server registry.
*/
@Deprecated
public static Client getClientRegistry() {
return Client.INSTANCE;
}
/**
* Find a packet by name. Must be capitalized and use underscores.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @param name - name of packet to find.
* @return The packet ID found.
*/
@Deprecated
public static int valueOf(String name) {
Integer serverAttempt = Server.INSTANCE.valueOf(name);
if (serverAttempt != null)
return serverAttempt;
else
return Client.INSTANCE.valueOf(name);
}
/**
* Retrieves the name of a packet.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @param packetID - packet to retrieve name.
* @return The name, or NULL if unable to find such a packet.
*/
@Deprecated
public static String getDeclaredName(int packetID) {
String serverAttempt = Server.INSTANCE.getDeclaredName(packetID);
if (serverAttempt != null)
return serverAttempt;
else
return Client.INSTANCE.getDeclaredName(packetID);
}
}
@@ -1,242 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import com.comphenix.protocol.utility.EnhancerFactory;
/**
* Represents a player object that can be serialized by Java.
*
* @author Kristian
*/
class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
/**
* Generated by Eclipse.
*/
private static final long serialVersionUID = -2728976288470282810L;
private transient Location bedSpawnLocation;
// Relevant data about an offline player
private String name;
private UUID uuid;
private long firstPlayed;
private long lastPlayed;
private boolean operator;
private boolean banned;
private boolean playedBefore;
private boolean online;
private boolean whitelisted;
// Proxy helper
private static Map<String, Method> lookup = new ConcurrentHashMap<String, Method>();
/**
* Constructor used by serialization.
*/
public SerializedOfflinePlayer() {
// Do nothing
}
/**
* Initialize this serializable offline player from another player.
* @param offline - another player.
*/
public SerializedOfflinePlayer(OfflinePlayer offline) {
this.name = offline.getName();
this.uuid = offline.getUniqueId();
this.firstPlayed = offline.getFirstPlayed();
this.lastPlayed = offline.getLastPlayed();
this.operator = offline.isOp();
this.banned = offline.isBanned();
this.playedBefore = offline.hasPlayedBefore();
this.online = offline.isOnline();
this.whitelisted = offline.isWhitelisted();
}
@Override
public boolean isOp() {
return operator;
}
@Override
public void setOp(boolean operator) {
this.operator = operator;
}
@Override
public Map<String, Object> serialize() {
throw new UnsupportedOperationException();
}
@Override
public Location getBedSpawnLocation() {
return bedSpawnLocation;
}
@Override
public long getFirstPlayed() {
return firstPlayed;
}
@Override
public long getLastPlayed() {
return lastPlayed;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String getName() {
return name;
}
@Override
public boolean hasPlayedBefore() {
return playedBefore;
}
@Override
public boolean isBanned() {
return banned;
}
public void setBanned(boolean banned) {
this.banned = banned;
}
@Override
public boolean isOnline() {
return online;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
}
@Override
public void setWhitelisted(boolean whitelisted) {
this.whitelisted = whitelisted;
}
private void writeObject(ObjectOutputStream output) throws IOException {
output.defaultWriteObject();
// Serialize the bed spawn location
output.writeUTF(bedSpawnLocation.getWorld().getName());
output.writeDouble(bedSpawnLocation.getX());
output.writeDouble(bedSpawnLocation.getY());
output.writeDouble(bedSpawnLocation.getZ());
}
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
input.defaultReadObject();
// Well, this is a problem
bedSpawnLocation = new Location(
getWorld(input.readUTF()),
input.readDouble(),
input.readDouble(),
input.readDouble()
);
}
private World getWorld(String name) {
try {
// Try to get the world at least
return Bukkit.getServer().getWorld(name);
} catch (Exception e) {
// Screw it
return null;
}
}
@Override
public Player getPlayer() {
try {
// Try to get the real player underneath
return Bukkit.getServer().getPlayerExact(name);
} catch (Exception e) {
return getProxyPlayer();
}
}
/**
* Retrieve a player object that implements OfflinePlayer by refering to this object.
* <p>
* All other methods cause an exception.
* @return Proxy object.
*/
public Player getProxyPlayer() {
// Remember to initialize the method filter
if (lookup.size() == 0) {
// Add all public methods
for (Method method : OfflinePlayer.class.getMethods()) {
lookup.put(method.getName(), method);
}
}
// MORE CGLIB magic!
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
ex.setSuperclass(Player.class);
ex.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// There's no overloaded methods, so we don't care
Method offlineMethod = lookup.get(method.getName());
// Ignore all other methods
if (offlineMethod == null) {
throw new UnsupportedOperationException(
"The method " + method.getName() + " is not supported for offline players.");
}
// Invoke our on method
return offlineMethod.invoke(SerializedOfflinePlayer.this, args);
}
});
return (Player) ex.create();
}
}
@@ -1,108 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import java.util.Map;
import java.util.Map.Entry;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.injector.packet.MapContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.common.collect.Maps;
/**
* @author dmulloy2
*/
public class NettyProtocolRegistry extends ProtocolRegistry {
public NettyProtocolRegistry() {
super();
}
@Override
protected synchronized void initialize() {
Object[] protocols = enumProtocol.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
StructureModifier<Object> modifier = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (modifier == null)
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
for (Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, entry.getValue());
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, entry.getValue());
}
}
}
// Maps we have to occasionally check have changed
for (Map<Integer, Class<?>> map : serverMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Map<Integer, Class<?>> map : clientMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol))
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
if (clientMaps.containsKey(protocol))
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
}
// Exchange (thread safe, as we have only one writer)
this.register = result;
}
@Override
protected void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
try {
register.typeToClass.put(type, entry.getValue());
if (sender == Sender.SERVER)
register.serverPackets.add(type);
if (sender == Sender.CLIENT)
register.clientPackets.add(type);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
}
}
}
@@ -1,33 +0,0 @@
/**
* (c) 2018 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Optional;
/**
* A cloner that can clone Java Optional objects
* @author dmulloy2
*/
public class JavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public JavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional;
}
@Override
public Object clone(Object source) {
Optional<?> optional = (Optional<?>) source;
return optional.map(o -> wrapped.clone(o));
}
public Cloner getWrapped() {
return wrapped;
}
}
@@ -1,44 +0,0 @@
package com.comphenix.protocol.utility;
import net.sf.cglib.proxy.Enhancer;
/**
* Represents a shared enchancer factory.
* @author Kristian
*/
public class EnhancerFactory {
private static EnhancerFactory INSTANCE = new EnhancerFactory();
// The current class loader
private ClassLoader loader = EnhancerFactory.class.getClassLoader();
public static EnhancerFactory getInstance() {
return INSTANCE;
}
/**
* Create a new CGLib enhancer.
* @return The new enhancer.
*/
public Enhancer createEnhancer() {
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(loader);
return enhancer;
}
/**
* Set the current class loader to use when constructing enhancers.
* @param loader - the class loader
*/
public void setClassLoader(ClassLoader loader) {
this.loader = loader;
}
/**
* Get the current class loader we are using.
* @return The current class loader.
*/
public ClassLoader getClassLoader() {
return loader;
}
}
-265
View File
@@ -1,265 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
<packaging>jar</packaging>
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>1.7.4</powermock.version>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.sf</pattern>
<shadedPattern>com.comphenix.net.sf</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
<exclude>io.netty:*</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,170 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import com.comphenix.protocol.async.AsyncListenerHandler;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.StructureCompiler;
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
/**
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
*
* @author Kristian
*/
class CleanupStaticMembers {
// Reports
public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s");
public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s.");
private ClassLoader loader;
private ErrorReporter reporter;
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
this.loader = loader;
this.reporter = reporter;
}
/**
* Ensure that the previous ClassLoader is not leaking.
*/
public void resetAll() {
// This list must always be updated
@SuppressWarnings("deprecation")
Class<?>[] publicClasses = {
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
BackgroundCompiler.class, StructureCompiler.class,
ObjectWriter.class, Packets.Server.class, Packets.Client.class,
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class,
AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class,
MinecraftReflection.class, NbtBinarySerializer.class
};
String[] internalClasses = {
"com.comphenix.protocol.events.SerializedOfflinePlayer",
"com.comphenix.protocol.injector.player.InjectedServerConnection",
"com.comphenix.protocol.injector.player.NetworkFieldInjector",
"com.comphenix.protocol.injector.player.NetworkObjectInjector",
"com.comphenix.protocol.injector.player.NetworkServerInjector",
"com.comphenix.protocol.injector.player.PlayerInjector",
"com.comphenix.protocol.injector.EntityUtilities",
"com.comphenix.protocol.injector.packet.PacketRegistry",
"com.comphenix.protocol.injector.packet.PacketInjector",
"com.comphenix.protocol.injector.packet.ReadPacketModifier",
"com.comphenix.protocol.injector.StructureCache",
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
"com.comphenix.protocol.reflect.compiler.MethodDescriptor",
"com.comphenix.protocol.wrappers.nbt.WrappedElement",
};
resetClasses(publicClasses);
resetClasses(getClasses(loader, internalClasses));
}
private void resetClasses(Class<?>[] classes) {
// Reset each class one by one
for (Class<?> clazz : classes) {
resetClass(clazz);
}
}
private void resetClass(Class<?> clazz) {
for (Field field : clazz.getFields()) {
Class<?> type = field.getType();
// Only check static non-primitive fields. We also skip strings.
if (Modifier.isStatic(field.getModifiers()) &&
!type.isPrimitive() && !type.equals(String.class) &&
!type.equals(ReportType.class)) {
try {
setFinalStatic(field, null);
} catch (IllegalAccessException e) {
// Just inform the player
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
);
e.printStackTrace();
}
}
}
}
// HACK! HAACK!
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
int modifier = field.getModifiers();
boolean isFinal = Modifier.isFinal(modifier);
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
// We have to remove the final field first
if (isFinal) {
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
}
// Now we can safely modify the field
FieldUtils.writeStaticField(field, newValue, true);
// Revert modifier
if (isFinal) {
FieldUtils.writeField(modifiersField, field, modifier, true);
}
}
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
List<Class<?>> output = new ArrayList<Class<?>>();
for (String name : names) {
try {
output.add(loader.loadClass(name));
} catch (ClassNotFoundException e) {
// Warn the user
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name));
}
}
return output.toArray(new Class<?>[0]);
}
}
@@ -1,524 +0,0 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* A protocol manager that delays all packet listener registrations and unregistrations until
* an underlying protocol manager can be constructed.
*
* @author Kristian
*/
public class DelayedPacketManager implements InternalManager {
// Registering packet IDs that are not supported
public static final ReportType REPORT_CANNOT_SEND_QUEUED_PACKET = new ReportType("Cannot send queued packet %s.");
public static final ReportType REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET = new ReportType("Cannot send queued wire packet %s.");
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
private volatile InternalManager delegate;
// Queued actions
private final List<Runnable> queuedActions = Collections.synchronizedList(Lists.<Runnable>newArrayList());
private final List<PacketListener> queuedListeners = Collections.synchronizedList(Lists.<PacketListener>newArrayList());
private AsynchronousManager asyncManager;
private ErrorReporter reporter;
// The current hook
private PlayerInjectHooks hook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
// If we have been closed
private boolean closed;
private boolean debug;
// Queued registration
private PluginManager queuedManager;
private Plugin queuedPlugin;
private MinecraftVersion version;
public DelayedPacketManager(@Nonnull ErrorReporter reporter, @Nonnull MinecraftVersion version) {
Preconditions.checkNotNull(reporter, "reporter cannot be NULL.");
Preconditions.checkNotNull(version, "version cannot be NULL.");
this.reporter = reporter;
this.version = version;
}
/**
* Retrieve the underlying protocol manager.
* @return The underlying manager.
*/
public InternalManager getDelegate() {
return delegate;
}
@Override
public int getProtocolVersion(Player player) {
if (delegate != null)
return delegate.getProtocolVersion(player);
else
return Integer.MIN_VALUE;
}
@Override
public MinecraftVersion getMinecraftVersion() {
if (delegate != null)
return delegate.getMinecraftVersion();
else
return version;
}
/**
* Update the delegate to the underlying manager.
* <p>
* This will prompt this packet manager to immediately transmit and
* register all queued packets an listeners.
* @param delegate - delegate to the new manager.
*/
protected void setDelegate(InternalManager delegate) {
this.delegate = delegate;
if (delegate != null) {
// Update the hook if needed
if (!Objects.equal(delegate.getPlayerHook(), hook)) {
delegate.setPlayerHook(hook);
}
// Register events as well
if (queuedManager != null && queuedPlugin != null) {
delegate.registerEvents(queuedManager, queuedPlugin);
}
// And update the debug mode
delegate.setDebug(debug);
// Add any pending listeners
synchronized (queuedListeners) {
for (PacketListener listener : queuedListeners) {
try {
delegate.addPacketListener(listener);
} catch (IllegalArgumentException e) {
// Inform about this plugin error
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
callerParam(delegate).messageParam(listener).error(e));
}
}
}
// Execute any delayed actions
synchronized (queuedActions) {
for (Runnable action : queuedActions) {
action.run();
}
}
// Don't keep this around anymore
queuedListeners.clear();
queuedActions.clear();
}
}
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
final NetworkMarker marker, final boolean filtered) {
return new Runnable() {
@Override
public void run() {
try {
// Attempt to send it now
switch (side) {
case CLIENT_SIDE:
delegate.recieveClientPacket(player, packet, marker, filtered);
break;
case SERVER_SIDE:
delegate.sendServerPacket(player, packet, marker, filtered);
break;
default:
throw new IllegalArgumentException("side cannot be " + side);
}
} catch (Exception e) {
// Inform about this plugin error
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
callerParam(delegate).messageParam(packet).error(e));
}
}
};
}
@Override
public void setPlayerHook(PlayerInjectHooks playerHook) {
this.hook = playerHook;
}
@Override
public PlayerInjectHooks getPlayerHook() {
return hook;
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet) throws InvocationTargetException {
sendServerPacket(receiver, packet, null, true);
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters) throws InvocationTargetException {
sendServerPacket(receiver, packet, null, filters);
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
if (delegate != null) {
delegate.sendServerPacket(receiver, packet, marker, filters);
} else {
queuedActions.add(queuedAddPacket(ConnectionSide.SERVER_SIDE, receiver, packet, marker, filters));
}
}
@Override
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException {
WirePacket packet = new WirePacket(id, bytes);
sendWirePacket(receiver, packet);
}
@Override
public void sendWirePacket(final Player receiver, final WirePacket packet) throws InvocationTargetException {
if (delegate != null) {
delegate.sendWirePacket(receiver, packet);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
try {
delegate.sendWirePacket(receiver, packet);
} catch (Throwable ex) {
// Inform about this plugin error
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET)
.callerParam(delegate)
.messageParam(packet)
.error(ex));
}
}
});
}
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, true);
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, filters);
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) throws IllegalAccessException, InvocationTargetException {
if (delegate != null) {
delegate.recieveClientPacket(sender, packet, marker, filters);
} else {
queuedActions.add(queuedAddPacket(ConnectionSide.CLIENT_SIDE, sender, packet, marker, filters));
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet, final Entity entity, final boolean includeTracker) {
if (delegate != null) {
delegate.broadcastServerPacket(packet, entity, includeTracker);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet, entity, includeTracker);
}
});
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet, final Location origin, final int maxObserverDistance) {
if (delegate != null) {
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
}
});
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet) {
if (delegate != null) {
delegate.broadcastServerPacket(packet);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet);
}
});
}
}
@Override
public ImmutableSet<PacketListener> getPacketListeners() {
if (delegate != null)
return delegate.getPacketListeners();
else
return ImmutableSet.copyOf(queuedListeners);
}
@Override
public void addPacketListener(PacketListener listener) {
if (delegate != null)
delegate.addPacketListener(listener);
else
queuedListeners.add(listener);
}
@Override
public void removePacketListener(PacketListener listener) {
if (delegate != null)
delegate.removePacketListener(listener);
else
queuedListeners.remove(listener);
}
@Override
public void removePacketListeners(Plugin plugin) {
if (delegate != null) {
delegate.removePacketListeners(plugin);
} else {
for (Iterator<PacketListener> it = queuedListeners.iterator(); it.hasNext(); ) {
// Remove listeners of the same plugin
if (Objects.equal(it.next().getPlugin(), plugin)) {
it.remove();
}
}
}
}
@Override
@Deprecated
public PacketContainer createPacket(int id) {
if (delegate != null)
return delegate.createPacket(id);
return createPacket(id, true);
}
@Override
@Deprecated
public PacketContainer createPacket(int id, boolean forceDefaults) {
if (delegate != null) {
return delegate.createPacket(id);
} else {
// Fallback implementation
PacketContainer packet = new PacketContainer(id);
// Use any default values if possible
if (forceDefaults) {
try {
packet.getModifier().writeDefaults();
} catch (FieldAccessException e) {
throw new RuntimeException("Security exception.", e);
}
}
return packet;
}
}
@SuppressWarnings("deprecation")
@Override
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
if (delegate != null)
return delegate.createPacketConstructor(id, arguments);
else
return PacketConstructor.DEFAULT.withPacket(id, arguments);
}
@Override
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
if (delegate != null)
return delegate.createPacketConstructor(type, arguments);
else
return PacketConstructor.DEFAULT.withPacket(type, arguments);
}
@Override
@Deprecated
public Set<Integer> getSendingFilters() {
if (delegate != null) {
return delegate.getSendingFilters();
} else {
// Linear scan is fast enough here
Set<Integer> sending = Sets.newHashSet();
for (PacketListener listener : queuedListeners) {
sending.addAll(listener.getSendingWhitelist().getWhitelist());
}
return sending;
}
}
@Override
@Deprecated
public Set<Integer> getReceivingFilters() {
if (delegate != null) {
return delegate.getReceivingFilters();
} else {
Set<Integer> recieving = Sets.newHashSet();
for (PacketListener listener : queuedListeners) {
recieving.addAll(listener.getReceivingWhitelist().getWhitelist());
}
return recieving;
}
}
@Override
public PacketContainer createPacket(PacketType type) {
return createPacket(type.getLegacyId());
}
@Override
public PacketContainer createPacket(PacketType type, boolean forceDefaults) {
return createPacket(type.getLegacyId(), forceDefaults);
}
@Override
public Set<PacketType> getSendingFilterTypes() {
return PacketRegistry.toPacketTypes(getSendingFilters(), Sender.SERVER);
}
@Override
public Set<PacketType> getReceivingFilterTypes() {
return PacketRegistry.toPacketTypes(getReceivingFilters(), Sender.CLIENT);
}
@Override
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (delegate != null)
delegate.updateEntity(entity, observers);
else
EntityUtilities.updateEntity(entity, observers);
}
@Override
public Entity getEntityFromID(World container, int id) throws FieldAccessException {
if (delegate != null)
return delegate.getEntityFromID(container, id);
else
return EntityUtilities.getEntityFromID(container, id);
}
@Override
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException {
if (delegate != null)
return delegate.getEntityTrackers(entity);
else
return EntityUtilities.getEntityTrackers(entity);
}
@Override
public boolean isClosed() {
return closed || (delegate != null && delegate.isClosed());
}
@Override
public AsynchronousManager getAsynchronousManager() {
if (delegate != null)
return delegate.getAsynchronousManager();
else
return asyncManager;
}
@Override
public boolean isDebug() {
return debug;
}
@Override
public void setDebug(boolean debug) {
this.debug = debug;
if (delegate != null) {
delegate.setDebug(debug);
}
}
/**
* Update the asynchronous manager. This must be set.
* @param asyncManager - the asynchronous manager.
*/
public void setAsynchronousManager(AsynchronousManager asyncManager) {
this.asyncManager = asyncManager;
}
@Override
public void registerEvents(PluginManager manager, Plugin plugin) {
if (delegate != null) {
delegate.registerEvents(manager, plugin);
} else {
queuedManager = manager;
queuedPlugin = plugin;
}
}
@Override
public void close() {
if (delegate != null)
delegate.close();
closed = true;
}
@Override
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
for (PacketType type : whitelist.getTypes()) {
if (type == null) {
throw new IllegalArgumentException(String.format("Packet type in in listener %s was NULL.",
PacketAdapter.getPluginName(listener))
);
}
}
}
}
@@ -1,256 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.Validate;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
import com.google.common.collect.Lists;
/**
* Used to perform certain operations on entities.
*
* @author Kristian
*/
class EntityUtilities {
private static Field entityTrackerField;
private static Field trackedEntitiesField;
private static Field trackedPlayersField;
private static Field trackerField;
private static Method scanPlayersMethod;
/*
public static void updateEntity2(Entity entity, List<Player> observers) {
EntityTrackerEntry entry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
entry.trackedPlayers.removeAll(nmsPlayers);
entry.scanPlayers(nmsPlayers);
}
*/
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (entity == null || !entity.isValid()) {
return;
}
try {
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + ".");
}
if (trackedPlayersField == null) {
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
// Phew, finally there.
Collection<?> trackedPlayers = getTrackedPlayers(trackedPlayersField, trackerEntry);
List<Object> nmsPlayers = unwrapBukkit(observers);
trackedPlayers.removeAll(nmsPlayers);
// We have to rely on a NAME once again. Damn it.
// TODO: Make sure this stays up to date with version changes - 1.8 - 1.10
if (scanPlayersMethod == null) {
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
}
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
} catch (IllegalArgumentException e) {
throw e;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Security limitation prevents access to 'get' method in IntHashMap", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Exception occurred in Minecraft.", e);
} catch (SecurityException e) {
throw new FieldAccessException("Security limitation prevents access to 'scanPlayers' method in trackerEntry.", e);
} catch (NoSuchMethodException e) {
throw new FieldAccessException("Cannot find 'scanPlayers' method. Is ProtocolLib up to date?", e);
}
}
/**
* Retrieve every client that is receiving information about a given entity.
* @param entity - the entity that is being tracked.
* @return Every client/player that is tracking the given entity.
* @throws FieldAccessException If reflection failed.
*/
public static List<Player> getEntityTrackers(Entity entity) {
if (entity == null || !entity.isValid()) {
return new ArrayList<>();
}
try {
List<Player> result = new ArrayList<Player>();
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + ".");
}
if (trackedPlayersField == null) {
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
Collection<?> trackedPlayers = getTrackedPlayers(trackedPlayersField, trackerEntry);
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
return result;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Security limitation prevented access to the list of tracked players.", e);
}
}
// Damn you, Paper
private static Collection<?> getTrackedPlayers(Field field, Object entry) throws IllegalAccessException {
Validate.notNull(field, "Cannot find 'trackedPlayers' field.");
Validate.notNull(entry, "entry cannot be null!");
Object value = FieldUtils.readField(field, entry, false);
if (value instanceof Collection) {
return (Collection<?>) value;
} else if (value instanceof Map) {
return ((Map<?, ?>) value).keySet();
} else {
// Please. No more changes.
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Collection or Map, but got " + value.getClass());
}
}
/*
private static EntityTrackerEntry getEntityTrackerEntry2(World world, int entityID) {
WorldServer worldServer = ((CraftWorld) world).getHandle();
EntityTracker tracker = worldServer.tracker;
return tracker.trackedEntities.get(entityID);
}
*/
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
if (entityTrackerField == null)
entityTrackerField = FuzzyReflection.fromObject(worldServer).
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass());
// Get the tracker
Object tracker = null;
try {
tracker = FieldUtils.readField(entityTrackerField, worldServer, false);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'tracker' field due to security limitations.", e);
}
// Looking for an IntHashMap in the tracker entry
if (trackedEntitiesField == null) {
trackedEntitiesField = FuzzyReflection.fromObject(tracker, false)
.getFieldByType("trackedEntities", MinecraftReflection.getIntHashMapClass());
}
// Read the map
Object trackedEntities = null;
try {
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, false);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
}
return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
}
/**
* Retrieve entity from a ID, even it it's newly created.
* @return The associated entity.
* @throws FieldAccessException Reflection error.
*/
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
try {
Object trackerEntry = getEntityTrackerEntry(world, entityID);
Object tracker = null;
// Handle NULL cases
if (trackerEntry != null) {
if (trackerField == null) {
try {
Class<?> entryClass = MinecraftReflection.getMinecraftClass("EntityTrackerEntry");
trackerField = entryClass.getDeclaredField("tracker");
} catch (NoSuchFieldException e) {
// Assume it's the first entity field then
trackerField = FuzzyReflection.fromObject(trackerEntry, true)
.getFieldByType("tracker", MinecraftReflection.getEntityClass());
}
}
tracker = FieldUtils.readField(trackerField, trackerEntry, true);
}
// If the tracker is NULL, we'll just assume this entity doesn't exist
if (tracker != null)
return (Entity) MinecraftReflection.getBukkitEntity(tracker);
else
return null;
} catch (Exception e) {
throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
}
}
private static List<Object> unwrapBukkit(List<Player> players) {
List<Object> output = Lists.newArrayList();
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
// Get the NMS equivalent
for (Player player : players) {
Object result = unwrapper.unwrapItem(player);
if (result != null)
output.add(result);
else
throw new IllegalArgumentException("Cannot unwrap item " + player);
}
return output;
}
}
@@ -1,92 +0,0 @@
package com.comphenix.protocol.injector;
import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
/**
* Packets that are known to be transmitted during login.
* <p>
* This may be dynamically extended later.
* @author Kristian
*/
class LoginPackets {
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
@SuppressWarnings("deprecation")
public LoginPackets(MinecraftVersion version) {
// Ordinary login
clientSide.add(Packets.Client.HANDSHAKE);
serverSide.add(Packets.Server.KEY_REQUEST);
clientSide.add(Packets.Client.KEY_RESPONSE);
serverSide.add(Packets.Server.KEY_RESPONSE);
clientSide.add(Packets.Client.CLIENT_COMMAND);
serverSide.add(Packets.Server.LOGIN);
// List ping
clientSide.add(Packets.Client.GET_INFO);
// In 1.6.2, Minecraft started sending CUSTOM_PAYLOAD in the server list protocol
// MCPC+/Cauldron contains Forge, which uses CUSTOM_PAYLOAD during login
if (version.isAtLeast(MinecraftVersion.HORSE_UPDATE) || isCauldronOrMCPC()) {
clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
}
if (isCauldronOrMCPC()) {
serverSide.add(Packets.Server.CUSTOM_PAYLOAD);
}
serverSide.add(Packets.Server.KICK_DISCONNECT);
}
/**
* Determine if we are running MCPC or Cauldron.
* @return TRUE if we are, FALSE otherwise.
*/
private static boolean isCauldronOrMCPC() {
String version = Bukkit.getServer().getVersion();
return version.contains("MCPC") || version.contains("Cauldron");
}
/**
* Determine if a packet may be sent during login from a given direction.
* @param packetId - the ID of the packet.
* @param side - the direction.
* @return TRUE if it may, FALSE otherwise.
*/
@Deprecated
public boolean isLoginPacket(int packetId, ConnectionSide side) {
switch (side) {
case CLIENT_SIDE:
return clientSide.contains(packetId);
case SERVER_SIDE:
return serverSide.contains(packetId);
case BOTH:
return clientSide.contains(packetId) ||
serverSide.contains(packetId);
default:
throw new IllegalArgumentException("Unknown connection side: " + side);
}
}
/**
* Determine if a given packet may be sent during login.
* @param type - the packet type.
* @return TRUE if it may, FALSE otherwise.
*/
public boolean isLoginPacket(PacketType type) {
if (!MinecraftReflection.isUsingNetty())
return isLoginPacket(type.getLegacyId(), type.getSender().toSide());
return PacketType.Login.Client.getInstance().hasMember(type) ||
PacketType.Login.Server.getInstance().hasMember(type) ||
PacketType.Status.Client.getInstance().hasMember(type) ||
PacketType.Status.Server.getInstance().hasMember(type);
}
}
@@ -1,64 +0,0 @@
package com.comphenix.protocol.injector.packet;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Represents an input stream that stores every read block of bytes in another output stream.
*
* @author Kristian
*/
class CaptureInputStream extends FilterInputStream {
protected OutputStream out;
public CaptureInputStream(InputStream in, OutputStream out) {
super(in);
this.out = out;
}
@Override
public int read() throws IOException {
int value = super.read();
// Write the byte
if (value >= 0)
out.write(value);
return value;
}
@Override
public void close() throws IOException {
super.close();
out.close();
}
@Override
public int read(byte[] b) throws IOException {
int count = super.read(b);
if (count > 0 ) {
out.write(b, 0, count);
}
return count;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int count = super.read(b, off, len);
if (count > 0 ) {
out.write(b, off, count);
}
return count;
}
/**
* Retrieve the output stream that receives all the read information.
* @return The output stream everything is copied to.
*/
public OutputStream getOutputStream() {
return out;
}
}
@@ -1,154 +0,0 @@
package com.comphenix.protocol.injector.packet;
import java.io.DataOutput;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.MethodInfo;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
/**
* Retrieve a packet instance that has its write method intercepted.
* @author Kristian
*/
public class InterceptWritePacket {
public static final ReportType REPORT_CANNOT_FIND_WRITE_PACKET_METHOD = new ReportType("Cannot find write packet method in %s.");
public static final ReportType REPORT_CANNOT_CONSTRUCT_WRITE_PROXY = new ReportType("Cannot construct write proxy packet %s.");
/**
* Matches the readPacketData(DataInputStream) method in Packet.
*/
private static FuzzyMethodContract WRITE_PACKET = FuzzyMethodContract.newBuilder().
returnTypeVoid().
parameterDerivedOf(DataOutput.class).
parameterCount(1).
build();
private CallbackFilter filter;
private boolean writePacketIntercepted;
private ConcurrentMap<Integer, Class<?>> proxyClasses = Maps.newConcurrentMap();
private ErrorReporter reporter;
private WritePacketModifier modifierWrite;
private WritePacketModifier modifierRest;
public InterceptWritePacket(ErrorReporter reporter) {
this.reporter = reporter;
// Initialize modifiers
this.modifierWrite = new WritePacketModifier(reporter, true);
this.modifierRest = new WritePacketModifier(reporter, false);
}
// TODO: PacketId should probably do something...
private Class<?> createProxyClass() {
// Construct the proxy object
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
// Attempt to share callback filter
if (filter == null) {
filter = new CallbackFilter() {
@Override
public int accept(Method method) {
// Skip methods defined in Object
if (WRITE_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
writePacketIntercepted = true;
return 0;
} else {
return 1;
}
}
};
}
// Subclass the generic packet class
ex.setSuperclass(MinecraftReflection.getPacketClass());
ex.setCallbackFilter(filter);
ex.setUseCache(false);
ex.setCallbackTypes( new Class[] { WritePacketModifier.class, WritePacketModifier.class });
Class<?> proxyClass = ex.createClass();
// Register write modifiers too
Enhancer.registerStaticCallbacks(proxyClass, new Callback[] { modifierWrite, modifierRest });
if (proxyClass != null) {
// Check that we found the read method
if (!writePacketIntercepted) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_FIND_WRITE_PACKET_METHOD).
messageParam(MinecraftReflection.getPacketClass()));
}
}
return proxyClass;
}
@SuppressWarnings("deprecation")
private Class<?> getProxyClass(int packetId) {
Class<?> stored = proxyClasses.get(packetId);
// Concurrent pattern
if (stored == null) {
final Class<?> created = createProxyClass();
stored = proxyClasses.putIfAbsent(packetId, created);
// We won!
if (stored == null) {
stored = created;
PacketRegistry.getPacketToID().put(stored, packetId);
}
}
return stored;
}
/**
* Construct a new instance of the proxy object.
* @param proxyObject - Object to construct proxy of
* @param event - Packet event
* @param marker - Network marker
* @return New instance of the proxy, or null if we failed.
*/
@SuppressWarnings("deprecation")
public Object constructProxy(Object proxyObject, PacketEvent event, NetworkMarker marker) {
Class<?> proxyClass = null;
try {
proxyClass = getProxyClass(event.getPacketID());
Object generated = proxyClass.newInstance();
modifierWrite.register(generated, proxyObject, event, marker);
modifierRest.register(generated, proxyObject, event, marker);
return generated;
} catch (Exception e) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_CONSTRUCT_WRITE_PROXY).
messageParam(proxyClass));
return null;
}
}
/**
* Invoked when the write packet proxy class should be removed.
*/
@SuppressWarnings("deprecation")
public void cleanup() {
// Remove all proxy classes from the registry
for (Class<?> stored : proxyClasses.values()) {
PacketRegistry.getPacketToID().remove(stored);
}
}
}
@@ -1,66 +0,0 @@
package com.comphenix.protocol.injector.packet;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ForwardingMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
public class InverseMaps {
private InverseMaps() {
// Not constructable
}
public static <K, V> Multimap<K, V> inverseMultimap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
final MapContainer container = new MapContainer(map);
return new ForwardingMultimap<K, V>() {
// The cached multimap
private Multimap<K, V> inverseMultimap;
@Override
protected Multimap<K, V> delegate() {
if (container.hasChanged()) {
inverseMultimap = HashMultimap.create();
// Construct the inverse map
for (Map.Entry<V, K> entry : map.entrySet()) {
if (filter.apply(entry)) {
inverseMultimap.put(entry.getValue(), entry.getKey());
}
}
container.setChanged(false);
}
return inverseMultimap;
}
};
}
public static <K, V> Map<K, V> inverseMap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
final MapContainer container = new MapContainer(map);
return new ForwardingMap<K, V>() {
// The cached map
private Map<K, V> inverseMap;
@Override
protected Map<K, V> delegate() {
if (container.hasChanged()) {
inverseMap = Maps.newHashMap();
// Construct the inverse map
for (Map.Entry<V, K> entry : map.entrySet()) {
if (filter.apply(entry)) {
inverseMap.put(entry.getValue(), entry.getKey());
}
}
container.setChanged(false);
}
return inverseMap;
}
};
}
}
@@ -1,65 +0,0 @@
package com.comphenix.protocol.injector.packet;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.NetworkMarker;
import com.google.common.io.ByteSource;
import com.google.common.primitives.Bytes;
/**
* Represents a network marker for 1.6.4 and earlier.
* @author Kristian
*/
public class LegacyNetworkMarker extends NetworkMarker {
public LegacyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
super(side, inputBuffer, type);
}
public LegacyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
super(side, inputBuffer, type);
}
@Override
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
// This has already been done
return input;
}
@Override
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
return ByteBuffer.wrap(Bytes.concat(new byte[] { (byte) type.getLegacyId() }, buffer.array()));
}
@Override
protected DataInputStream addHeader(final DataInputStream input, final PacketType type) {
ByteSource header = new ByteSource() {
@Override
public InputStream openStream() throws IOException {
byte[] data = new byte[] { (byte) type.getLegacyId() };
return new ByteArrayInputStream(data);
}
};
ByteSource data = new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return input;
}
};
// Combine them into a single stream
try {
return new DataInputStream(ByteSource.concat(header, data).openStream());
} catch (IOException e) {
throw new RuntimeException("Cannot add header.", e);
}
}
}
@@ -1,334 +0,0 @@
package com.comphenix.protocol.injector.packet;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import net.sf.cglib.proxy.Factory;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.TroveWrapper;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
@SuppressWarnings("rawtypes")
class LegacyPacketRegistry {
private static final int MIN_SERVER_PACKETS = 5;
private static final int MIN_CLIENT_PACKETS = 5;
// Fuzzy reflection
private FuzzyReflection packetRegistry;
// The packet class to packet ID translator
private Map<Class, Integer> packetToID;
// Packet IDs to classes, grouped by whether or not they're vanilla or custom defined
private Multimap<Integer, Class> customIdToPacket;
private Map<Integer, Class> vanillaIdToPacket;
// Whether or not certain packets are sent by the client or the server
private ImmutableSet<Integer> serverPackets;
private ImmutableSet<Integer> clientPackets;
// The underlying sets
private Set<Integer> serverPacketsRef;
private Set<Integer> clientPacketsRef;
// New proxy values
private Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
// Vanilla packets
private Map<Integer, Class> previousValues = new HashMap<Integer, Class>();
/**
* Initialize the registry.
*/
@SuppressWarnings({ "unchecked" })
public void initialize() {
if (packetToID == null) {
try {
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
} catch (IllegalArgumentException e) {
// Spigot 1.2.5 MCPC workaround
try {
packetToID = getSpigotWrapper();
} catch (Exception e2) {
// Very bad indeed
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
}
// Create the inverse maps
customIdToPacket = InverseMaps.inverseMultimap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
@Override
public boolean apply(@Nullable Entry<Class, Integer> entry) {
return !MinecraftReflection.isMinecraftClass(entry.getKey());
}
});
// And the vanilla pack - here we assume a unique ID to class mapping
vanillaIdToPacket = InverseMaps.inverseMap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
@Override
public boolean apply(@Nullable Entry<Class, Integer> entry) {
return MinecraftReflection.isMinecraftClass(entry.getKey());
}
});
}
initializeSets();
}
@SuppressWarnings("unchecked")
private void initializeSets() throws FieldAccessException {
if (serverPacketsRef == null || clientPacketsRef == null) {
List<Field> sets = getPacketRegistry().getFieldListByType(Set.class);
try {
if (sets.size() > 1) {
serverPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(0), true);
clientPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(1), true);
// Impossible
if (serverPacketsRef == null || clientPacketsRef == null)
throw new FieldAccessException("Packet sets are in an illegal state.");
// NEVER allow callers to modify the underlying sets
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
// Check sizes
if (serverPackets.size() < MIN_SERVER_PACKETS)
throw new InsufficientPacketsException("Insufficient server packets.", false, serverPackets.size());
if (clientPackets.size() < MIN_CLIENT_PACKETS)
throw new InsufficientPacketsException("Insufficient client packets.", true, clientPackets.size());
} else {
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
}
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access field.", e);
}
} else {
// Copy over again if it has changed
if (serverPacketsRef != null && serverPacketsRef.size() != serverPackets.size())
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
if (clientPacketsRef != null && clientPacketsRef.size() != clientPackets.size())
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
}
}
/**
* Retrieve the packet mapping.
* @return The packet map.
*/
public Map<Class, Integer> getPacketToID() {
// Initialize it, if we haven't already
if (packetToID == null) {
initialize();
}
return packetToID;
}
private Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
// If it talks like a duck, etc.
// Perhaps it would be nice to have a proper duck typing library as well
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
method(FuzzyMethodContract.newBuilder().
nameExact("size").returnTypeExact(int.class)).
method(FuzzyMethodContract.newBuilder().
nameExact("put").parameterCount(2)).
method(FuzzyMethodContract.newBuilder().
nameExact("get").parameterCount(1)).
build();
Field packetsField = getPacketRegistry().getField(
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
Object troveMap = FieldUtils.readStaticField(packetsField, true);
// Fix incorrect no entry values
TroveWrapper.transformNoEntryValue(troveMap, new Function<Integer, Integer>() {
public Integer apply(Integer value) {
if (value >= 0 && value < 256) {
// Someone forgot to set the no entry value. Let's help them.
return -1;
}
return value;
}
});
// We'll assume this a Trove map
return TroveWrapper.getDecoratedMap(troveMap);
}
/**
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
* @return Reflected packet registry.
*/
private FuzzyReflection getPacketRegistry() {
if (packetRegistry == null)
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
return packetRegistry;
}
/**
* Retrieve the injected proxy classes handlig each packet ID.
* @return Injected classes.
*/
public Map<Integer, Class> getOverwrittenPackets() {
return overwrittenPackets;
}
/**
* Retrieve the vanilla classes handling each packet ID.
* @return Vanilla classes.
*/
public Map<Integer, Class> getPreviousPackets() {
return previousValues;
}
/**
* Retrieve every known and supported server packet.
* @return An immutable set of every known server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
public Set<Integer> getServerPackets() throws FieldAccessException {
initializeSets();
// Sanity check. This is impossible!
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
return serverPackets;
}
/**
* Retrieve every known and supported client packet.
* @return An immutable set of every known client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public Set<Integer> getClientPackets() throws FieldAccessException {
initializeSets();
// As above
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
return clientPackets;
}
/**
* Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID.
* @return The associated class.
*/
public Class getPacketClassFromID(int packetID) {
return getPacketClassFromID(packetID, false);
}
/**
* Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
*/
public Class getPacketClassFromID(int packetID, boolean forceVanilla) {
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
Class<?> result = null;
// Optimized lookup
if (lookup.containsKey(packetID)) {
return removeEnhancer(lookup.get(packetID), forceVanilla);
}
// Refresh lookup tables
getPacketToID();
// See if we can look for non-vanilla classes
if (!forceVanilla) {
result = Iterables.getFirst(customIdToPacket.get(packetID), null);
}
if (result == null) {
result = vanillaIdToPacket.get(packetID);
}
// See if we got it
if (result != null)
return result;
else
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
}
/**
* Retrieve the packet ID of a given packet.
* @param packet - the type of packet to check.
* @return The ID of the given packet.
* @throws IllegalArgumentException If this is not a valid packet.
*/
public int getPacketID(Class<?> packet) {
if (packet == null)
throw new IllegalArgumentException("Packet type class cannot be NULL.");
if (!MinecraftReflection.getPacketClass().isAssignableFrom(packet))
throw new IllegalArgumentException("Type must be a packet.");
// The registry contains both the overridden and original packets
return getPacketToID().get(packet);
}
/**
* Find the first superclass that is not a CBLib proxy object.
* @param clazz - the class whose hierachy we're going to search through.
* @param remove - whether or not to skip enhanced (proxy) classes.
* @return If remove is TRUE, the first superclass that is not a proxy.
*/
private static Class removeEnhancer(Class clazz, boolean remove) {
if (remove) {
// Get the underlying vanilla class
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
clazz = clazz.getSuperclass();
}
}
return clazz;
}
/**
* Occurs when we were unable to retrieve all the packets in the registry.
* @author Kristian
*/
public static class InsufficientPacketsException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final boolean client;
private final int packetCount;
private InsufficientPacketsException(String message, boolean client, int packetCount) {
super(message);
this.client = client;
this.packetCount = packetCount;
}
public boolean isClient() {
return client;
}
public int getPacketCount() {
return packetCount;
}
}
}
@@ -1,96 +0,0 @@
package com.comphenix.protocol.injector.packet;
import javax.annotation.Nonnull;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.google.common.base.Preconditions;
/**
* A builder responsible for creating incoming packet injectors.
*
* @author Kristian
*/
public class PacketInjectorBuilder {
protected PacketInjectorBuilder() {
// No need to construct this
}
/**
* Retrieve a new packet injector builder.
* @return Injector builder.
*/
public static PacketInjectorBuilder newBuilder() {
return new PacketInjectorBuilder();
}
protected ListenerInvoker invoker;
protected ErrorReporter reporter;
protected PlayerInjectionHandler playerInjection;
/**
* The error reporter used by the created injector.
* @param reporter - new error reporter.
* @return This builder, for chaining.
*/
public PacketInjectorBuilder reporter(@Nonnull ErrorReporter reporter) {
Preconditions.checkNotNull(reporter, "reporter cannot be NULL");
this.reporter = reporter;
return this;
}
/**
* The packet stream invoker.
* @param invoker - the invoker.
* @return This builder, for chaining.
*/
public PacketInjectorBuilder invoker(@Nonnull ListenerInvoker invoker) {
Preconditions.checkNotNull(invoker, "invoker cannot be NULL");
this.invoker = invoker;
return this;
}
/**
* Set the player injection.
* @param playerInjection - the injection.
* @return This builder, for chaining.
*/
@Nonnull
public PacketInjectorBuilder playerInjection(@Nonnull PlayerInjectionHandler playerInjection) {
Preconditions.checkNotNull(playerInjection, "playerInjection cannot be NULL");
this.playerInjection = playerInjection;
return this;
}
/**
* Called before an object is created with this builder.
*/
private void initializeDefaults() {
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
// Initialize with default values if we can
if (reporter == null)
reporter = ProtocolLibrary.getErrorReporter();
if (invoker == null)
invoker = (PacketFilterManager) manager;
if (playerInjection == null)
throw new IllegalStateException("Player injection parameter must be initialized.");
}
/**
* Create a packet injector using the provided fields or the default values.
* <p>
* Note that any non-null builder parameters must be set.
* @return The created injector.
* @throws FieldAccessException If anything goes wrong in terms of reflection.
*/
public PacketInjector buildInjector() throws FieldAccessException {
initializeDefaults();
return new ProxyPacketInjector(invoker, playerInjection, reporter);
}
}
@@ -1,372 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.packet;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.NoOp;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodInfo;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
/**
* This class is responsible for adding or removing proxy objects that intercepts received packets.
*
* @author Kristian
*/
class ProxyPacketInjector implements PacketInjector {
public static final ReportType REPORT_CANNOT_FIND_READ_PACKET_METHOD = new ReportType("Cannot find read packet method for ID %s.");
public static final ReportType REPORT_UNKNOWN_ORIGIN_FOR_PACKET = new ReportType("Timeout: Unknown origin %s for packet %s. Are you using GamePhase.LOGIN?");
/**
* Represents a way to update the packet ID to class lookup table.
* @author Kristian
*/
private static interface PacketClassLookup {
public void setLookup(int packetID, Class<?> clazz);
}
private static class IntHashMapLookup implements PacketClassLookup {
private WrappedIntHashMap intHashMap;
public IntHashMapLookup() throws IllegalAccessException {
initialize();
}
@Override
public void setLookup(int packetID, Class<?> clazz) {
intHashMap.put(packetID, clazz);
}
private void initialize() throws IllegalAccessException {
if (intHashMap == null) {
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
getFieldByType("packetIdMap", MinecraftReflection.getIntHashMapClass());
try {
intHashMap = WrappedIntHashMap.fromHandle(
FieldUtils.readField(intHashMapField, (Object) null, true));
} catch (IllegalArgumentException e) {
throw new RuntimeException("Minecraft is incompatible.", e);
}
}
}
}
private static class ArrayLookup implements PacketClassLookup {
private Class<?>[] array;
public ArrayLookup() throws IllegalAccessException {
initialize();
}
@Override
public void setLookup(int packetID, Class<?> clazz) {
array[packetID] = clazz;
}
private void initialize() throws IllegalAccessException {
FuzzyReflection reflection = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass());
// Is there a Class array with 256 elements instead?
for (Field field : reflection.getFieldListByType(Class[].class)) {
Class<?>[] test = (Class<?>[]) FieldUtils.readField(field, (Object)null);
if (test.length == 256) {
array = test;
return;
}
}
throw new IllegalArgumentException(
"Unable to find an array with the type " + Class[].class +
" in " + MinecraftReflection.getPacketClass());
}
}
/**
* Matches the readPacketData(DataInputStream) method in Packet.
*/
private static FuzzyMethodContract READ_PACKET = FuzzyMethodContract.newBuilder().
returnTypeVoid().
parameterDerivedOf(DataInput.class).
parameterCount(1).
build();
private static PacketClassLookup lookup;
// The packet filter manager
private ListenerInvoker manager;
// Error reporter
private ErrorReporter reporter;
// Allows us to determine the sender
private PlayerInjectionHandler playerInjection;
// Share callback filter
private CallbackFilter filter;
// Determine if the read packet method was found
private boolean readPacketIntercepted = false;
public ProxyPacketInjector(ListenerInvoker manager, PlayerInjectionHandler playerInjection,
ErrorReporter reporter) throws FieldAccessException {
this.manager = manager;
this.playerInjection = playerInjection;
this.reporter = reporter;
initialize();
}
@Override
public boolean isCancelled(Object packet) {
return ReadPacketModifier.isCancelled(packet);
}
@Override
public void setCancelled(Object packet, boolean cancelled) {
if (cancelled) {
ReadPacketModifier.setOverride(packet, null);
} else {
ReadPacketModifier.removeOverride(packet);
}
}
private void initialize() throws FieldAccessException {
if (lookup == null) {
try {
lookup = new IntHashMapLookup();
} catch (Exception e1) {
try {
lookup = new ArrayLookup();
} catch (Exception e2) {
// Wow
throw new FieldAccessException(e1.getMessage() + ". Workaround failed too.", e2);
}
}
// Should work fine now
}
}
@Override
public void inputBuffersChanged(Set<PacketType> set) {
// No need to do anything
}
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
final int packetID = type.getLegacyId();
if (hasPacketHandler(type))
return false;
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
// Unfortunately, we can't easily distinguish between these two functions:
// * Object lookup(int par1)
// * Object removeObject(int par1)
// So, we'll use the classMapToInt registry instead.
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
Map<Class, Integer> registry = PacketRegistry.getPacketToID();
Class old = PacketRegistry.getPacketClassFromType(type);
// If this packet is not known
if (old == null) {
throw new IllegalStateException("Packet ID " + type + " is not a valid packet type in this version.");
}
// Check for previous injections
if (Factory.class.isAssignableFrom(old)) {
throw new IllegalStateException("Packet " + type + " has already been injected.");
}
if (filter == null) {
readPacketIntercepted = false;
filter = new CallbackFilter() {
@Override
public int accept(Method method) {
// Skip methods defined in Object
if (method.getDeclaringClass().equals(Object.class)) {
return 0;
} else if (READ_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
readPacketIntercepted = true;
return 1;
} else {
return 2;
}
}
};
}
// Subclass the specific packet class
ex.setSuperclass(old);
ex.setCallbackFilter(filter);
ex.setCallbackTypes(new Class<?>[] { NoOp.class, ReadPacketModifier.class, ReadPacketModifier.class });
Class proxy = ex.createClass();
// Create the proxy handlers
ReadPacketModifier modifierReadPacket = new ReadPacketModifier(packetID, this, reporter, true);
ReadPacketModifier modifierRest = new ReadPacketModifier(packetID, this, reporter, false);
// Add a static reference
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
// Check that we found the read method
if (!readPacketIntercepted) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_FIND_READ_PACKET_METHOD).messageParam(packetID));
}
// Override values
previous.put(packetID, old);
registry.put(proxy, packetID);
overwritten.put(packetID, proxy);
lookup.setLookup(packetID, proxy);
return true;
}
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public boolean removePacketHandler(PacketType type) {
final int packetID = type.getLegacyId();
if (!hasPacketHandler(type))
return false;
Map<Class, Integer> registry = PacketRegistry.getPacketToID();
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
Class old = previous.get(packetID);
Class proxy = PacketRegistry.getPacketClassFromType(type);
lookup.setLookup(packetID, old);
previous.remove(packetID);
registry.remove(proxy);
overwritten.remove(packetID);
return true;
}
/**
* Determine if the data a packet read must be buffered.
* @param packetId - the packet to check.
* @return TRUE if it does, FALSE otherwise.
*/
@Deprecated
public boolean requireInputBuffers(int packetId) {
return manager.requireInputBuffer(packetId);
}
@SuppressWarnings("deprecation")
@Override
public boolean hasPacketHandler(PacketType type) {
return PacketRegistry.getPreviousPackets().containsKey(type.getLegacyId());
}
@SuppressWarnings("deprecation")
@Override
public Set<PacketType> getPacketHandlers() {
return PacketRegistry.toPacketTypes(PacketRegistry.getPreviousPackets().keySet(), Sender.CLIENT);
}
// Called from the ReadPacketModified monitor
public PacketEvent packetRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
if (playerInjection.canRecievePackets()) {
return playerInjection.handlePacketRecieved(packet, input, buffered);
}
try {
Player client = playerInjection.getPlayerByConnection((DataInputStream) input);
// Never invoke a event if we don't know where it's from
if (client != null) {
return packetRecieved(packet, client, buffered);
} else {
// The timeout elapsed!
reporter.reportDetailed(this, Report.newBuilder(REPORT_UNKNOWN_ORIGIN_FOR_PACKET).messageParam(input, packet.getType()));
return null;
}
} catch (InterruptedException e) {
// We will ignore this - it occurs when a player disconnects
//reporter.reportDetailed(this, "Thread was interrupted.", e, packet, input);
return null;
}
}
@Override
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
PacketEvent event = PacketEvent.fromClient(manager, packet, marker, client);
manager.invokePacketRecieving(event);
return event;
}
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public synchronized void cleanupAll() {
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
// Remove every packet handler
for (Integer id : previous.keySet().toArray(new Integer[0])) {
removePacketHandler(PacketType.findLegacy(id, Sender.CLIENT));
removePacketHandler(PacketType.findLegacy(id, Sender.SERVER));
}
overwritten.clear();
previous.clear();
}
}
@@ -1,186 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.packet;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.NetworkProcessor;
import com.google.common.collect.MapMaker;
class ReadPacketModifier implements MethodInterceptor {
public static final ReportType REPORT_CANNOT_HANDLE_CLIENT_PACKET = new ReportType("Cannot handle client packet.");
// A cancel marker
private static final Object CANCEL_MARKER = new Object();
// Common for all packets of the same type
private ProxyPacketInjector packetInjector;
private int packetID;
// Report errors
private ErrorReporter reporter;
private NetworkProcessor processor;
// If this is a read packet data method
private boolean isReadPacketDataMethod;
// Whether or not a packet has been cancelled
private static Map<Object, Object> override = new MapMaker().weakKeys().makeMap();
public ReadPacketModifier(int packetID, ProxyPacketInjector packetInjector, ErrorReporter reporter, boolean isReadPacketDataMethod) {
this.packetID = packetID;
this.packetInjector = packetInjector;
this.reporter = reporter;
this.processor = new NetworkProcessor(reporter);
this.isReadPacketDataMethod = isReadPacketDataMethod;
}
/**
* Remove any packet overrides.
* @param packet - the packet to rever
*/
public static void removeOverride(Object packet) {
override.remove(packet);
}
/**
* Retrieve the packet that overrides the methods of the given packet.
* @param packet - the given packet.
* @return Overriden object.
*/
public static Object getOverride(Object packet) {
return override.get(packet);
}
/**
* Set the packet instance to delegate to instead, or mark the packet as cancelled.
* <p>
* To undo a override, use {@link #removeOverride(Object)}.
* @param packet - the packet.
* @param override - the override method. NULL to cancel this packet.
*/
public static void setOverride(Object packet, Object overridePacket) {
override.put(packet, overridePacket != null ? overridePacket : CANCEL_MARKER);
}
/**
* Determine if the given packet has been cancelled before.
* @param packet - the packet to check.
* @return TRUE if it has been cancelled, FALSE otherwise.
*/
public static boolean isCancelled(Object packet) {
return getOverride(packet) == CANCEL_MARKER;
}
@SuppressWarnings("deprecation")
@Override
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// Atomic retrieval
Object overridenObject = override.get(thisObj);
Object returnValue = null;
// We need this in order to get the correct player
InputStream input = isReadPacketDataMethod ? (InputStream) args[0] : null;
ByteArrayOutputStream bufferStream = null;
// See if we need to buffer the read data
if (isReadPacketDataMethod && packetInjector.requireInputBuffers(packetID)) {
CaptureInputStream captured = new CaptureInputStream(
input, bufferStream = new ByteArrayOutputStream());
// Swap it with our custom stream
args[0] = new DataInputStream(captured);
}
if (overridenObject != null) {
// This packet has been cancelled
if (overridenObject == CANCEL_MARKER) {
// So, cancel all void methods
if (method.getReturnType().equals(Void.TYPE))
return null;
else // Revert to normal for everything else
overridenObject = thisObj;
}
returnValue = proxy.invokeSuper(overridenObject, args);
} else {
returnValue = proxy.invokeSuper(thisObj, args);
}
// Is this a readPacketData method?
if (isReadPacketDataMethod) {
// Swap back custom stream
args[0] = input;
try {
byte[] buffer = bufferStream != null ? bufferStream.toByteArray() : null;
// Let the people know
PacketType type = PacketType.findLegacy(packetID, Sender.CLIENT);
PacketContainer container = new PacketContainer(type, thisObj);
PacketEvent event = packetInjector.packetRecieved(container, input, buffer);
// Handle override
if (event != null) {
Object result = event.getPacket().getHandle();
if (event.isCancelled()) {
override.put(thisObj, CANCEL_MARKER);
return returnValue;
} else if (!objectEquals(thisObj, result)) {
override.put(thisObj, result);
}
// This is fine - received packets are enqueued in any case
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
processor.invokePostEvent(event, marker);
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft cannot handle this error
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_HANDLE_CLIENT_PACKET).callerParam(args[0]).error(e)
);
}
}
return returnValue;
}
private boolean objectEquals(Object a, Object b) {
return System.identityHashCode(a) != System.identityHashCode(b);
}
}
@@ -1,130 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.packet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.lang.reflect.Method;
import java.util.Map;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.NetworkProcessor;
import com.google.common.collect.MapMaker;
public class WritePacketModifier implements MethodInterceptor {
public static final ReportType REPORT_CANNOT_WRITE_SERVER_PACKET = new ReportType("Cannot write server packet.");
private static class ProxyInformation {
// Marker that contains custom writers
public final Object proxyObject;
public final PacketEvent event;
public final NetworkMarker marker;
public ProxyInformation(Object proxyObject, PacketEvent event, NetworkMarker marker) {
this.proxyObject = proxyObject;
this.event = event;
this.marker = marker;
}
}
private Map<Object, ProxyInformation> proxyLookup = new MapMaker().weakKeys().makeMap();
// Report errors
private final ErrorReporter reporter;
private final NetworkProcessor processor;
// Whether or not this represents the write method
private boolean isWriteMethod;
public WritePacketModifier(ErrorReporter reporter, boolean isWriteMethod) {
this.reporter = reporter;
this.processor = new NetworkProcessor(reporter);
this.isWriteMethod = isWriteMethod;
}
/**
* Associate the given generated instance of a class and the given parameteters.
* @param generatedClass - the generated class.
* @param proxyObject - the object to call from the generated class.
* @param event - the packet event.
* @param marker - the network marker.
*/
public void register(Object generatedClass, Object proxyObject, PacketEvent event, NetworkMarker marker) {
proxyLookup.put(generatedClass, new ProxyInformation(proxyObject, event, marker));
}
@Override
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
ProxyInformation information = proxyLookup.get(thisObj);
if (information == null) {
// This is really bad - someone forgot to register the proxy
throw new RuntimeException("Cannot find proxy information for " + thisObj);
}
if (isWriteMethod) {
// If every output handler has been removed - ignore everything
if (!information.marker.getOutputHandlers().isEmpty()) {
try {
DataOutput output = (DataOutput) args[0];
// First - we need the initial buffer
ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream();
proxy.invoke(information.proxyObject, new Object[] { new DataOutputStream(outputBufferStream) });
// Let each handler prepare the actual output
byte[] outputBuffer = processor.processOutput(information.event, information.marker,
outputBufferStream.toByteArray());
// Write that output to the network stream
output.write(outputBuffer);
// We're done
processor.invokePostEvent(information.event, information.marker);
return null;
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft cannot handle this error
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_WRITE_SERVER_PACKET).callerParam(args[0]).error(e)
);
}
}
// Invoke this write method first
proxy.invoke(information.proxyObject, args);
processor.invokePostEvent(information.event, information.marker);
return null;
}
// Default to the super method
return proxy.invoke(information.proxyObject, args);
}
}
@@ -1,193 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.MapMaker;
/**
* The array list that notifies when packets are sent by the server.
*
* @author Kristian
*/
class InjectedArrayList extends ArrayList<Object> {
public static final ReportType REPORT_CANNOT_REVERT_CANCELLED_PACKET = new ReportType("Reverting cancelled packet failed.");
/**
* Silly Eclipse.
*/
private static final long serialVersionUID = -1173865905404280990L;
// Fake inverted proxy objects
private static ConcurrentMap<Object, Object> delegateLookup = new MapMaker().weakKeys().makeMap();
private transient PlayerInjector injector;
private transient Set<Object> ignoredPackets;
private transient InvertedIntegerCallback callback;
public InjectedArrayList(PlayerInjector injector, Set<Object> ignoredPackets) {
this.injector = injector;
this.ignoredPackets = ignoredPackets;
this.callback = new InvertedIntegerCallback();
}
@Override
public boolean add(Object packet) {
Object result = null;
// Check for fake packets and ignored packets
if (packet instanceof FakePacket) {
return true;
} else if (ignoredPackets.contains(packet)) {
// Don't send it to the filters
result = ignoredPackets.remove(packet);
} else {
result = injector.handlePacketSending(packet);
}
// A NULL packet indicate cancelling
try {
if (result != null) {
super.add(result);
} else {
// We'll use the FakePacket marker instead of preventing the filters
injector.sendServerPacket(createNegativePacket(packet), null, true);
}
// Collection.add contract
return true;
} catch (InvocationTargetException e) {
// Prefer to report this to the user, instead of risking sending it to Minecraft
ProtocolLibrary.getErrorReporter().reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_REVERT_CANCELLED_PACKET).error(e).callerParam(packet)
);
// Failure
return false;
}
}
/**
* Used by a hack that reverses the effect of a cancelled packet. Returns a packet
* whereby every int method's return value is inverted (a => -a).
*
* @param source - packet to invert.
* @return The inverted packet.
*/
@SuppressWarnings("deprecation")
Object createNegativePacket(Object source) {
ListenerInvoker invoker = injector.getInvoker();
PacketType type = invoker.getPacketType(source);
// We want to subtract the byte amount that were added to the running
// total of outstanding packets. Otherwise, cancelling too many packets
// might cause a "disconnect.overflow" error.
//
// We do that by constructing a special packet of the same type that returns
// a negative integer for all zero-parameter integer methods. This includes the
// size() method, which is used by the queue method to count the number of
// bytes to add.
//
// Essentially, we have:
//
// public class NegativePacket extends [a packet] {
// @Override
// public int size() {
// return -super.size();
// }
// ect.
// }
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
ex.setSuperclass(MinecraftReflection.getPacketClass());
ex.setInterfaces(new Class[] { FakePacket.class } );
ex.setUseCache(true);
ex.setCallbackType(InvertedIntegerCallback.class);
Class<?> proxyClass = ex.createClass();
Enhancer.registerCallbacks(proxyClass, new Callback[] { callback });
try {
// Temporarily associate the fake packet class
invoker.registerPacketClass(proxyClass, type.getLegacyId());
Object proxy = proxyClass.newInstance();
InjectedArrayList.registerDelegate(proxy, source);
return proxy;
} catch (Exception e) {
// Don't pollute the throws tree
throw new RuntimeException("Cannot create fake class.", e);
} finally {
// Remove this association
invoker.unregisterPacketClass(proxyClass);
}
}
/**
* Ensure that the inverted integer proxy uses the given object as source.
* @param proxy - inverted integer proxy.
* @param source - source object.
*/
private static void registerDelegate(Object proxy, Object source) {
delegateLookup.put(proxy, source);
}
/**
* Inverts the integer result of every integer method.
* @author Kristian
*/
private class InvertedIntegerCallback implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
final Object delegate = delegateLookup.get(obj);
if (delegate == null) {
throw new IllegalStateException("Unable to find delegate source for " + obj);
}
if (method.getReturnType().equals(int.class) && args.length == 0) {
Integer result = (Integer) proxy.invoke(delegate, args);
return -result;
} else {
return proxy.invoke(delegate, args);
}
}
}
}
@@ -1,442 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Server;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Used to ensure that the 1.3 server is referencing the correct server handler.
*
* @author Kristian
*/
public class InjectedServerConnection {
// A number of things can go wrong ...
public static final ReportType REPORT_CANNOT_FIND_MINECRAFT_SERVER = new ReportType("Cannot extract minecraft server from Bukkit.");
public static final ReportType REPORT_CANNOT_INJECT_SERVER_CONNECTION = new ReportType("Cannot inject into server connection. Bad things will happen.");
public static final ReportType REPORT_CANNOT_FIND_LISTENER_THREAD = new ReportType("Cannot find listener thread in MinecraftServer.");
public static final ReportType REPORT_CANNOT_READ_LISTENER_THREAD = new ReportType("Unable to read the listener thread.");
public static final ReportType REPORT_CANNOT_FIND_SERVER_CONNECTION = new ReportType("Unable to retrieve server connection");
public static final ReportType REPORT_UNEXPECTED_THREAD_COUNT = new ReportType("Unexpected number of threads in %s: %s");
public static final ReportType REPORT_CANNOT_FIND_NET_HANDLER_THREAD = new ReportType("Unable to retrieve net handler thread.");
public static final ReportType REPORT_INSUFFICENT_THREAD_COUNT = new ReportType("Unable to inject %s lists in %s.");
public static final ReportType REPORT_CANNOT_COPY_OLD_TO_NEW = new ReportType("Cannot copy old %s to new.");
private static Field listenerThreadField;
private static Field minecraftServerField;
private static Field listField;
private static Field dedicatedThreadField;
private static Method serverConnectionMethod;
private List<VolatileField> listFields;
private List<ReplacedArrayList<Object>> replacedLists;
// The current detected server socket
public enum ServerSocketType {
SERVER_CONNECTION,
LISTENER_THREAD,
}
// Used to inject net handlers
private NetLoginInjector netLoginInjector;
// Inject server connections
private AbstractInputStreamLookup socketInjector;
// Detected by the initializer
private ServerSocketType socketType;
private Server server;
private ErrorReporter reporter;
private boolean hasAttempted;
private boolean hasSuccess;
private Object minecraftServer = null;
public InjectedServerConnection(ErrorReporter reporter, AbstractInputStreamLookup socketInjector, Server server, NetLoginInjector netLoginInjector) {
this.listFields = new ArrayList<VolatileField>();
this.replacedLists = new ArrayList<ReplacedArrayList<Object>>();
this.reporter = reporter;
this.server = server;
this.socketInjector = socketInjector;
this.netLoginInjector = netLoginInjector;
}
/**
* Retrieve the current server connection.
* @param reporter - error reproter.
* @param server - the current server.
* @return The current server connection, or NULL if it hasn't been initialized yet.
* @throws FieldAccessException Reflection error.
*/
public static Object getServerConnection(ErrorReporter reporter, Server server) {
try {
// Now we are probably able to check for Netty
InjectedServerConnection inspector = new InjectedServerConnection(reporter, null, server, null);
return inspector.getServerConnection();
} catch (IllegalAccessException e) {
throw new FieldAccessException("Reflection error.", e);
} catch (IllegalArgumentException e) {
throw new FieldAccessException("Corrupt data.", e);
} catch (InvocationTargetException e) {
throw new FieldAccessException("Minecraft error.", e);
}
}
/**
* Initial reflective detective work. Will be automatically called by most methods in this class.
*/
public void initialize() {
// Only execute this method once
if (!hasAttempted)
hasAttempted = true;
else
return;
if (minecraftServerField == null)
minecraftServerField = FuzzyReflection.fromObject(server, true).
getFieldByType("MinecraftServer", MinecraftReflection.getMinecraftServerClass());
try {
minecraftServer = FieldUtils.readField(minecraftServerField, server, true);
} catch (IllegalAccessException e1) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_FIND_MINECRAFT_SERVER));
return;
}
try {
if (serverConnectionMethod == null)
serverConnectionMethod = FuzzyReflection.fromClass(minecraftServerField.getType()).
getMethodByParameters("getServerConnection",
MinecraftReflection.getServerConnectionClass(), new Class[] {});
// We're using Minecraft 1.3.1
socketType = ServerSocketType.SERVER_CONNECTION;
} catch (IllegalArgumentException e) {
// Minecraft 1.2.5 or lower
socketType = ServerSocketType.LISTENER_THREAD;
} catch (Exception e) {
// Oh damn - inform the player
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_INJECT_SERVER_CONNECTION).error(e));
}
}
/**
* Retrieve the known server socket type.
* <p>
* This depends on the version of CraftBukkit we are using.
* @return The server socket type.
*/
public ServerSocketType getServerSocketType() {
return socketType;
}
/**
* Inject the connection interceptor into the correct server socket implementation.
*/
public void injectList() {
initialize();
if (socketType == ServerSocketType.SERVER_CONNECTION) {
injectServerConnection();
} else if (socketType == ServerSocketType.LISTENER_THREAD) {
injectListenerThread();
} else {
// Damn it
throw new IllegalStateException("Unable to detected server connection.");
}
}
/**
* Retrieve the listener thread field.
*/
private void initializeListenerField() {
if (listenerThreadField == null)
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
getFieldByType("networkListenThread", MinecraftReflection.getNetworkListenThreadClass());
}
/**
* Retrieve the listener thread object, or NULL the server isn't using this socket implementation.
* @return The listener thread, or NULL.
* @throws IllegalAccessException Cannot access field.
* @throws RuntimeException Unexpected class structure - the field doesn't exist.
*/
public Object getListenerThread() throws RuntimeException, IllegalAccessException {
initialize();
if (socketType == ServerSocketType.LISTENER_THREAD) {
initializeListenerField();
return listenerThreadField.get(minecraftServer);
} else {
return null;
}
}
/**
* Retrieve the server connection object, or NULL if the server isn't using it as the socket implementation.
* @return The socket connection, or NULL.
* @throws IllegalAccessException If the reflective operation failed.
* @throws IllegalArgumentException If the reflective operation failed.
* @throws InvocationTargetException If the reflective operation failed.
*/
public Object getServerConnection() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
initialize();
if (socketType == ServerSocketType.SERVER_CONNECTION)
return serverConnectionMethod.invoke(minecraftServer);
else
return null;
}
private void injectListenerThread() {
try {
initializeListenerField();
} catch (RuntimeException e) {
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_LISTENER_THREAD).callerParam(minecraftServer).error(e)
);
return;
}
Object listenerThread = null;
// Attempt to get the thread
try {
listenerThread = getListenerThread();
} catch (Exception e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_LISTENER_THREAD).error(e));
return;
}
// Inject the server socket too
injectServerSocket(listenerThread);
// Just inject every list field we can get
injectEveryListField(listenerThread, 1);
hasSuccess = true;
}
private void injectServerConnection() {
Object serverConnection = null;
// Careful - we might fail
try {
serverConnection = getServerConnection();
} catch (Exception e) {
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_SERVER_CONNECTION).callerParam(minecraftServer).error(e)
);
return;
}
if (listField == null)
listField = FuzzyReflection.fromClass(serverConnectionMethod.getReturnType(), true).
getFieldByType("netServerHandlerList", List.class);
if (dedicatedThreadField == null) {
List<Field> matches = FuzzyReflection.fromObject(serverConnection, true).
getFieldListByType(Thread.class);
// Verify the field count
if (matches.size() != 1)
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNEXPECTED_THREAD_COUNT).messageParam(serverConnection.getClass(), matches.size())
);
else
dedicatedThreadField = matches.get(0);
}
// Next, try to get the dedicated thread
try {
if (dedicatedThreadField != null) {
Object dedicatedThread = FieldUtils.readField(dedicatedThreadField, serverConnection, true);
// Inject server socket and NetServerHandlers.
injectServerSocket(dedicatedThread);
injectEveryListField(dedicatedThread, 1);
}
} catch (IllegalAccessException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_FIND_NET_HANDLER_THREAD).error(e));
}
injectIntoList(serverConnection, listField);
hasSuccess = true;
}
private void injectServerSocket(Object container) {
socketInjector.inject(container);
}
/**
* Automatically inject into every List-compatible public or private field of the given object.
* @param container - container object with the fields to inject.
* @param minimum - the minimum number of fields we expect exists.
*/
private void injectEveryListField(Object container, int minimum) {
// Ok, great. Get every list field
List<Field> lists = FuzzyReflection.fromObject(container, true).getFieldListByType(List.class);
for (Field list : lists) {
injectIntoList(container, list);
}
// Warn about unexpected errors
if (lists.size() < minimum) {
reporter.reportWarning(this, Report.newBuilder(REPORT_INSUFFICENT_THREAD_COUNT).messageParam(minimum, container.getClass()));
}
}
@SuppressWarnings("unchecked")
private void injectIntoList(Object instance, Field field) {
VolatileField listFieldRef = new VolatileField(field, instance, true);
List<Object> list = (List<Object>) listFieldRef.getValue();
// Careful not to inject twice
if (list instanceof ReplacedArrayList) {
replacedLists.add((ReplacedArrayList<Object>) list);
} else {
ReplacedArrayList<Object> injectedList = createReplacement(list);
replacedLists.add(injectedList);
listFieldRef.setValue(injectedList);
listFields.add(listFieldRef);
}
}
// Hack to avoid the "moved to quickly" error
private ReplacedArrayList<Object> createReplacement(List<Object> list) {
return new ReplacedArrayList<Object>(list) {
/**
* Shut up Eclipse!
*/
private static final long serialVersionUID = 2070481080950500367L;
// Object writer we'll use
private final ObjectWriter writer = new ObjectWriter();
@Override
protected void onReplacing(Object inserting, Object replacement) {
// Is this a normal Minecraft object?
if (!(inserting instanceof Factory)) {
// If so, copy the content of the old element to the new
try {
writer.copyTo(inserting, replacement, inserting.getClass());
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportDetailed(InjectedServerConnection.this,
Report.newBuilder(REPORT_CANNOT_COPY_OLD_TO_NEW).messageParam(inserting).callerParam(inserting, replacement).error(e)
);
}
}
}
@Override
protected void onInserting(Object inserting) {
// Ready for some login handler injection?
if (MinecraftReflection.isLoginHandler(inserting)) {
Object replaced = netLoginInjector.onNetLoginCreated(inserting);
// Only replace if it has changed
if (inserting != replaced)
addMapping(inserting, replaced, true);
}
}
@Override
protected void onRemoved(Object removing) {
// Clean up?
if (MinecraftReflection.isLoginHandler(removing)) {
netLoginInjector.cleanup(removing);
}
}
};
}
/**
* Replace the server handler instance kept by the "keep alive" object.
* @param oldHandler - old server handler.
* @param newHandler - new, proxied server handler.
*/
public void replaceServerHandler(Object oldHandler, Object newHandler) {
if (!hasAttempted) {
injectList();
}
if (hasSuccess) {
for (ReplacedArrayList<Object> replacedList : replacedLists) {
replacedList.addMapping(oldHandler, newHandler);
}
}
}
/**
* Revert to the old vanilla server handler, if it has been replaced.
* @param oldHandler - old vanilla server handler.
*/
public void revertServerHandler(Object oldHandler) {
if (hasSuccess) {
for (ReplacedArrayList<Object> replacedList : replacedLists) {
replacedList.removeMapping(oldHandler);
}
}
}
/**
* Undoes everything.
*/
public void cleanupAll() {
if (replacedLists.size() > 0) {
// Repair the underlying lists
for (ReplacedArrayList<Object> replacedList : replacedLists) {
replacedList.revertAll();
}
for (VolatileField field : listFields) {
field.revertValue();
}
listFields.clear();
replacedLists.clear();
}
}
}
@@ -1,162 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
/**
* Injects every NetLoginHandler created by the server.
*
* @author Kristian
*/
class NetLoginInjector {
public static final ReportType REPORT_CANNOT_HOOK_LOGIN_HANDLER = new ReportType("Unable to hook %s.");
public static final ReportType REPORT_CANNOT_CLEANUP_LOGIN_HANDLER = new ReportType("Cannot cleanup %s.");
private ConcurrentMap<Object, PlayerInjector> injectedLogins = Maps.newConcurrentMap();
// Handles every hook
private ProxyPlayerInjectionHandler injectionHandler;
// Create temporary players
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
// The current error reporter
private ErrorReporter reporter;
private Server server;
public NetLoginInjector(ErrorReporter reporter, Server server, ProxyPlayerInjectionHandler injectionHandler) {
this.reporter = reporter;
this.server = server;
this.injectionHandler = injectionHandler;
}
/**
* Invoked when a NetLoginHandler has been created.
* @param inserting - the new NetLoginHandler.
* @return An injected NetLoginHandler, or the original object.
*/
public Object onNetLoginCreated(Object inserting) {
try {
// Make sure we actually need to inject during this phase
if (!injectionHandler.isInjectionNecessary(GamePhase.LOGIN))
return inserting;
Player temporary = playerFactory.createTemporaryPlayer(server);
// Note that we bail out if there's an existing player injector
PlayerInjector injector = injectionHandler.injectPlayer(
temporary, inserting, ConflictStrategy.BAIL_OUT, GamePhase.LOGIN);
if (injector != null) {
// Update injector as well
TemporaryPlayerFactory.setInjectorInPlayer(temporary, injector);
injector.updateOnLogin = true;
// Save the login
injectedLogins.putIfAbsent(inserting, injector);
}
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
return inserting;
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft can't handle this, so we'll deal with it here
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_HOOK_LOGIN_HANDLER).
messageParam(MinecraftReflection.getNetLoginHandlerName()).
callerParam(inserting, injectionHandler).
error(e)
);
return inserting;
}
}
/**
* Invoked when a NetLoginHandler should be reverted.
* @param inserting - the original NetLoginHandler.
* @return An injected NetLoginHandler, or the original object.
*/
public synchronized void cleanup(Object removing) {
PlayerInjector injected = injectedLogins.get(removing);
if (injected != null) {
try {
PlayerInjector newInjector = null;
Player player = injected.getPlayer();
// Clean up list
injectedLogins.remove(removing);
// No need to clean up twice
if (injected.isClean())
return;
// Hack to clean up other references
newInjector = injectionHandler.getInjectorByNetworkHandler(injected.getNetworkManager());
injectionHandler.uninjectPlayer(player);
// Update NetworkManager
if (newInjector != null) {
if (injected instanceof NetworkObjectInjector) {
newInjector.setNetworkManager(injected.getNetworkManager(), true);
}
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Don't leak this to Minecraft
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_CLEANUP_LOGIN_HANDLER).
messageParam(MinecraftReflection.getNetLoginHandlerName()).
callerParam(removing).
error(e)
);
}
}
}
/**
* Remove all injected hooks.
*/
public void cleanupAll() {
for (PlayerInjector injector : injectedLogins.values()) {
injector.cleanupAll();
}
injectedLogins.clear();
}
}
@@ -1,221 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.Sets;
/**
* Injection hook that overrides the packet queue lists in NetworkHandler.
*
* @author Kristian
*/
class NetworkFieldInjector extends PlayerInjector {
/**
* Marker interface that indicates a packet is fake and should not be processed.
* @author Kristian
*/
public interface FakePacket {
// Nothing
}
// After commit 336a4e00668fd2518c41242755ed6b3bdc3b0e6c (Update CraftBukkit to Minecraft 1.4.4.),
// CraftBukkit stopped redirecting map chunk and map chunk bulk packets to a separate queue.
// Thus, NetworkFieldInjector can safely handle every packet (though not perfectly - some packets
// will be slightly processed).
private MinecraftVersion safeVersion = new MinecraftVersion("1.4.4");
// Packets to ignore
private Set<Object> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
// Overridden fields
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
// Sync field
private static Field syncField;
private Object syncObject;
// Determine if we're listening
private IntegerSet sendingFilters;
public NetworkFieldInjector(ErrorReporter reporter, Player player, ListenerInvoker manager, IntegerSet sendingFilters) {
super(reporter, player, manager);
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public synchronized void initialize(Object injectionSource) throws IllegalAccessException {
super.initialize(injectionSource);
// Get the sync field as well
if (hasInitialized) {
if (syncField == null)
syncField = FuzzyReflection.fromObject(networkManager, true).getFieldByType("java\\.lang\\.Object");
syncObject = FieldUtils.readField(syncField, networkManager, true);
}
}
@Override
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
if (networkManager != null) {
try {
if (!filtered) {
ignoredPackets.add(packet);
}
if (marker != null) {
queuedMarkers.put(packet, marker);
}
// Note that invocation target exception is a wrapper for a checked exception
queueMethod.invoke(networkManager, packet);
} catch (IllegalArgumentException e) {
throw e;
} catch (InvocationTargetException e) {
throw e;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access queue method.", e);
}
} else {
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
}
}
@Override
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
if (version != null && version.compareTo(safeVersion) > 0) {
return null;
} else {
@SuppressWarnings("deprecation")
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
return new UnsupportedListener("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.", unsupported);
} else {
return null;
}
}
}
@Override
public void injectManager() {
if (networkManager != null) {
@SuppressWarnings("rawtypes")
StructureModifier<List> list = networkModifier.withType(List.class);
// Subclass both send queues
for (Field field : list.getFields()) {
VolatileField overwriter = new VolatileField(field, networkManager, true);
@SuppressWarnings("unchecked")
List<Object> minecraftList = (List<Object>) overwriter.getOldValue();
synchronized(syncObject) {
// The list we'll be inserting
List<Object> hackedList = new InjectedArrayList(this, ignoredPackets);
// Add every previously stored packet
for (Object packet : minecraftList) {
hackedList.add(packet);
}
// Don' keep stale packets around
minecraftList.clear();
overwriter.setValue(Collections.synchronizedList(hackedList));
}
overridenLists.add(overwriter);
}
}
}
@Override
@SuppressWarnings("unchecked")
protected void cleanHook() {
// Clean up
for (VolatileField overriden : overridenLists) {
List<Object> minecraftList = (List<Object>) overriden.getOldValue();
List<Object> hacketList = (List<Object>) overriden.getValue();
if (minecraftList == hacketList) {
return;
}
// Get a lock before we modify the list
synchronized(syncObject) {
try {
// Copy over current packets
for (Object packet : (List<Object>) overriden.getValue()) {
minecraftList.add(packet);
}
} finally {
overriden.revertValue();
}
}
}
overridenLists.clear();
}
@Override
public void handleDisconnect() {
// No need to do anything
}
@Override
public boolean canInject(GamePhase phase) {
// All phases should work
return true;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_HANDLER_FIELDS;
}
}
@@ -1,230 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.LazyLoader;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftVersion;
/**
* Injection method that overrides the NetworkHandler itself, and its queue-method.
*
* @author Kristian
*/
public class NetworkObjectInjector extends PlayerInjector {
// Determine if we're listening
private IntegerSet sendingFilters;
// After commit 336a4e00668fd2518c41242755ed6b3bdc3b0e6c (Update CraftBukkit to Minecraft 1.4.4.),
// CraftBukkit stopped redirecting map chunk and map chunk bulk packets to a separate queue.
// Thus, NetworkFieldInjector can safely handle every packet (though not perfectly - some packets
// will be slightly processed).
private MinecraftVersion safeVersion = new MinecraftVersion("1.4.4");
// Shared callback filter - avoid creating a new class every time
private volatile static CallbackFilter callbackFilter;
// Temporary player factory
private static volatile TemporaryPlayerFactory tempPlayerFactory;
/**
* Create a new network object injector.
* <p>
* Note: This class is intended to be internal. Do not use.
* @param reporter - the error reporter.
* @param player - the player Bukkit entity.
* @param invoker - the packet invoker.
* @param sendingFilters - list of permitted packet IDs.
* @throws IllegalAccessException If reflection failed.
*/
public NetworkObjectInjector(ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
super(reporter, player, invoker);
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
/**
* Create a temporary player for use during login.
* @param server - Bukkit server.
* @return The temporary player.
*/
public Player createTemporaryPlayer(Server server) {
if (tempPlayerFactory == null)
tempPlayerFactory = new TemporaryPlayerFactory();
// Create and associate the fake player with this network injector
return tempPlayerFactory.createTemporaryPlayer(server, this);
}
@Override
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
if (networkDelegate != null) {
try {
if (marker != null) {
queuedMarkers.put(packet, marker);
}
// Note that invocation target exception is a wrapper for a checked exception
queueMethod.invoke(networkDelegate, packet);
} catch (IllegalArgumentException e) {
throw e;
} catch (InvocationTargetException e) {
throw e;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access queue method.", e);
}
} else {
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
}
}
@Override
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
if (version != null && version.compareTo(safeVersion) > 0) {
return null;
} else {
@SuppressWarnings("deprecation")
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
return new UnsupportedListener("The NETWORK_OBJECT_INJECTOR hook doesn't support map chunk listeners.", unsupported);
} else {
return null;
}
}
}
@Override
public void injectManager() {
if (networkManager != null) {
final Class<?> networkInterface = networkManagerRef.getField().getType();
final Object networkDelegate = networkManagerRef.getOldValue();
if (!networkInterface.isInterface()) {
throw new UnsupportedOperationException(
"Must use CraftBukkit 1.3.0 or later to inject into into NetworkMananger.");
}
Callback queueFilter = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object packet = args[0];
if (packet != null) {
packet = handlePacketSending(packet);
// A NULL packet indicate cancelling
if (packet != null)
args[0] = packet;
else
return null;
}
// Delegate to our underlying class
return proxy.invokeSuper(networkDelegate, args);
}
};
Callback dispatch = new LazyLoader() {
@Override
public Object loadObject() throws Exception {
return networkDelegate;
}
};
// Share callback filter - that way, we avoid generating a new class every time.
if (callbackFilter == null) {
callbackFilter = new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.equals(queueMethod))
return 0;
else
return 1;
}
};
}
// Create our proxy object
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
ex.setSuperclass(networkInterface);
ex.setCallbacks(new Callback[] { queueFilter, dispatch });
ex.setCallbackFilter(callbackFilter);
// Inject it, if we can.
networkManagerRef.setValue(ex.create());
}
}
@Override
protected void cleanHook() {
// Clean up
if (networkManagerRef != null && networkManagerRef.isCurrentSet()) {
networkManagerRef.revertValue();
}
}
@Override
public void handleDisconnect() {
// No need to do anything
}
@Override
public boolean canInject(GamePhase phase) {
// Works for all phases
return true;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_MANAGER_OBJECT;
}
}
@@ -1,367 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import org.bukkit.entity.Player;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
/**
* Represents a player hook into the NetServerHandler class.
*
* @author Kristian
*/
class NetworkServerInjector extends PlayerInjector {
// Disconnected field
public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s.");
public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?");
public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice.");
private volatile static CallbackFilter callbackFilter;
private volatile static boolean foundSendPacket;
private volatile static Field disconnectField;
private InjectedServerConnection serverInjection;
// Determine if we're listening
private IntegerSet sendingFilters;
// Whether or not the player has disconnected
private boolean hasDisconnected;
// Used to copy fields
private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector(
ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters,
InjectedServerConnection serverInjection) {
super(reporter, player, invoker);
this.sendingFilters = sendingFilters;
this.serverInjection = serverInjection;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
if (serverDelegate != null) {
try {
if (marker != null) {
queuedMarkers.put(packet, marker);
}
// Note that invocation target exception is a wrapper for a checked exception
MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet);
} catch (IllegalArgumentException e) {
throw e;
} catch (InvocationTargetException e) {
throw e;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access send packet method.", e);
}
} else {
throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
}
}
@Override
public void injectManager() {
if (serverHandlerRef == null)
throw new IllegalStateException("Cannot find server handler.");
// Don't inject twice
if (serverHandlerRef.getValue() instanceof Factory)
return;
if (!tryInjectManager()) {
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
// Try to override the proxied object
if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue();
if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
else
serverHandlerClass = serverHandler.getClass();
// Try again
if (tryInjectManager()) {
// It worked - probably
return;
}
}
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the "
+ serverHandlerClass.getName() + " object.");
}
}
private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass();
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
Callback sendPacketCallback = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object packet = args[0];
if (packet != null) {
packet = handlePacketSending(packet);
// A NULL packet indicate cancelling
if (packet != null)
args[0] = packet;
else
return null;
}
// Call the method directly
return proxy.invokeSuper(obj, args);
};
};
Callback noOpCallback = NoOp.INSTANCE;
// Share callback filter - that way, we avoid generating a new class for
// every logged in player.
if (callbackFilter == null) {
callbackFilter = new SendMethodFilter();
}
ex.setSuperclass(serverClass);
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
ex.setCallbackFilter(callbackFilter);
// Find the Minecraft NetServerHandler superclass
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
DefaultInstances serverInstances = null;
// Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) {
serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
} else {
serverInstances = DefaultInstances.fromArray(generator);
}
serverInstances.setNonNull(true);
serverInstances.setMaximumRecursion(1);
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
// Inject it now
if (proxyObject != null) {
// Did we override a sendPacket method?
if (!foundSendPacket) {
throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass);
}
serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverHandlerRef.setValue(proxyObject);
return true;
} else {
return false;
}
}
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Oh well
}
}
return null;
}
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (MinecraftReflection.isMinecraftClass(clazz))
return clazz;
else if (clazz.equals(Object.class))
return clazz;
else
return getFirstMinecraftSuperClass(clazz.getSuperclass());
}
@Override
protected void cleanHook() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
try {
if (getNetHandler() != null) {
// Restore packet listener
try {
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
} catch (IllegalAccessException e) {
// Oh well
e.printStackTrace();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Prevent the PlayerQuitEvent from being sent twice
if (hasDisconnected) {
setDisconnect(serverHandlerRef.getValue(), true);
}
}
serverInjection.revertServerHandler(serverHandler);
}
@Override
public void handleDisconnect() {
hasDisconnected = true;
}
/**
* Set the disconnected field in a NetServerHandler.
* @param handler - the NetServerHandler.
* @param value - the new value.
*/
private void setDisconnect(Object handler, boolean value) {
// Set it
try {
// Load the field
if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
}
FieldUtils.writeField(disconnectField, handler, value);
} catch (IllegalArgumentException e) {
// Assume it's the first ...
if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class);
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField));
// Try again
if (disconnectField != null) {
setDisconnect(handler, value);
return;
}
}
// This is really bad
reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e));
} catch (IllegalAccessException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e));
}
}
@Override
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
// We support everything
return null;
}
@Override
public boolean canInject(GamePhase phase) {
// Doesn't work when logging in
return phase == GamePhase.PLAYING;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
}
/**
* Represents a CallbackFilter that only matches the SendPacket method.
* @author Kristian
*/
private static class SendMethodFilter implements CallbackFilter {
private Method sendPacket = MinecraftMethods.getSendPacketMethod();
@Override
public int accept(Method method) {
if (isCallableEqual(sendPacket, method)) {
NetworkServerInjector.foundSendPacket = true;
return 0;
} else {
return 1;
}
}
/**
* Determine if the two methods are equal in terms of call semantics.
* <p>
* Two methods are equal if they have the same name, parameter types and return type.
* @param first - first method.
* @param second - second method.
* @return TRUE if they are, FALSE otherwise.
*/
private boolean isCallableEqual(Method first, Method second) {
return first.getName().equals(second.getName()) &&
first.getReturnType().equals(second.getReturnType()) &&
Arrays.equals(first.getParameterTypes(), second.getParameterTypes());
}
}
}
@@ -1,760 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Map;
import net.sf.cglib.proxy.Factory;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.MapMaker;
public abstract class PlayerInjector implements SocketInjector {
// Disconnect method related reports
public static final ReportType REPORT_ASSUME_DISCONNECT_METHOD = new ReportType("Cannot find disconnect method by name. Assuming %s.");
public static final ReportType REPORT_INVALID_ARGUMENT_DISCONNECT = new ReportType("Invalid argument passed to disconnect method: %s");
public static final ReportType REPORT_CANNOT_ACCESS_DISCONNECT = new ReportType("Unable to access disconnect method.");
public static final ReportType REPORT_CANNOT_CLOSE_SOCKET = new ReportType("Unable to close socket.");
public static final ReportType REPORT_ACCESS_DENIED_CLOSE_SOCKET = new ReportType("Insufficient permissions. Cannot close socket.");
public static final ReportType REPORT_DETECTED_CUSTOM_SERVER_HANDLER =
new ReportType("Detected server handler proxy type by another plugin. Conflict may occur!");
public static final ReportType REPORT_CANNOT_PROXY_SERVER_HANDLER = new ReportType("Unable to load server handler from proxy type.");
public static final ReportType REPORT_CANNOT_UPDATE_PLAYER = new ReportType("Cannot update player in PlayerEvent.");
public static final ReportType REPORT_CANNOT_HANDLE_PACKET = new ReportType("Cannot handle server packet.");
public static final ReportType REPORT_INVALID_NETWORK_MANAGER = new ReportType("NetworkManager doesn't appear to be valid.");
// Net login handler stuff
private static Field netLoginNetworkField;
// Different disconnect methods
private static Method loginDisconnect;
private static Method serverDisconnect;
// Cache previously retrieved fields
protected static Field serverHandlerField;
protected static Field proxyServerField;
protected static Field networkManagerField;
protected static Field netHandlerField;
protected static Field socketField;
protected static Field socketAddressField;
private static Field inputField;
private static Field entityPlayerField;
// Whether or not we're using a proxy type
private static boolean hasProxyType;
// To add our injected array lists
protected static StructureModifier<Object> networkModifier;
// And methods
protected static Method queueMethod;
protected static Method processMethod;
protected volatile Player player;
protected boolean hasInitialized;
// Reference to the player's network manager
protected VolatileField networkManagerRef;
protected VolatileField serverHandlerRef;
protected Object networkManager;
// Current net handler
protected Object loginHandler;
protected Object serverHandler;
protected Object netHandler;
// Current socket and address
protected Socket socket;
protected SocketAddress socketAddress;
// The packet manager and filters
protected ListenerInvoker invoker;
// Previous data input
protected DataInputStream cachedInput;
// Handle errors
protected ErrorReporter reporter;
// Previous markers
protected Map<Object, NetworkMarker> queuedMarkers = new MapMaker().weakKeys().makeMap();
protected InterceptWritePacket writePacketInterceptor;
// Whether or not the injector has been cleaned
private boolean clean;
// Whether or not to update the current player on the first Packet1Login
boolean updateOnLogin;
volatile Player updatedPlayer;
public PlayerInjector(ErrorReporter reporter, Player player, ListenerInvoker invoker) {
this.reporter = reporter;
this.player = player;
this.invoker = invoker;
this.writePacketInterceptor = invoker.getInterceptWritePacket();
}
/**
* Retrieve the notch (NMS) entity player object.
* @param player - the player to retrieve.
* @return Notch player object.
*/
protected Object getEntityPlayer(Player player) {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
return unwrapper.unwrapItem(player);
}
/**
* Initialize all fields for this player injector, if it hasn't already.
* @param injectionSource - Injection source
* @throws IllegalAccessException An error has occured.
*/
public void initialize(Object injectionSource) throws IllegalAccessException {
if (injectionSource == null)
throw new IllegalArgumentException("injectionSource cannot be NULL");
//Dispatch to the correct injection method
if (injectionSource instanceof Player)
initializePlayer((Player) injectionSource);
else if (MinecraftReflection.isLoginHandler(injectionSource))
initializeLogin(injectionSource);
else
throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName());
}
/**
* Initialize the player injector using an actual player instance.
* @param player - the player to hook.
*/
public void initializePlayer(Player player) {
Object notchEntity = getEntityPlayer(player);
// Save the player too
this.player = player;
if (!hasInitialized) {
// Do this first, in case we encounter an exception
hasInitialized = true;
// Retrieve the server handler
if (serverHandlerField == null) {
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(
"NetServerHandler", MinecraftReflection.getPlayerConnectionClass());
proxyServerField = getProxyField(notchEntity, serverHandlerField);
}
// Yo dawg
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
serverHandler = serverHandlerRef.getValue();
// Next, get the network manager
if (networkManagerField == null)
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(
"networkManager", MinecraftReflection.getNetworkManagerClass());
initializeNetworkManager(networkManagerField, serverHandler);
}
}
/**
* Initialize the player injector from a NetLoginHandler.
* @param netLoginHandler - the net login handler to inject.
*/
public void initializeLogin(Object netLoginHandler) {
if (!hasInitialized) {
// Just in case
if (!MinecraftReflection.isLoginHandler(netLoginHandler))
throw new IllegalArgumentException("netLoginHandler (" + netLoginHandler + ") is not a " +
MinecraftReflection.getNetLoginHandlerName());
hasInitialized = true;
loginHandler = netLoginHandler;
if (netLoginNetworkField == null)
netLoginNetworkField = FuzzyReflection.fromObject(netLoginHandler).
getFieldByType("networkManager", MinecraftReflection.getNetworkManagerClass());
initializeNetworkManager(netLoginNetworkField, netLoginHandler);
}
}
private void initializeNetworkManager(Field reference, Object container) {
networkManagerRef = new VolatileField(reference, container);
networkManager = networkManagerRef.getValue();
// No, don't do it
if (networkManager instanceof Factory) {
return;
}
// Create the network manager modifier from the actual object type
if (networkManager != null && networkModifier == null)
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
// And the queue method
if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(reference.getType()).
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
}
/**
* Retrieve whether or not the server handler is a proxy object.
* @return TRUE if it is, FALSE otherwise.
*/
protected boolean hasProxyServerHandler() {
return hasProxyType;
}
/**
* Retrieve the current network manager.
* @return Current network manager.
*/
public Object getNetworkManager() {
return networkManagerRef.getValue();
}
/**
* Retrieve the current server handler (PlayerConnection).
* @return Current server handler.
*/
public Object getServerHandler() {
return serverHandlerRef.getValue();
}
/**
* Set the current network manager.
* @param value - new network manager.
* @param force - whether or not to save this value.
*/
public void setNetworkManager(Object value, boolean force) {
networkManagerRef.setValue(value);
if (force)
networkManagerRef.saveValue();
initializeNetworkManager(networkManagerField, serverHandler);
}
/**
* Retrieve the associated socket of this player.
* @return The associated socket.
* @throws IllegalAccessException If we're unable to read the socket field.
*/
@Override
public Socket getSocket() throws IllegalAccessException {
try {
if (socketField == null)
socketField = FuzzyReflection.fromObject(networkManager, true).
getFieldListByType(Socket.class).get(0);
if (socket == null)
socket = (Socket) FieldUtils.readField(socketField, networkManager, true);
return socket;
} catch (IndexOutOfBoundsException e) {
throw new IllegalAccessException("Unable to read the socket field.");
}
}
/**
* Retrieve the associated remote address of a player.
* @return The associated remote address.
* @throws IllegalAccessException If we're unable to read the socket address field.
*/
@Override
public SocketAddress getAddress() throws IllegalAccessException {
try {
if (socketAddressField == null)
socketAddressField = FuzzyReflection.fromObject(networkManager, true).
getFieldListByType(SocketAddress.class).get(0);
if (socketAddress == null)
socketAddress = (SocketAddress) FieldUtils.readField(socketAddressField, networkManager, true);
return socketAddress;
} catch (IndexOutOfBoundsException e) {
// Inform about the state of the network manager too
reporter.reportWarning(
this, Report.newBuilder(REPORT_INVALID_NETWORK_MANAGER).callerParam(networkManager).build());
throw new IllegalAccessException("Unable to read the socket address field.");
}
}
/**
* Attempt to disconnect the current client.
* @param message - the message to display.
* @throws InvocationTargetException If disconnection failed.
*/
@Override
public void disconnect(String message) throws InvocationTargetException {
// Get a non-null handler
boolean usingNetServer = serverHandler != null;
Object handler = usingNetServer ? serverHandler : loginHandler;
Method disconnect = usingNetServer ? serverDisconnect : loginDisconnect;
// Execute disconnect on it
if (handler != null) {
if (disconnect == null) {
try {
disconnect = FuzzyReflection.fromObject(handler).getMethodByName("disconnect.*");
} catch (IllegalArgumentException e) {
// Just assume it's the first String method
disconnect = FuzzyReflection.fromObject(handler).getMethodByParameters("disconnect", String.class);
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUME_DISCONNECT_METHOD).messageParam(disconnect));
}
// Save the method for later
if (usingNetServer)
serverDisconnect = disconnect;
else
loginDisconnect = disconnect;
}
try {
disconnect.invoke(handler, message);
return;
} catch (IllegalArgumentException e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_INVALID_ARGUMENT_DISCONNECT).error(e).messageParam(message).callerParam(handler));
} catch (IllegalAccessException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_ACCESS_DISCONNECT).error(e));
}
}
// Fuck it
try {
Socket socket = getSocket();
try {
socket.close();
} catch (IOException e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_CLOSE_SOCKET).error(e).callerParam(socket));
}
} catch (IllegalAccessException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_ACCESS_DENIED_CLOSE_SOCKET).error(e));
}
}
private Field getProxyField(Object notchEntity, Field serverField) {
try {
Object currentHandler = FieldUtils.readField(serverHandlerField, notchEntity, true);
// This is bad
if (currentHandler == null)
throw new ServerHandlerNull();
// See if this isn't a standard net handler class
if (!isStandardMinecraftNetHandler(currentHandler)) {
// This is our proxy object
if (currentHandler instanceof Factory)
return null;
hasProxyType = true;
reporter.reportWarning(this, Report.newBuilder(REPORT_DETECTED_CUSTOM_SERVER_HANDLER).callerParam(serverField));
// No? Is it a Proxy type?
try {
FuzzyReflection reflection = FuzzyReflection.fromObject(currentHandler, true);
// It might be
return reflection.getFieldByType("NetServerHandler", MinecraftReflection.getPlayerConnectionClass());
} catch (RuntimeException e) {
// Damn
}
}
} catch (IllegalAccessException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_PROXY_SERVER_HANDLER).error(e).callerParam(notchEntity, serverField));
}
// Nope, just go with it
return null;
}
/**
* Determine if a given object is a standard Minecraft net handler.
* @param obj the object to test.
* @return TRUE if it is, FALSE otherwise.
*/
private boolean isStandardMinecraftNetHandler(Object obj) {
if (obj == null)
return false;
Class<?> clazz = obj.getClass();
return MinecraftReflection.getNetLoginHandlerClass().equals(clazz) ||
MinecraftReflection.getPlayerConnectionClass().equals(clazz);
}
/**
* Retrieves the current net handler for this player.
* @return Current net handler.
* @throws IllegalAccessException Unable to find or retrieve net handler.
*/
protected Object getNetHandler() throws IllegalAccessException {
return getNetHandler(false);
}
/**
* Retrieves the current net handler for this player.
* @param refresh - Whether or not to refresh
* @return Current net handler.
* @throws IllegalAccessException Unable to find or retrieve net handler.
* @return The current net handler for this player
*/
protected Object getNetHandler(boolean refresh) throws IllegalAccessException {
// What a mess
try {
if (netHandlerField == null)
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType("NetHandler", MinecraftReflection.getNetHandlerClass());
} catch (RuntimeException e1) {
// Swallow it
}
// Second attempt
if (netHandlerField == null) {
try {
// Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
} catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
}
}
// Get the handler
if (netHandler == null || refresh)
netHandler = FieldUtils.readField(netHandlerField, networkManager, true);
return netHandler;
}
/**
* Retrieve the stored entity player from a given NetHandler.
* @param netHandler - the nethandler to retrieve it from.
* @return The stored entity player.
* @throws IllegalAccessException If the reflection failed.
*/
private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
if (entityPlayerField == null)
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(
"EntityPlayer", MinecraftReflection.getEntityPlayerClass());
return FieldUtils.readField(entityPlayerField, netHandler);
}
/**
* Processes the given packet as if it was transmitted by the current player.
* @param packet - packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException {
Object netHandler = getNetHandler();
// Get the process method
if (processMethod == null) {
try {
processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
}
}
// We're ready
try {
processMethod.invoke(packet, netHandler);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Method " + processMethod.getName() + " is not compatible.");
} catch (InvocationTargetException e) {
throw e;
}
}
/**
* Send a packet to the client.
* @param packet - server packet to send.
* @param marker - the network marker.
* @param filtered - whether or not the packet will be filtered by our listeners.
* @throws InvocationTargetException If an error occured when sending the packet.
*/
@Override
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException;
/**
* Inject a hook to catch packets sent to the current player.
*/
public abstract void injectManager();
/**
* Remove all hooks and modifications.
*/
public final void cleanupAll() {
if (!clean) {
cleanHook();
writePacketInterceptor.cleanup();
}
clean = true;
}
/**
* Clean up after the player has disconnected.
*/
public abstract void handleDisconnect();
/**
* Override to add custom cleanup behavior.
*/
protected abstract void cleanHook();
/**
* Determine whether or not this hook has already been cleaned.
* @return TRUE if it has, FALSE otherwise.
*/
public boolean isClean() {
return clean;
}
/**
* Determine if this inject method can even be attempted.
* @param state - Game phase
* @return TRUE if can be attempted, though possibly with failure, FALSE otherwise.
*/
public abstract boolean canInject(GamePhase state);
/**
* Retrieve the hook type this class represents.
* @return Hook type this class represents.
*/
public abstract PlayerInjectHooks getHookType();
/**
* Invoked before a new listener is registered.
* <p>
* The player injector should only return a non-null value if some or all of the packet IDs are unsupported.
* @param version - the current Minecraft version, or NULL if unknown.
* @param listener - the listener that is about to be registered.
* @return A error message with the unsupported packet IDs, or NULL if this listener is valid.
*/
public abstract UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener);
/**
* Allows a packet to be sent by the listeners.
* @param packet - packet to sent.
* @return The given packet, or the packet replaced by the listeners.
*/
@SuppressWarnings({ "deprecation", "null" })
public Object handlePacketSending(Object packet) {
try {
// Get the packet ID too
Integer id = invoker.getPacketID(packet);
Player currentPlayer = player;
// Hack #1
if (updateOnLogin) {
if (updatedPlayer == null) {
try {
final Object handler = getNetHandler(true);
// Is this a net server class?
if (MinecraftReflection.getPlayerConnectionClass().isAssignableFrom(handler.getClass())) {
setUpdatedPlayer(
(Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(handler))
);
}
} catch (IllegalAccessException e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLAYER).error(e).callerParam(packet));
}
}
// This will only occur in the NetLoginHandler injection
if (updatedPlayer != null) {
currentPlayer = updatedPlayer;
updateOnLogin = false;
}
}
// Make sure we're listening
if (id != null && hasListener(id)) {
NetworkMarker marker = queuedMarkers.remove(packet);
// A packet has been sent guys!
PacketType type = PacketType.findLegacy(id, Sender.SERVER);
PacketContainer container = new PacketContainer(type, packet);
PacketEvent event = PacketEvent.fromServer(invoker, container, marker, currentPlayer);
invoker.invokePacketSending(event);
// Cancelling is pretty simple. Just ignore the packet.
if (event.isCancelled())
return null;
// Right, remember to replace the packet again
Object result = event.getPacket().getHandle();
marker = NetworkMarker.getNetworkMarker(event);
// See if we need to proxy the write method
if (result != null && (NetworkMarker.hasOutputHandlers(marker) || NetworkMarker.hasPostListeners(marker))) {
result = writePacketInterceptor.constructProxy(result, event, marker);
}
return result;
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_HANDLE_PACKET).error(e).callerParam(packet));
}
return packet;
}
/**
* Determine if the given injector is listening for this packet ID.
* @param packetID - packet ID to check.
* @return TRUE if it is, FALSE oterhwise.
*/
protected abstract boolean hasListener(int packetID);
/**
* Retrieve the current player's input stream.
* @param cache - whether or not to cache the result of this method.
* @return The player's input stream.
*/
public DataInputStream getInputStream(boolean cache) {
// And the data input stream that we'll use to identify a player
if (networkManager == null)
throw new IllegalStateException("Network manager is NULL.");
if (inputField == null)
inputField = FuzzyReflection.fromObject(networkManager, true).
getFieldByType("java\\.io\\.DataInputStream");
// Get the associated input stream
try {
if (cache && cachedInput != null)
return cachedInput;
// Save to cache
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager, true);
return cachedInput;
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to read input stream.", e);
}
}
/**
* Retrieve the hooked player.
*/
@Override
public Player getPlayer() {
return player;
}
/**
* Set the hooked player.
* <p>
* Should only be called during the creation of the injector.
* @param player - the new hooked player.
*/
public void setPlayer(Player player) {
this.player = player;
}
/**
* Object that can invoke the packet events.
* @return Packet event invoker.
*/
public ListenerInvoker getInvoker() {
return invoker;
}
/**
* Retrieve the hooked player object OR the more up-to-date player instance.
* @return The hooked player, or a more up-to-date instance.
*/
@Override
public Player getUpdatedPlayer() {
if (updatedPlayer != null)
return updatedPlayer;
else
return player;
}
@Override
public void transferState(SocketInjector delegate) {
// Do nothing
}
@Override
public void setUpdatedPlayer(Player updatedPlayer) {
this.updatedPlayer = updatedPlayer;
}
/**
* Indicates that a player's NetServerHandler or PlayerConnection was NULL.
* <p>
* This is usually because the player has just logged out, or due to it being a "fake" player in MCPC+/Cauldron.
* @author Kristian
*/
public static class ServerHandlerNull extends IllegalAccessError {
private static final long serialVersionUID = 1L;
public ServerHandlerNull() {
super("Unable to fetch server handler: was NUll.");
}
public ServerHandlerNull(String s) {
super(s);
}
}
}
@@ -1,143 +0,0 @@
package com.comphenix.protocol.injector.player;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
/**
* Constructor for different player injectors.
*
* @author Kristian
*/
public class PlayerInjectorBuilder {
public static PlayerInjectorBuilder newBuilder() {
return new PlayerInjectorBuilder();
}
protected PlayerInjectorBuilder() {
// Use the static method.
}
protected ErrorReporter reporter;
protected Predicate<GamePhase> injectionFilter;
protected ListenerInvoker invoker;
protected Set<PacketListener> packetListeners;
protected Server server;
protected MinecraftVersion version;
/**
* The error reporter used by the created injector.
* @param reporter - new error reporter.
* @return This builder, for chaining.
*/
public PlayerInjectorBuilder reporter(@Nonnull ErrorReporter reporter) {
Preconditions.checkNotNull(reporter, "reporter cannot be NULL");
this.reporter = reporter;
return this;
}
/**
* The injection filter that is used to determine if it is necessary to perform
* injection during a certain phase.
* @param injectionFilter - filter predicate.
* @return This builder, for chaining.
*/
@Nonnull
public PlayerInjectorBuilder injectionFilter(@Nonnull Predicate<GamePhase> injectionFilter) {
Preconditions.checkNotNull(injectionFilter, "injectionFilter cannot be NULL");
this.injectionFilter = injectionFilter;
return this;
}
/**
* The packet stream invoker.
* @param invoker - the invoker.
* @return This builder, for chaining.
*/
public PlayerInjectorBuilder invoker(@Nonnull ListenerInvoker invoker) {
Preconditions.checkNotNull(invoker, "invoker cannot be NULL");
this.invoker = invoker;
return this;
}
/**
* Set the set of packet listeners.
* @param packetListeners - packet listeners.
* @return This builder, for chaining.
*/
@Nonnull
public PlayerInjectorBuilder packetListeners(@Nonnull Set<PacketListener> packetListeners) {
Preconditions.checkNotNull(packetListeners, "packetListeners cannot be NULL");
this.packetListeners = packetListeners;
return this;
}
/**
* Set the Bukkit server used for scheduling.
* @param server - the Bukkit server.
* @return This builder, for chaining.
*/
public PlayerInjectorBuilder server(@Nonnull Server server) {
Preconditions.checkNotNull(server, "server cannot be NULL");
this.server = server;
return this;
}
/**
* Set the current Minecraft version.
* @param version - the current Minecraft version, or NULL if unknown.
* @return This builder, for chaining.
*/
public PlayerInjectorBuilder version(MinecraftVersion version) {
this.version = version;
return this;
}
/**
* Called before an object is created with this builder.
*/
private void initializeDefaults() {
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
// Initialize with default values if we can
if (reporter == null)
reporter = ProtocolLibrary.getErrorReporter();
if (invoker == null)
invoker = (PacketFilterManager) manager;
if (server == null)
server = Bukkit.getServer();
if (injectionFilter == null)
throw new IllegalStateException("injectionFilter must be initialized.");
if (packetListeners == null)
throw new IllegalStateException("packetListeners must be initialized.");
}
/**
* Construct the injection handler.
* <p>
* Any builder parameter marked as NON-NULL is essential and must be initialized.
* @return The constructed injection handler using the current parameters.
*/
public PlayerInjectionHandler buildHandler() {
// Fill any default fields
initializeDefaults();
return new ProxyPlayerInjectionHandler(
reporter, injectionFilter,
invoker, packetListeners, server, version);
}
}
@@ -1,763 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import io.netty.channel.Channel;
import java.io.DataInputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.BlockingHashMap;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.comphenix.protocol.injector.PlayerLoggedOutException;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.injector.server.BukkitSocketInjector;
import com.comphenix.protocol.injector.server.InputStreamLookupBuilder;
import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
/**
* Responsible for injecting into a player's sendPacket method.
*
* @author Kristian
*/
class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
// Warnings and errors
public static final ReportType REPORT_UNSUPPPORTED_LISTENER = new ReportType("Cannot fully register listener for %s: %s");
// Fallback to older player hook types
public static final ReportType REPORT_PLAYER_HOOK_FAILED = new ReportType("Player hook %s failed.");
public static final ReportType REPORT_SWITCHED_PLAYER_HOOK = new ReportType("Switching to %s instead.");
public static final ReportType REPORT_HOOK_CLEANUP_FAILED = new ReportType("Cleaing up after player hook failed.");
public static final ReportType REPORT_CANNOT_REVERT_HOOK = new ReportType("Unable to fully revert old injector. May cause conflicts.");
// Server connection injection
private InjectedServerConnection serverInjection;
// Server socket injection
private AbstractInputStreamLookup inputStreamLookup;
// NetLogin injector
private NetLoginInjector netLoginInjector;
// The last successful player hook
private WeakReference<PlayerInjector> lastSuccessfulHook;
// Dummy injection
private ConcurrentMap<Player, PlayerInjector> dummyInjectors =
SafeCacheBuilder.newBuilder().
expireAfterWrite(30, TimeUnit.SECONDS).
build(BlockingHashMap.<Player, PlayerInjector>newInvalidCacheLoader());
// Player injection
private Map<Player, PlayerInjector> playerInjection = Maps.newConcurrentMap();
// Player injection types
private volatile PlayerInjectHooks loginPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
private volatile PlayerInjectHooks playingPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
// Error reporter
private ErrorReporter reporter;
// Whether or not we're closing
private boolean hasClosed;
// Used to invoke events
private ListenerInvoker invoker;
// Current Minecraft version
private MinecraftVersion version;
// Enabled packet filters
private IntegerSet sendingFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
// List of packet listeners
private Set<PacketListener> packetListeners;
// Used to filter injection attempts
private Predicate<GamePhase> injectionFilter;
public ProxyPlayerInjectionHandler(
ErrorReporter reporter, Predicate<GamePhase> injectionFilter,
ListenerInvoker invoker, Set<PacketListener> packetListeners, Server server, MinecraftVersion version) {
this.reporter = reporter;
this.invoker = invoker;
this.injectionFilter = injectionFilter;
this.packetListeners = packetListeners;
this.version = version;
this.inputStreamLookup = InputStreamLookupBuilder.newBuilder().
server(server).
reporter(reporter).
build();
// Create net login injectors and the server connection injector
this.netLoginInjector = new NetLoginInjector(reporter, server, this);
this.serverInjection = new InjectedServerConnection(reporter, inputStreamLookup, server, netLoginInjector);
serverInjection.injectList();
}
@Override
public int getProtocolVersion(Player player) {
// Just use the server version
return MinecraftProtocolVersion.getCurrentVersion();
}
/**
* Retrieves how the server packets are read.
* @return Injection method for reading server packets.
*/
@Override
public PlayerInjectHooks getPlayerHook() {
return getPlayerHook(GamePhase.PLAYING);
}
/**
* Retrieves how the server packets are read.
* @param phase - the current game phase.
* @return Injection method for reading server packets.
*/
@Override
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
switch (phase) {
case LOGIN:
return loginPlayerHook;
case PLAYING:
return playingPlayerHook;
default:
throw new IllegalArgumentException("Cannot retrieve injection hook for both phases at the same time.");
}
}
@Override
public boolean hasMainThreadListener(PacketType type) {
return sendingFilters.contains(type.getLegacyId());
}
/**
* Sets how the server packets are read.
* @param playerHook - the new injection method for reading server packets.
*/
@Override
public void setPlayerHook(PlayerInjectHooks playerHook) {
setPlayerHook(GamePhase.PLAYING, playerHook);
}
/**
* Sets how the server packets are read.
* @param phase - the current game phase.
* @param playerHook - the new injection method for reading server packets.
*/
@Override
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
if (phase.hasLogin())
loginPlayerHook = playerHook;
if (phase.hasPlaying())
playingPlayerHook = playerHook;
// Make sure the current listeners are compatible
checkListener(packetListeners);
}
@Override
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
sendingFilters.add(type.getLegacyId());
}
@Override
public void removePacketHandler(PacketType type) {
sendingFilters.remove(type.getLegacyId());
}
/**
* Used to construct a player hook.
* @param player - the player to hook.
* @param hook - the hook type.
* @return A new player hoook
* @throws IllegalAccessException Unable to do our reflection magic.
*/
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
// Construct the correct player hook
switch (hook) {
case NETWORK_HANDLER_FIELDS:
return new NetworkFieldInjector(reporter, player, invoker, sendingFilters);
case NETWORK_MANAGER_OBJECT:
return new NetworkObjectInjector(reporter, player, invoker, sendingFilters);
case NETWORK_SERVER_OBJECT:
return new NetworkServerInjector(reporter, player, invoker, sendingFilters, serverInjection);
default:
throw new IllegalArgumentException("Cannot construct a player injector.");
}
}
/**
* Retrieve a player by its DataInput connection.
* @param inputStream - the associated DataInput connection.
* @return The player we found.
*/
@Override
public Player getPlayerByConnection(DataInputStream inputStream) {
// Wait until the connection owner has been established
SocketInjector injector = inputStreamLookup.waitSocketInjector(inputStream);
if (injector != null) {
return injector.getPlayer();
} else {
return null;
}
}
/**
* Helper function that retrieves the injector type of a given player injector.
* @param injector - injector type.
* @return The injector type.
*/
private PlayerInjectHooks getInjectorType(PlayerInjector injector) {
return injector != null ? injector.getHookType() : PlayerInjectHooks.NONE;
}
/**
* Initialize a player hook, allowing us to read server packets.
* <p>
* This call will be ignored if there's no listener that can receive the given events.
* @param player - player to hook.
* @param strategy - how to handle previous player injections.
*/
@Override
public void injectPlayer(Player player, ConflictStrategy strategy) {
// Inject using the player instance itself
if (isInjectionNecessary(GamePhase.PLAYING)) {
injectPlayer(player, player, strategy, GamePhase.PLAYING);
}
}
/**
* Determine if it's truly necessary to perform the given player injection.
* @param phase - current game phase.
* @return TRUE if we should perform the injection, FALSE otherwise.
*/
public boolean isInjectionNecessary(GamePhase phase) {
return injectionFilter.apply(phase);
}
/**
* Initialize a player hook, allowing us to read server packets.
* <p>
* This method will always perform the instructed injection.
*
* @param player - player to hook.
* @param injectionPoint - the object to use during the injection process.
* @param phase - the current game phase.
* @return The resulting player injector, or NULL if the injection failed.
*/
PlayerInjector injectPlayer(Player player, Object injectionPoint, ConflictStrategy stategy, GamePhase phase) {
if (player == null)
throw new IllegalArgumentException("Player cannot be NULL.");
if (injectionPoint == null)
throw new IllegalArgumentException("injectionPoint cannot be NULL.");
if (phase == null)
throw new IllegalArgumentException("phase cannot be NULL.");
// Unfortunately, due to NetLoginHandler, multiple threads may potentially call this method.
synchronized (player) {
return injectPlayerInternal(player, injectionPoint, stategy, phase);
}
}
// Unsafe variant of the above
private PlayerInjector injectPlayerInternal(Player player, Object injectionPoint, ConflictStrategy stategy, GamePhase phase) {
PlayerInjector injector = playerInjection.get(player);
PlayerInjectHooks tempHook = getPlayerHook(phase);
PlayerInjectHooks permanentHook = tempHook;
// The given player object may be fake, so be careful!
// See if we need to inject something else
boolean invalidInjector = injector != null ? !injector.canInject(phase) : true;
// Don't inject if the class has closed
if (!hasClosed && (tempHook != getInjectorType(injector) || invalidInjector)) {
while (tempHook != PlayerInjectHooks.NONE) {
// Whether or not the current hook method failed completely
boolean hookFailed = false;
// Remove the previous hook, if any
cleanupHook(injector);
try {
injector = getHookInstance(player, tempHook);
// Make sure this injection method supports the current game phase
if (injector.canInject(phase)) {
injector.initialize(injectionPoint);
// Get socket and socket injector
SocketAddress address = injector.getAddress();
// Ignore logged out players
if (address == null)
return null;
SocketInjector previous = inputStreamLookup.peekSocketInjector(address);
Socket socket = injector.getSocket();
// Close any previously associated hooks before we proceed
if (previous != null && !(player instanceof Factory)) {
switch (stategy) {
case OVERRIDE:
uninjectPlayer(previous.getPlayer(), true);
break;
case BAIL_OUT:
return null;
}
}
injector.injectManager();
saveAddressLookup(address, socket, injector);
break;
}
} catch (PlayerLoggedOutException e) {
throw e;
} catch (Exception e) {
// Mark this injection attempt as a failure
reporter.reportDetailed(this,
Report.newBuilder(REPORT_PLAYER_HOOK_FAILED).messageParam(tempHook).callerParam(player, injectionPoint, phase).error(e)
);
hookFailed = true;
}
// Choose the previous player hook type
tempHook = PlayerInjectHooks.values()[tempHook.ordinal() - 1];
if (hookFailed)
reporter.reportWarning(this, Report.newBuilder(REPORT_SWITCHED_PLAYER_HOOK).messageParam(tempHook));
// Check for UTTER FAILURE
if (tempHook == PlayerInjectHooks.NONE) {
cleanupHook(injector);
injector = null;
hookFailed = true;
}
// Should we set the default hook method too?
if (hookFailed) {
permanentHook = tempHook;
}
}
// Update values
if (injector != null)
lastSuccessfulHook = new WeakReference<PlayerInjector>(injector);
if (permanentHook != getPlayerHook(phase))
setPlayerHook(phase, tempHook);
// Save injector
if (injector != null) {
playerInjection.put(player, injector);
}
}
return injector;
}
private void saveAddressLookup(SocketAddress address, Socket socket, SocketInjector injector) {
SocketAddress socketAddress = socket != null ? socket.getRemoteSocketAddress() : null;
if (socketAddress != null && !Objects.equal(socketAddress, address)) {
// Save this version as well
inputStreamLookup.setSocketInjector(socketAddress, injector);
}
// Save injector
inputStreamLookup.setSocketInjector(address, injector);
}
private void cleanupHook(PlayerInjector injector) {
// Clean up as much as possible
try {
if (injector != null)
injector.cleanupAll();
} catch (Exception ex) {
reporter.reportDetailed(this, Report.newBuilder(REPORT_HOOK_CLEANUP_FAILED).callerParam(injector).error(ex));
}
}
/**
* Invoke special routines for handling disconnect before a player is uninjected.
* @param player - player to process.
*/
@Override
public void handleDisconnect(Player player) {
PlayerInjector injector = getInjector(player);
if (injector != null) {
injector.handleDisconnect();
}
}
@Override
public void updatePlayer(Player player) {
SocketAddress address = player.getAddress();
// Ignore logged out players
if (address != null) {
SocketInjector injector = inputStreamLookup.peekSocketInjector(address);
if (injector != null) {
injector.setUpdatedPlayer(player);
} else {
inputStreamLookup.setSocketInjector(player.getAddress(),
new BukkitSocketInjector(player));
}
}
}
/**
* Unregisters the given player.
* @param player - player to unregister.
* @return TRUE if a player has been uninjected, FALSE otherwise.
*/
@Override
public boolean uninjectPlayer(Player player) {
return uninjectPlayer(player, false);
}
/**
* Unregisters the given player.
* @param player - player to unregister.
* @param prepareNextHook - whether or not we need to fix any lingering hooks.
* @return TRUE if a player has been uninjected, FALSE otherwise.
*/
private boolean uninjectPlayer(Player player, boolean prepareNextHook) {
if (!hasClosed && player != null) {
PlayerInjector injector = playerInjection.remove(player);
if (injector != null) {
injector.cleanupAll();
// Remove the "hooked" network manager in our instance as well
if (prepareNextHook && injector instanceof NetworkObjectInjector) {
try {
PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT);
dummyInjector.initializePlayer(player);
dummyInjector.setNetworkManager(injector.getNetworkManager(), true);
} catch (IllegalAccessException e) {
// Let the user know
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_REVERT_HOOK).error(e));
}
}
return true;
}
}
return false;
}
/**
* Unregisters a player by the given address.
* <p>
* If the server handler has been created before we've gotten a chance to unject the player,
* the method will try a workaround to remove the injected hook in the NetServerHandler.
*
* @param address - address of the player to unregister.
* @return TRUE if a player has been uninjected, FALSE otherwise.
*/
@Override
public boolean uninjectPlayer(InetSocketAddress address) {
if (!hasClosed && address != null) {
SocketInjector injector = inputStreamLookup.peekSocketInjector(address);
// Clean up
if (injector != null)
uninjectPlayer(injector.getPlayer(), true);
return true;
}
return false;
}
/**
* Send the given packet to the given receiver.
* @param receiver - the player receiver.
* @param packet - the packet to send.
* @param filters - whether or not to invoke the packet filters.
* @throws InvocationTargetException If an error occured during sending.
*/
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
SocketInjector injector = getInjector(receiver);
// Send the packet, or drop it completely
if (injector != null) {
injector.sendServerPacket(packet.getHandle(), marker, filters);
} else {
throw new PlayerLoggedOutException(String.format(
"Unable to send packet %s (%s): Player %s has logged out.",
packet.getType(), packet, receiver
));
}
}
/**
* Recieve a packet as if it were sent by the given player.
* @param player - the sender.
* @param mcPacket - the packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
@Override
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
PlayerInjector injector = getInjector(player);
// Process the given packet, or simply give up
if (injector != null)
injector.processPacket(mcPacket);
else
throw new PlayerLoggedOutException(String.format(
"Unable to receieve packet %s. Player %s has logged out.",
mcPacket, player
));
}
/**
* Retrieve the injector associated with this player.
* @param player - the player to find.
* @return The injector, or NULL if not found.
*/
private PlayerInjector getInjector(Player player) {
PlayerInjector injector = playerInjection.get(player);
if (injector == null) {
// Try getting it from the player itself
SocketAddress address = player.getAddress();
// Must have logged out - there's nothing we can do
if (address == null)
return null;
// Look that up without blocking
SocketInjector result = inputStreamLookup.peekSocketInjector(address);
// Ensure that it is non-null and a player injector
if (result instanceof PlayerInjector)
return (PlayerInjector) result;
else
// Make a dummy injector them
return createDummyInjector(player);
} else {
return injector;
}
}
/**
* Construct a simple dummy injector incase none has been constructed.
* @param player - the CraftPlayer to construct for.
* @return A dummy injector, or NULL if the given player is not a CraftPlayer.
*/
private PlayerInjector createDummyInjector(Player player) {
if (!MinecraftReflection.getCraftPlayerClass().isAssignableFrom(player.getClass())) {
// No - this is not safe
return null;
}
try {
PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT);
dummyInjector.initializePlayer(player);
// This probably means the player has disconnected
if (dummyInjector.getSocket() == null) {
return null;
}
inputStreamLookup.setSocketInjector(dummyInjector.getAddress(), dummyInjector);
dummyInjectors.put(player, dummyInjector);
return dummyInjector;
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access fields.", e);
}
}
/**
* Retrieve a player injector by looking for its NetworkManager.
* @param networkManager - current network manager.
* @return Related player injector.
*/
PlayerInjector getInjectorByNetworkHandler(Object networkManager) {
// That's not legal
if (networkManager == null)
return null;
// O(n) is okay in this instance. This is only a backup solution.
for (PlayerInjector injector : playerInjection.values()) {
if (injector.getNetworkManager() == networkManager)
return injector;
}
// None found
return null;
}
@Override
public boolean canRecievePackets() {
return false;
}
@Override
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
throw new UnsupportedOperationException("Proxy injection cannot handle received packets.");
}
/**
* Determine if the given listeners are valid.
* @param listeners - listeners to check.
*/
@Override
public void checkListener(Set<PacketListener> listeners) {
// Make sure the current listeners are compatible
if (getLastSuccessfulHook() != null) {
for (PacketListener listener : listeners) {
checkListener(listener);
}
}
}
/**
* Retrieve the last successful hook.
* <p>
* May be NULL if the hook has been uninjected.
* @return Last successful hook.
*/
private PlayerInjector getLastSuccessfulHook() {
return lastSuccessfulHook != null ? lastSuccessfulHook.get() : null;
}
/**
* Determine if a listener is valid or not.
* <p>
* If not, a warning will be printed to the console.
* @param listener - listener to check.
*/
@Override
public void checkListener(PacketListener listener) {
PlayerInjector last = getLastSuccessfulHook();
if (last != null) {
UnsupportedListener result = last.checkListener(version, listener);
// We won't prevent the listener, as it may still have valid packets
if (result != null) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPPORTED_LISTENER).messageParam(PacketAdapter.getPluginName(listener), result)
);
// These are illegal
for (int packetID : result.getPackets()) {
removePacketHandler(PacketType.findLegacy(packetID, Sender.CLIENT));
removePacketHandler(PacketType.findLegacy(packetID, Sender.SERVER));
}
}
}
}
/**
* Retrieve the current list of registered sending listeners.
* @return List of the sending listeners's packet IDs.
*/
@Override
public Set<PacketType> getSendingFilters() {
return PacketRegistry.toPacketTypes(sendingFilters.toSet(), Sender.SERVER);
}
@Override
public void close() {
// Guard
if (hasClosed || playerInjection == null)
return;
// Remove everything
for (PlayerInjector injection : playerInjection.values()) {
if (injection != null) {
injection.cleanupAll();
}
}
// Remove server handler
if (inputStreamLookup != null)
inputStreamLookup.cleanupAll();
if (serverInjection != null)
serverInjection.cleanupAll();
if (netLoginInjector != null)
netLoginInjector.cleanupAll();
inputStreamLookup = null;
serverInjection = null;
netLoginInjector = null;
hasClosed = true;
playerInjection.clear();
invoker = null;
}
@Override
public Channel getChannel(Player player) {
throw new UnsupportedOperationException();
}
}
@@ -1,370 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.google.common.base.Objects;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
/**
* Represents an array list that wraps another list, while automatically replacing one element with another.
* <p>
* The replaced elements can be recovered.
*
* @author Kristian
* @param <TKey> - type of the elements we're replacing.
*/
class ReplacedArrayList<TKey> extends ArrayList<TKey> {
/**
* Generated by Eclipse.
*/
private static final long serialVersionUID = 1008492765999744804L;
private BiMap<TKey, TKey> replaceMap = HashBiMap.create();
private List<TKey> underlyingList;
public ReplacedArrayList(List<TKey> underlyingList) {
this.underlyingList = underlyingList;
}
/**
* Invoked when a element inserted is replaced.
* @param inserting - the element inserted.
* @param replacement - the element that it should replace.
*/
protected void onReplacing(TKey inserting, TKey replacement) {
// Default is to do nothing.
}
/**
* Invoked when an element is being inserted.
* <p>
* This should be used to add a "replace" map.
* @param inserting - the element to insert.
*/
protected void onInserting(TKey inserting) {
// Default is again nothing
}
/**
* Invoked when an element is being removed.
* @param removing - the element being removed.
*/
protected void onRemoved(TKey removing) {
// Do nothing
}
@Override
public boolean add(TKey element) {
onInserting(element);
if (replaceMap.containsKey(element)) {
TKey replacement = replaceMap.get(element);
onReplacing(element, replacement);
return delegate().add(replacement);
} else {
return delegate().add(element);
}
}
@Override
public void add(int index, TKey element) {
onInserting(element);
if (replaceMap.containsKey(element)) {
TKey replacement = replaceMap.get(element);
onReplacing(element, replacement);
delegate().add(index, replacement);
} else {
delegate().add(index, element);
}
}
@Override
public boolean addAll(Collection<? extends TKey> collection) {
int oldSize = size();
for (TKey element : collection)
add(element);
return size() != oldSize;
}
@Override
public boolean addAll(int index, Collection<? extends TKey> elements) {
int oldSize = size();
for (TKey element : elements)
add(index++, element);
return size() != oldSize;
}
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object object) {
boolean success = delegate().remove(object);
if (success)
onRemoved((TKey) object);
return success;
}
@Override
public TKey remove(int index) {
TKey removed = delegate().remove(index);
if (removed != null)
onRemoved(removed);
return removed;
}
@Override
public boolean removeAll(Collection<?> collection) {
int oldSize = size();
// Use the remove method
for (Object element : collection)
remove(element);
return size() != oldSize;
}
protected List<TKey> delegate() {
return underlyingList;
}
@Override
public void clear() {
for (TKey element : delegate())
onRemoved(element);
delegate().clear();
}
@Override
public boolean contains(Object o) {
return delegate().contains(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate().containsAll(c);
}
@Override
public TKey get(int index) {
return delegate().get(index);
}
@Override
public int indexOf(Object o) {
return delegate().indexOf(o);
}
@Override
public boolean isEmpty() {
return delegate().isEmpty();
}
@Override
public Iterator<TKey> iterator() {
return delegate().iterator();
}
@Override
public int lastIndexOf(Object o) {
return delegate().lastIndexOf(o);
}
@Override
public ListIterator<TKey> listIterator() {
return delegate().listIterator();
}
@Override
public ListIterator<TKey> listIterator(int index) {
return delegate().listIterator(index);
}
@Override
public boolean retainAll(Collection<?> c) {
int oldSize = size();
for (Iterator<TKey> it = delegate().iterator(); it.hasNext(); ) {
TKey current = it.next();
// Remove elements that are not in the list
if (!c.contains(current)) {
it.remove();
onRemoved(current);
}
}
return size() != oldSize;
}
@Override
public TKey set(int index, TKey element) {
// Make sure to replace the element
if (replaceMap.containsKey(element)) {
TKey replacement = replaceMap.get(element);
onReplacing(element, replacement);
return delegate().set(index, replacement);
} else {
return delegate().set(index, element);
}
}
@Override
public int size() {
return delegate().size();
}
@Override
public List<TKey> subList(int fromIndex, int toIndex) {
return delegate().subList(fromIndex, toIndex);
}
@Override
public Object[] toArray() {
return delegate().toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate().toArray(a);
}
/**
* Add a replace rule.
* <p>
* This automatically replaces every existing element.
* @param target - instance to find.
* @param replacement - instance to replace with.
*/
public synchronized void addMapping(TKey target, TKey replacement) {
addMapping(target, replacement, false);
}
/**
* Retrieve the old value, if it exists.
* @param target - the key.
* @return The value that was replaced, or NULL.
*/
public TKey getMapping(TKey target) {
return replaceMap.get(target);
}
/**
* Add a replace rule.
* <p>
* This automatically replaces every existing element.
* @param target - instance to find.
* @param replacement - instance to replace with.
* @param ignoreExisting - whether or not to ignore the existing elements.
*/
public synchronized void addMapping(TKey target, TKey replacement, boolean ignoreExisting) {
replaceMap.put(target, replacement);
// Replace existing elements
if (!ignoreExisting) {
replaceAll(target, replacement);
}
}
/**
* Revert the given mapping.
* @param target - the instance we replaced.
* @return The old mapped value, or NULL if nothing was replaced.
*/
public synchronized TKey removeMapping(TKey target) {
// Make sure the mapping exist
if (replaceMap.containsKey(target)) {
TKey replacement = replaceMap.get(target);
replaceMap.remove(target);
// Revert existing elements
replaceAll(replacement, target);
return replacement;
}
return null;
}
/**
* Swap the new replaced value with its old value.
* @param target - the instance we replaced.
* @param The old mapped value, or NULL if nothing was swapped.
*/
public synchronized TKey swapMapping(TKey target) {
// Make sure the mapping exist
TKey replacement = removeMapping(target);
// Add the reverse
if (replacement != null) {
replaceMap.put(replacement, target);
}
return replacement;
}
/**
* Replace all instances of the given object.
* @param find - object to find.
* @param replace - object to replace it with.
*/
public synchronized void replaceAll(TKey find, TKey replace) {
for (int i = 0; i < underlyingList.size(); i++) {
if (Objects.equal(underlyingList.get(i), find)) {
onReplacing(find, replace);
underlyingList.set(i, replace);
}
}
}
/**
* Undo all replacements.
*/
public synchronized void revertAll() {
// No need to do anything else
if (replaceMap.size() < 1)
return;
BiMap<TKey, TKey> inverse = replaceMap.inverse();
for (int i = 0; i < underlyingList.size(); i++) {
TKey replaced = underlyingList.get(i);
if (inverse.containsKey(replaced)) {
TKey original = inverse.get(replaced);
onReplacing(replaced, original);
underlyingList.set(i, original);
}
}
replaceMap.clear();
}
@Override
protected void finalize() throws Throwable {
revertAll();
super.finalize();
}
}
@@ -1,64 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.Arrays;
import com.google.common.base.Joiner;
/**
* Represents an error message from a player injector.
*
* @author Kristian
*/
class UnsupportedListener {
private String message;
private int[] packets;
/**
* Create a new error message.
* @param message - the message.
* @param packets - unsupported packets.
*/
public UnsupportedListener(String message, int[] packets) {
super();
this.message = message;
this.packets = packets;
}
/**
* Retrieve the error message.
* @return Error message.
*/
public String getMessage() {
return message;
}
/**
* Retrieve all unsupported packets.
* @return Unsupported packets.
*/
public int[] getPackets() {
return packets;
}
@Override
public String toString() {
return String.format("%s (%s)", message, Joiner.on(", ").join(Arrays.asList(packets)));
}
}
@@ -1,45 +0,0 @@
package com.comphenix.protocol.injector.spigot;
import java.util.Set;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.google.common.collect.Sets;
/**
* Dummy packet injector that simply delegates to its parent Spigot packet injector or receiving filters.
*
* @author Kristian
*/
class DummyPacketInjector extends AbstractPacketInjector {
private SpigotPacketInjector injector;
private PacketTypeSet lastBufferedPackets = new PacketTypeSet();
public DummyPacketInjector(SpigotPacketInjector injector, PacketTypeSet reveivedFilters) {
super(reveivedFilters);
this.injector = injector;
}
@Override
public void inputBuffersChanged(Set<PacketType> set) {
Set<PacketType> removed = Sets.difference(lastBufferedPackets.values(), set);
Set<PacketType> added = Sets.difference(set, lastBufferedPackets.values());
// Update the proxy packet injector
for (PacketType packet : removed) {
injector.getProxyPacketInjector().removePacketHandler(packet);
}
for (PacketType packet : added) {
injector.getProxyPacketInjector().addPacketHandler(packet, null);
}
}
@Override
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
return injector.packetReceived(packet, client, buffered);
}
}
@@ -1,92 +0,0 @@
package com.comphenix.protocol.injector.spigot;
import io.netty.channel.Channel;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
/**
* Dummy player handler that simply delegates to its parent Spigot packet injector.
*
* @author Kristian
*/
class DummyPlayerHandler extends AbstractPlayerHandler {
private SpigotPacketInjector injector;
@Override
public int getProtocolVersion(Player player) {
// Just use the server version
return MinecraftProtocolVersion.getCurrentVersion();
}
public DummyPlayerHandler(SpigotPacketInjector injector, PacketTypeSet sendingFilters) {
super(sendingFilters);
this.injector = injector;
}
@Override
public boolean uninjectPlayer(InetSocketAddress address) {
return true;
}
@Override
public boolean uninjectPlayer(Player player) {
injector.uninjectPlayer(player);
return true;
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
injector.sendServerPacket(receiver, packet, marker, filters);
}
@Override
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
injector.processPacket(player, mcPacket);
}
@Override
public void injectPlayer(Player player, ConflictStrategy strategy) {
// We don't care about strategy
injector.injectPlayer(player);
}
@Override
public boolean hasMainThreadListener(PacketType type) {
return sendingFilters.contains(type);
}
@Override
public void handleDisconnect(Player player) {
// Just ignore
}
@Override
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
// Associate this buffered data
if (buffered != null) {
injector.saveBuffered(packet.getHandle(), buffered);
}
return null;
}
@Override
public void updatePlayer(Player player) {
// Do nothing
}
@Override
public Channel getChannel(Player player) {
throw new UnsupportedOperationException();
}
}
@@ -1,672 +0,0 @@
package com.comphenix.protocol.injector.spigot;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.PlayerLoggedOutException;
import com.comphenix.protocol.injector.packet.LegacyNetworkMarker;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.player.NetworkObjectInjector;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodInfo;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
/**
* Offload all the work to Spigot, if possible.
*
* @author Kristian
*/
public class SpigotPacketInjector implements SpigotPacketListener {
public static final ReportType REPORT_CANNOT_CLEANUP_SPIGOT = new ReportType("Cannot cleanup Spigot listener.");
// Lazily retrieve the spigot listener class
private static volatile Class<?> spigotListenerClass;
private static volatile boolean classChecked;
// Retrieve the entity player from a PlayerConnection
private static volatile Field playerConnectionPlayer;
// Packets that are not to be processed by the filters
private Set<Object> ignoredPackets = Collections.newSetFromMap(new MapMaker().weakKeys().<Object, Boolean>makeMap());
/**
* The amount of ticks to wait before removing all traces of a player.
*/
private static final int CLEANUP_DELAY = 100;
// The listener we will register on Spigot.
// Unfortunately, due to the use of PlayerConnection, INetworkManager and Packet, we're
// unable to reference it directly. But with CGLib, it shouldn't cost us much.
private Object dynamicListener;
// Reference to ProtocolLib
private Plugin plugin;
// Different sending filters
private PacketTypeSet queuedFilters;
private PacketTypeSet reveivedFilters;
// NetworkManager to injector and player
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = Maps.newConcurrentMap();
// Player to injector
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = Maps.newConcurrentMap();
// For handling read buffered packet data
private Map<Object, byte[]> readBufferedPackets = new MapMaker().weakKeys().makeMap();
// Responsible for informing the PL packet listeners
private ListenerInvoker invoker;
private ErrorReporter reporter;
private Server server;
// The proxy packet injector
private PacketInjector proxyPacketInjector;
// Background task
private static final int BACKGROUND_DELAY = 30 * PacketFilterManager.TICKS_PER_SECOND;
private int backgroundId;
/**
* Create a new spigot injector.
* @param reporter - error reporter
* @param invoker - listener invoker
* @param server - server
*/
public SpigotPacketInjector(ErrorReporter reporter, ListenerInvoker invoker, Server server) {
this.reporter = reporter;
this.invoker = invoker;
this.server = server;
this.queuedFilters = new PacketTypeSet();
this.reveivedFilters = new PacketTypeSet();
}
/**
* Retrieve the underlying listener invoker.
* @return The invoker.
*/
public ListenerInvoker getInvoker() {
return invoker;
}
/**
* Set the real proxy packet injector.
* @param proxyPacketInjector - the real injector.
*/
public void setProxyPacketInjector(PacketInjector proxyPacketInjector) {
this.proxyPacketInjector = proxyPacketInjector;
}
/**
* Retrieve the real proxy packet injector.
* @return The real injector.
*/
public PacketInjector getProxyPacketInjector() {
return proxyPacketInjector;
}
/**
* Retrieve the spigot packet listener class.
* @return The listener class.
*/
private static Class<?> getSpigotListenerClass() {
if (!classChecked) {
try {
spigotListenerClass = SpigotPacketInjector.class.getClassLoader().loadClass("org.spigotmc.netty.PacketListener");
} catch (ClassNotFoundException e) {
return null;
} finally {
// We've given it a try now
classChecked = true;
}
}
return spigotListenerClass;
}
/**
* Retrieve the register packet listener method.
* @return The method used to register a packet listener.
*/
private static Method getRegisterMethod() {
Class<?> clazz = getSpigotListenerClass();
if (clazz != null) {
try {
return clazz.getMethod("register", clazz, Plugin.class);
} catch (SecurityException e) {
// If this happens, then ... we're doomed
throw new RuntimeException("Reflection is not allowed.", e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Cannot find register() method in " + clazz, e);
}
}
// Also bad
throw new IllegalStateException("Spigot could not be found!");
}
/**
* Determine if there is a Spigot packet listener.
* @return Spigot packet listener.
*/
public static boolean canUseSpigotListener() {
return getSpigotListenerClass() != null;
}
/**
* Register the Spigot packet injector.
* @param plugin - the parent plugin.
* @return TRUE if we registered the plugin, FALSE otherwise.
*/
public boolean register(Plugin plugin) {
if (hasRegistered())
return false;
// Save the plugin too
this.plugin = plugin;
final Callback[] callbacks = new Callback[3];
final boolean[] found = new boolean[3];
// Packets received from the clients
callbacks[0] = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return SpigotPacketInjector.this.packetReceived(args[0], args[1], args[2]);
}
};
// Packet sent/queued
callbacks[1] = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return SpigotPacketInjector.this.packetQueued(args[0], args[1], args[2]);
}
};
// Don't care for everything else
callbacks[2] = NoOp.INSTANCE;
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
enhancer.setSuperclass(getSpigotListenerClass());
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
// We'll be pretty stringent
if (matchMethod("packetReceived", method)) {
found[0] = true;
return 0;
} else if (matchMethod("packetQueued", method)) {
found[1] = true;
return 1;
} else {
found[2] = true;
return 2;
}
}
});
dynamicListener = enhancer.create();
// Verify methods
if (!found[0])
throw new IllegalStateException("Unable to find a valid packet receiver in Spigot.");
if (!found[1])
throw new IllegalStateException("Unable to find a valid packet queue in Spigot.");
// Lets register it too
try {
getRegisterMethod().invoke(null, dynamicListener, plugin);
} catch (Exception e) {
throw new RuntimeException("Cannot register Spigot packet listener.", e);
}
// Remember to register background task
backgroundId = createBackgroundTask();
// If we succeed
return true;
}
/**
* Create and register a background task.
* @return The background task ID.
*/
private int createBackgroundTask() {
return Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
@Override
public void run() {
cleanupInjectors();
}
}, BACKGROUND_DELAY, BACKGROUND_DELAY);
}
/**
* Ensure that all disconnected injectors are removed from memory.
*/
private void cleanupInjectors() {
for (NetworkObjectInjector injector : networkManagerInjector.values()) {
try {
if (injector.getSocket() != null && injector.getSocket().isClosed()) {
cleanupInjector(injector);
}
} catch (Exception e) {
reporter.reportMinimal(plugin, "cleanupInjectors", e);
// What?
cleanupInjector(injector);
}
}
}
/**
* Remove a given network object injector.
* @param injector - the injector.
*/
private void cleanupInjector(final NetworkObjectInjector injector) {
// Clean up
playerInjector.remove(injector.getPlayer());
playerInjector.remove(injector.getUpdatedPlayer());
networkManagerInjector.remove(injector.getNetworkManager());
}
/**
* Determine if the given method is a valid packet receiver or queued method.
* @param methodName - the expected name of the method.
* @param method - the method we're testing.
* @return TRUE if this is a correct method, FALSE otherwise.
*/
private boolean matchMethod(String methodName, Method method) {
return FuzzyMethodContract.newBuilder().
nameExact(methodName).
parameterCount(3).
parameterSuperOf(MinecraftReflection.getNetHandlerClass(), 1).
parameterSuperOf(MinecraftReflection.getPacketClass(), 2).
returnTypeExact(MinecraftReflection.getPacketClass()).
build().
isMatch(MethodInfo.fromMethod(method), null);
}
/**
* Determine if the Spigot packet listener has been registered.
* @return TRUE if it has, FALSE otherwise.
*/
public boolean hasRegistered() {
return dynamicListener != null;
}
/**
* Retrieve the dummy player injection handler.
* @return Dummy player injection handler.
*/
public PlayerInjectionHandler getPlayerHandler() {
return new DummyPlayerHandler(this, queuedFilters);
}
/**
* Retrieve the dummy packet injection handler.
* @return Dummy packet injection handler.
*/
public PacketInjector getPacketInjector() {
return new DummyPacketInjector(this, reveivedFilters);
}
/**
* Retrieve the currently registered injector for the given player.
* @param player - injected player.
* @param createNew - whether or not to create a new injector if the current is missing.
* @return The injector.
*/
NetworkObjectInjector getInjector(Player player, boolean createNew) {
NetworkObjectInjector injector = playerInjector.get(player);
if (injector == null && createNew) {
// Check for temporary players ..
if ((player instanceof Factory))
throw new IllegalArgumentException("Cannot inject tempoary player " + player);
try {
NetworkObjectInjector created = new NetworkObjectInjector(
filterImpossibleWarnings(reporter), null, invoker, null);
created.initializePlayer(player);
if (created.getNetworkManager() == null)
throw new PlayerLoggedOutException("Player " + player + " has logged out.");
injector = saveInjector(created.getNetworkManager(), created);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create dummy injector.", e);
}
}
return injector;
}
/**
* Retrieve or create a registered injector for the given network manager and connection.
* @param networkManager - a INetworkManager object.
* @param connection - a Connection (PlayerConnection, PendingConnection) object.
* @return The created NetworkObjectInjector with a temporary player.
*/
NetworkObjectInjector getInjector(Object networkManager, Object connection) {
NetworkObjectInjector dummyInjector = networkManagerInjector.get(networkManager);
if (dummyInjector == null) {
// Inject the network manager
try {
NetworkObjectInjector created = new NetworkObjectInjector(
filterImpossibleWarnings(reporter), null, invoker, null);
if (MinecraftReflection.isLoginHandler(connection)) {
created.initialize(connection);
created.setPlayer(created.createTemporaryPlayer(server));
} else if (MinecraftReflection.isServerHandler(connection)) {
// Get the player instead
if (playerConnectionPlayer == null)
playerConnectionPlayer = FuzzyReflection.fromObject(connection).
getFieldByType("player", MinecraftReflection.getEntityPlayerClass());
Object entityPlayer = playerConnectionPlayer.get(connection);
created.initialize(MinecraftReflection.getBukkitEntity(entityPlayer));
} else {
throw new IllegalArgumentException("Unregonized connection in NetworkManager.");
}
dummyInjector = saveInjector(networkManager, created);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create dummy injector.", e);
}
}
return dummyInjector;
}
/**
* Return a delegated error reporter that ignores certain warnings that are irrelevant on Spigot.
* @param reporter - error reporter to delegate.
* @return The filtered error reporter.
*/
private ErrorReporter filterImpossibleWarnings(ErrorReporter reporter) {
return new DelegatedErrorReporter(reporter) {
@Override
protected Report filterReport(Object sender, Report report, boolean detailed) {
// This doesn't matter - ignore it
if (report.getType() == NetworkObjectInjector.REPORT_DETECTED_CUSTOM_SERVER_HANDLER)
return null;
return report;
}
};
}
/**
* Save a given player injector for later.
* @param networkManager - the associated network manager.
* @param created - the created network object creator.
* @return Any other network injector that came before us.
*/
private NetworkObjectInjector saveInjector(Object networkManager, NetworkObjectInjector created) {
// Concurrency - use the same injector!
NetworkObjectInjector result = networkManagerInjector.putIfAbsent(networkManager, created);
if (result == null) {
result = created;
}
// Save the player as well
playerInjector.put(created.getPlayer(), created);
return result;
}
/**
* Save the buffered serialized input packet.
* @param handle - the associated packet.
* @param buffered - the buffere data to save.
*/
public void saveBuffered(Object handle, byte[] buffered) {
readBufferedPackets.put(handle, buffered);
}
@Override
public Object packetReceived(Object networkManager, Object connection, Object packet) {
if (reveivedFilters.contains(packet.getClass())) {
@SuppressWarnings("deprecation")
Integer id = invoker.getPacketID(packet);
// Check for ignored packets
if (ignoredPackets.remove(packet)) {
return packet;
}
Player sender = getInjector(networkManager, connection).getUpdatedPlayer();
PacketType type = PacketType.findLegacy(id, Sender.CLIENT);
PacketContainer container = new PacketContainer(type, packet);
PacketEvent event = packetReceived(container, sender, readBufferedPackets.get(packet));
if (!event.isCancelled())
return event.getPacket().getHandle();
else
return null; // Cancel
}
// Don't change anything
return packet;
}
@Override
public Object packetQueued(Object networkManager, Object connection, Object packet) {
if (queuedFilters.contains(packet.getClass())) {
@SuppressWarnings("deprecation")
Integer id = invoker.getPacketID(packet);
// Check for ignored packets
if (ignoredPackets.remove(packet)) {
return packet;
}
Player reciever = getInjector(networkManager, connection).getUpdatedPlayer();
PacketType type = PacketType.findLegacy(id, Sender.SERVER);
PacketContainer container = new PacketContainer(type, packet);
PacketEvent event = packetQueued(container, reciever);
if (!event.isCancelled())
return event.getPacket().getHandle();
else
return null; // Cancel
}
// Don't change anything
return packet;
}
/**
* Called to inform the event listeners of a queued packet.
* @param packet - the packet that is to be sent.
* @param receiver - the receiver of this packet.
* @return The packet event that was used.
*/
PacketEvent packetQueued(PacketContainer packet, Player receiver) {
PacketEvent event = PacketEvent.fromServer(this, packet, receiver);
invoker.invokePacketSending(event);
return event;
}
/**
* Called to inform the event listeners of a received packet.
* @param packet - the packet that has been receieved.
* @param sender - the client packet.
* @return The packet event that was used.
*/
PacketEvent packetReceived(PacketContainer packet, Player sender, byte[] buffered) {
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender);
invoker.invokePacketRecieving(event);
return event;
}
/**
* Called when a player has logged in properly.
* @param player - the player that has logged in.
*/
void injectPlayer(Player player) {
try {
NetworkObjectInjector dummy = new NetworkObjectInjector(
filterImpossibleWarnings(reporter), player, invoker, null);
dummy.initializePlayer(player);
// Save this player for the network manager
NetworkObjectInjector realInjector = networkManagerInjector.get(dummy.getNetworkManager());
if (realInjector != null) {
// Update all future references
realInjector.setUpdatedPlayer(player);
playerInjector.put(player, realInjector);
} else {
// Ah - in that case, save this injector
saveInjector(dummy.getNetworkManager(), dummy);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot inject " + player);
}
}
/**
* Uninject the given player.
* @param player - the player to uninject.
*/
void uninjectPlayer(Player player) {
final NetworkObjectInjector injector = getInjector(player, false);
if (player != null && injector != null) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
public void run() {
cleanupInjector(injector);
}
}, CLEANUP_DELAY);
}
}
/**
* Invoked when a plugin wants to sent a packet.
* @param receiver - the packet receiver.
* @param packet - the packet to transmit.
* @param marker - the network marker object.
* @param filters - whether or not to invoke the packet listeners.
* @throws InvocationTargetException If anything went wrong.
*/
void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
NetworkObjectInjector networkObject = getInjector(receiver, true);
// If TRUE, process this packet like any other
if (filters)
ignoredPackets.remove(packet.getHandle());
else
ignoredPackets.add(packet.getHandle());
networkObject.sendServerPacket(packet.getHandle(), marker, filters);
}
/**
* Invoked when a plugin wants to simulate receiving a packet.
* @param player - the supposed sender.
* @param mcPacket - the packet to receieve.
* @throws IllegalAccessException Reflection is not permitted.
* @throws InvocationTargetException Minecraft threw an exception.
*/
void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
NetworkObjectInjector networkObject = getInjector(player, true);
// We will always ignore this packet
ignoredPackets.add(mcPacket);
networkObject.processPacket(mcPacket);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void cleanupListener() {
Class<?> listenerClass = getSpigotListenerClass();
// Yeah ... there's no easy way to remove the listener
synchronized (listenerClass) {
try {
Field listenersField = FieldUtils.getField(listenerClass, "listeners", true);
Field bakedField = FieldUtils.getField(listenerClass, "baked", true);
Map<Object, Plugin> listenerMap = (Map<Object, Plugin>) listenersField.get(null);
List<Object> listenerArray = Lists.newArrayList((Object[]) bakedField.get(null));
listenerMap.remove(dynamicListener);
listenerArray.remove(dynamicListener);
// Save the array back
bakedField.set(null, Iterables.toArray(listenerArray, (Class)listenerClass));
// Success
dynamicListener = null;
} catch (Exception e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_CLEANUP_SPIGOT).
callerParam(dynamicListener).error(e));
}
}
}
/**
* Invoked when the server is cleaning up.
*/
public void cleanupAll() {
// Cleanup the Spigot listener
if (dynamicListener != null) {
cleanupListener();
}
if (backgroundId >= 0) {
Bukkit.getScheduler().cancelTask(backgroundId);
backgroundId = -1;
}
// Cleanup network marker
if (proxyPacketInjector != null) {
proxyPacketInjector.cleanupAll();
}
}
}
@@ -1,34 +0,0 @@
package com.comphenix.protocol.injector.spigot;
/**
* Represents a proxy for a Spigot packet listener.
*
* @author Kristian
*/
interface SpigotPacketListener {
/**
* Called when a packet has been received and is about to be handled by the
* current Connection.
* <p>
* The returned packet will be the packet passed on for handling, or in the case of
* null being returned, not handled at all.
*
* @param networkManager - the NetworkManager receiving the packet
* @param connection - the connection which will handle the packet
* @param packet - the received packet
* @return the packet to be handled, or null to cancel
*/
public Object packetReceived(Object networkManager, Object connection, Object packet);
/**
* Called when a packet is queued to be sent.
* <p>
* The returned packet will be the packet sent. In the case of null being returned,
* the packet will not be sent.
* @param networkManager - the NetworkManager which will send the packet
* @param connection - the connection which queued the packet
* @param packet - the queue packet
* @return the packet to be sent, or null if the packet will not be sent.
*/
public Object packetQueued(Object networkManager, Object connection, Object packet);
}
@@ -1,96 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import net.minecraft.server.v1_13_R2.EnumProtocol;
import net.minecraft.server.v1_13_R2.EnumProtocolDirection;
import net.minecraft.server.v1_13_R2.PacketLoginInStart;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
@BeforeClass
public static void initializeReflection() {
BukkitInitialization.initializePackage();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
}
@Test
public void testLoginStart() {
// This packet is critical for handleLoin
assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
}
@Test
public void testDeprecation() {
assertTrue("Packet isn't properly deprecated", PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated());
assertTrue("Deprecated packet isn't properly included",
PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO));
assertFalse("Packet isn't properly deprecated", PacketType.Play.Server.CHAT.isDeprecated());
assertEquals("Deprecated packets aren't equal", PacketType.Status.Server.OUT_SERVER_INFO,
PacketType.Status.Server.SERVER_INFO);
}
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
boolean fail = false;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("h");
field.setAccessible(true);
Map<EnumProtocolDirection, Map<Integer, Class<?>>> map = (Map<EnumProtocolDirection, Map<Integer, Class<?>>>) field.get(protocol);
for (Entry<EnumProtocolDirection, Map<Integer, Class<?>>> entry : map.entrySet()) {
Map<Integer, Class<?>> treeMap = new TreeMap<>(entry.getValue());
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey())
throw new IllegalStateException("Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got " + type.getCurrentId());
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
}
}
}
}
assertTrue("Packet type(s) were incorrect!", !fail);
}
}
@@ -1,22 +0,0 @@
package com.comphenix.protocol.utility;
import static org.junit.Assert.assertNotNull;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
public class MinecraftMethodsTest {
@BeforeClass
public static void initializeReflection() {
BukkitInitialization.initializePackage();
}
@Test
public void testSendPacketMethods() {
assertNotNull(MinecraftMethods.getSendPacketMethod());
assertNotNull(MinecraftMethods.getNetworkManagerHandleMethod());
}
}
+329 -129
View File
@@ -1,129 +1,329 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>4.4.0</version>
<name>ProtocolLib-Parent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spigotVersion>1.13.1-R0.1-SNAPSHOT</spigotVersion>
</properties>
<modules>
<module>modules/API</module>
<module>modules/ProtocolLib</module>
</modules>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
<packaging>pom</packaging>
<inceptionYear>2012</inceptionYear>
<distributionManagement>
<repository>
<id>dmulloy2-releases</id>
<url>http://repo.dmulloy2.net/nexus/repository/releases/</url>
</repository>
<snapshotRepository>
<id>dmulloy2-snapshots</id>
<url>http://repo.dmulloy2.net/nexus/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- Compile with the old version of Netty for backwards compat -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigotVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigotVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean package install</defaultGoal>
<finalName>${project.name}</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<!--
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>src/main/resources/</directory>
<includes>
<include>*.yml</include>
</includes>
</resource>
</resources>
-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<failOnError>false</failOnError>
<encoding>ISO-8859-1</encoding>
<doctitle>ProtocolLib JavaDocs</doctitle>
<windowtitle>ProtocolLib JavaDocs</windowtitle>
<bottom>Copyright &#169; {inceptionYear}&#x2013;{currentYear}
Comphenix and dmulloy2. Licensed under the GNU GPL v2.</bottom>
</configuration>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.6.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>https://github.com/dmulloy2/ProtocolLib</url>
<packaging>jar</packaging>
<inceptionYear>2012</inceptionYear>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>2.0.7</powermock.version>
<spigot.version>1.16.4-R0.1-SNAPSHOT</spigot.version>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.bytebuddy</pattern>
<shadedPattern>com.comphenix.net.bytebuddy</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
<exclude>io.netty:*</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
<useFile>false</useFile>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<failOnError>false</failOnError>
<encoding>ISO-8859-1</encoding>
<doctitle>ProtocolLib JavaDocs</doctitle>
<windowtitle>ProtocolLib JavaDocs</windowtitle>
<bottom>Copyright &#169; {inceptionYear}&#x2013;{currentYear}
Comphenix and dmulloy2. Licensed under the GNU GPL v2.</bottom>
<additionalOptions>
<additionalOption>-Xdoclint:none</additionalOption>
</additionalOptions>
</configuration>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<!--<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>-->
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
<distributionManagement>
<repository>
<id>dmulloy2-releases</id>
<url>https://repo.dmulloy2.net/nexus/repository/releases/</url>
</repository>
<snapshotRepository>
<id>dmulloy2-snapshots</id>
<url>https://repo.dmulloy2.net/nexus/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- Compile with the old version of Netty for backwards compat -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.16</version>
</dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -42,83 +42,63 @@ public interface AsynchronousManager {
* @param listener - the packet listener that will receive these asynchronous events.
* @return An asynchronous handler.
*/
public abstract AsyncListenerHandler registerAsyncHandler(PacketListener listener);
AsyncListenerHandler registerAsyncHandler(PacketListener listener);
/**
* Unregisters and closes the given asynchronous handler.
* @param handler - asynchronous handler.
*/
public abstract void unregisterAsyncHandler(AsyncListenerHandler handler);
void unregisterAsyncHandler(AsyncListenerHandler handler);
/**
* Unregisters and closes the first asynchronous handler associated with the given listener.
* @param listener - asynchronous listener
*/
public abstract void unregisterAsyncHandler(PacketListener listener);
void unregisterAsyncHandler(PacketListener listener);
/**
* Unregisters every asynchronous handler associated with this plugin.
* @param plugin - the original plugin.
*/
public void unregisterAsyncHandlers(Plugin plugin);
/**
* Retrieves a immutable set containing the ID of the sent server packets that will be
* observed by the asynchronous listeners.
* <p>
* Deprecated: Use {@link #getSendingTypes()} instead.
* @return Every filtered server packet.
*/
@Deprecated
public abstract Set<Integer> getSendingFilters();
void unregisterAsyncHandlers(Plugin plugin);
/**
* Retrieves a immutable set containing the types of the sent server packets that will be
* observed by the asynchronous listeners.
* @return Every filtered server packet.
*/
public abstract Set<PacketType> getSendingTypes();
/**
* Retrieves a immutable set containing the ID of the recieved client packets that will be
* <p>
* Deprecated: Use {@link #getReceivingTypes()} instead.
* observed by the asynchronous listeners.
* @return Every filtered client packet.
*/
@Deprecated
public abstract Set<Integer> getReceivingFilters();
Set<PacketType> getSendingTypes();
/**
* Retrieves a immutable set containing the types of the received client packets that will be
* observed by the asynchronous listeners.
* @return Every filtered client packet.
*/
public abstract Set<PacketType> getReceivingTypes();
Set<PacketType> getReceivingTypes();
/**
* Determine if a given synchronous packet has asynchronous listeners.
* @param packet - packet to test.
* @return TRUE if it does, FALSE otherwise.
*/
public abstract boolean hasAsynchronousListeners(PacketEvent packet);
boolean hasAsynchronousListeners(PacketEvent packet);
/**
* Retrieve the default packet stream.
* @return Default packet stream.
*/
public abstract PacketStream getPacketStream();
PacketStream getPacketStream();
/**
* Retrieve the default error reporter.
* @return Default reporter.
*/
public abstract ErrorReporter getErrorReporter();
ErrorReporter getErrorReporter();
/**
* Remove listeners, close threads and transmit every delayed packet.
*/
public abstract void cleanupAll();
void cleanupAll();
/**
* Signal that a packet is ready to be transmitted.
@@ -127,29 +107,29 @@ public interface AsynchronousManager {
* has been called previously.
* @param packet - packet to signal.
*/
public abstract void signalPacketTransmission(PacketEvent packet);
void signalPacketTransmission(PacketEvent packet);
/**
* Register a synchronous listener that handles packets when they time out.
* @param listener - synchronous listener that will handle timed out packets.
*/
public abstract void registerTimeoutHandler(PacketListener listener);
void registerTimeoutHandler(PacketListener listener);
/**
* Unregisters a given timeout listener.
* @param listener - the timeout listener to unregister.
*/
public abstract void unregisterTimeoutHandler(PacketListener listener);
void unregisterTimeoutHandler(PacketListener listener);
/**
* Get a immutable set of every registered timeout handler.
* @return Set of every registered timeout handler.
*/
public abstract Set<PacketListener> getTimeoutHandlers();
Set<PacketListener> getTimeoutHandlers();
/**
* Get an immutable set of every registered asynchronous packet listener.
* @return Set of every asynchronous packet listener.
*/
public abstract Set<PacketListener> getAsyncHandlers();
Set<PacketListener> getAsyncHandlers();
}
@@ -29,13 +29,12 @@ import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.cglib.proxy.Factory;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.utility.ByteBuddyGenerated;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.error.ErrorReporter;
@@ -77,11 +76,6 @@ class CommandPacket extends CommandBase {
*/
public static final int PAGE_LINE_COUNT = 9;
/**
* Number of bytes before we do a hex dump.
*/
private static final int HEX_DUMP_THRESHOLD = 256;
private Plugin plugin;
private Logger logger;
private ProtocolManager manager;
@@ -371,7 +365,7 @@ class CommandPacket extends CommandBase {
logger.info("Initial packet:\n" + original + " -> ");
}
logger.info(shortDescription + ":\n" + getPacketDescription(
logger.info(shortDescription + ":\n" + HexDumper.getPacketDescription(
event.getPacket())
);
} catch (IllegalAccessException e) {
@@ -428,7 +422,7 @@ class CommandPacket extends CommandBase {
*/
private void savePacketState(PacketEvent event) {
try {
originalPackets.put(event, getPacketDescription(event.getPacket()));
originalPackets.put(event, HexDumper.getPacketDescription(event.getPacket()));
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot read packet.", e);
}
@@ -450,68 +444,6 @@ class CommandPacket extends CommandBase {
}
};
}
/**
* Retrieve a detailed string representation of the given packet.
* @param packetContainer - the packet to describe.
* @return The detailed description.
* @throws IllegalAccessException An error occured.
*/
public String getPacketDescription(PacketContainer packetContainer) throws IllegalAccessException {
Object packet = packetContainer.getHandle();
Class<?> clazz = packet.getClass();
// Get the first Minecraft super class
while (clazz != null && clazz != Object.class &&
(!MinecraftReflection.isMinecraftClass(clazz) ||
Factory.class.isAssignableFrom(clazz))) {
clazz = clazz.getSuperclass();
}
return PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass(), PrettyPrinter.RECURSE_DEPTH, new ObjectPrinter() {
@Override
public boolean print(StringBuilder output, Object value) {
// Special case
if (value instanceof byte[]) {
byte[] data = (byte[]) value;
if (data.length > HEX_DUMP_THRESHOLD) {
output.append("[");
HexDumper.defaultDumper().appendTo(output, data);
output.append("]");
return true;
}
} else if (value != null) {
EquivalentConverter<Object> converter = findConverter(value.getClass());
if (converter != null) {
output.append(converter.getSpecific(value));
return true;
}
}
return false;
}
});
}
/**
* Retrieve the closest equivalent converter to a specific class.
* @param clazz - the class.
* @return The closest converter, or NULL if not found,
*/
private EquivalentConverter<Object> findConverter(Class<?> clazz) {
Map<Class<?>, EquivalentConverter<Object>> converters = BukkitConverters.getConvertersForGeneric();
while (clazz != null) {
EquivalentConverter<Object> result = converters.get(clazz);
if (result != null)
return result;
else
clazz = clazz.getSuperclass();
}
return null;
}
public PacketListener updatePacketListener() {
if (listener != null) {
@@ -33,11 +33,11 @@ import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.google.common.base.Charsets;
import org.apache.commons.io.HexDump;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.libs.org.apache.commons.io.HexDump;
import org.bukkit.plugin.Plugin;
/**
@@ -5,29 +5,22 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import java.util.*;
import java.util.function.Consumer;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
/**
* Represents the type of a packet in a specific protocol.
@@ -58,7 +51,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SetProtocol");
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, "SetProtocol", "C00Handshake");
private final static Client INSTANCE = new Client();
@@ -109,92 +102,98 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SpawnEntity");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "SpawnEntityExperienceOrb");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "SpawnEntityWeather");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SpawnEntityLiving");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "SpawnEntityPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x05, "NamedEntitySpawn");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x06, "Animation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x07, "Statistic");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x08, "BlockBreakAnimation");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x09, "TileEntityData");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x0A, "BlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x0B, "BlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0x0C, "Boss");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0D, "ServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0F, "Chat");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0F, 0x10, "MultiBlockChange");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, 0x0E, "TabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, -0x01, "Commands");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x12, 0x11, "Transaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x12, "CloseWindow");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x14, 0x13, "OpenWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x15, 0x14, "WindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x16, 0x15, "WindowData");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x16, "SetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x18, 0x17, "SetCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, 0x18, "CustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x19, "CustomSoundEffect");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "KickDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, 0x1B, "EntityStatus");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x1D, -0x01, "NBTQuery");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1E, 0x1C, "Explosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1F, 0x1D, "UnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x20, 0x1E, "GameStateChange");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x21, 0x1F, "KeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x22, 0x20, "MapChunk");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x23, 0x21, "WorldEvent");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x24, 0x22, "WorldParticles");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x25, 0x23, "Login");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x26, 0x24, "Map");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x27, 0x25, "Entity");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x28, 0x26, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, 0x27, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, 0x28, "EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, 0x29, "VehicleMove");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2C, 0x2A, "OpenSignEditor");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2D, 0x2B, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2E, 0x2C, "Abilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2F, 0x2D, "CombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x30, 0x2E, "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x31, -0x01, "LookAt");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x32, 0x2F, "Position");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, 0x30, "Bed");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x34, 0x31, "Recipes");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x35, 0x32, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x36, 0x33, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x37, 0x34, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x38, 0x35, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x39, 0x36, "EntityHeadRotation");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3A, 0x37, "SelectAdvancementTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3B, 0x38, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3C, 0x39, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3D, 0x3A, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3E, 0x3B, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x40, 0x3D, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x42, 0x3F, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x43, 0x40, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x44, 0x41, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x45, 0x42, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x46, 0x43, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x47, 0x44, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, 0x45, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x49, 0x46, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4A, 0x47, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4B, 0x48, "Title");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x4C, -0x01, "StopSound");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4D, 0x49, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x4E, 0x4A, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x4F, 0x4B, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x50, 0x4C, "EntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x51, 0x4D, "Advancements");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x52, 0x4E, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x53, 0x4F, "EntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x54, 0x54, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x55, 0x55, "Tags");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, "SpawnEntity", "SPacketSpawnObject");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityLiving", "SPacketSpawnMob");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x03, "SpawnEntityPainting", "SPacketSpawnPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x04, "NamedEntitySpawn", "SPacketSpawnPlayer");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x05, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x06, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_BREAK = new PacketType(PROTOCOL, SENDER, 0x07, "BlockBreak");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, "Chat", "SPacketChat");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x11, "Transaction", "SPacketConfirmTransaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x13, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x14, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x15, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x16, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, "CustomPayload", "SPacketCustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x18, "CustomSoundEffect", "SPacketCustomSound");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x19, "KickDisconnect", "SPacketDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1A, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1B, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1C, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1D, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1E, "OpenWindowHorse");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x23, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x24, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x25, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x26, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x27, "Entity$RelEntityMove", "SPacketEntity$S15PacketEntityRelMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$RelEntityMoveLook", "SPacketEntity$S17PacketEntityLookMove");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$EntityLook", "SPacketEntity$S16PacketEntityLook");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x2A, "Entity", "SPacketEntity");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2E, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2F, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x30, "Abilities", "SPacketPlayerAbilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x31, "CombatEvent", "SPacketCombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerInfo", "SPacketPlayerListItem");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x33, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x34, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x35, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x36, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x37, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x38, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x39, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3A, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x3B, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3C, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3D, "WorldBorder", "SPacketWorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3E, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3F, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x40, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x41, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x42, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x43, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x44, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x45, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x46, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x47, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x48, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x49, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4A, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x4B, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x4C, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x4D, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4E, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4F, "Title", "SPacketTitle");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x50, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x51, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x52, "StopSound");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x53, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x54, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x55, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x56, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x57, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x58, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x59, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x5A, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x5B, "Tags");
// ---- Removed in 1.9
@@ -202,19 +201,19 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk");
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, "MapChunkBulk");
/**
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, "SetCompression");
/**
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT");
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, "UpdateEntityNBT");
// ----- Renamed packets
@@ -248,9 +247,29 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Replaced by {@link #TILE_ENTITY_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, 252, "UpdateSign") :
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, "UpdateSign") :
TILE_ENTITY_DATA.clone();
// ---- Removed in 1.14
/**
* @deprecated Removed in 1.14
*/
@Deprecated
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, "Bed", "SPacketUseBed");
/**
* @deprecated Renamed to {@link #BED}
*/
@Deprecated
public static final PacketType USE_BED = BED.clone();
/**
* @deprecated Removed in 1.16
*/
@Deprecated
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityWeather", "SPacketSpawnGlobalEntity");
private final static Server INSTANCE = new Server();
// Prevent accidental construction
@@ -271,49 +290,54 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "TeleportAccept");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, -0x01, "TileNBTQuery");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "Settings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x05, 0x01, "TabComplete");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x06, 0x05, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, 0x06, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, 0x07, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, 0x08, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, 0x09, "CustomPayload");
public static final PacketType BOOK_EDIT = new PacketType(PROTOCOL, SENDER, 0x0B, -0x01, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0C, -0x01, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0A, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0B, "KeepAlive");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x0C, "Flying");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x10, 0x0D, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x11, 0x0E, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x12, 0x0F, "Look");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x13, 0x10, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x14, 0x11, "BoatMove");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x15, -0x01, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x16, 0x12, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x17, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x18, 0x14, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x19, 0x15, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1A, 0x16, "SteerVehicle");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1B, 0x17, "RecipeDisplayed");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x1C, -0x01, "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x1D, 0x18, "ResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x1E, 0x19, "Advancements");
public static final PacketType TRADE_SELECT = new PacketType(PROTOCOL, SENDER, 0x1F, -0x01, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x20, -0x01, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x21, 0x1A, "HeldItemSlot");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x22, 0x22, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x23, 0x23, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x24, 0x1B, "SetCreativeSlot");
public static final PacketType STRUCTURE_BLOCK = new PacketType(PROTOCOL, SENDER, 0x25, 0x25, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x26, 0x1C, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x27, 0x1D, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x28, 0x1E, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x29, 0x1F, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2A, 0x20, "BlockPlace");
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "TeleportAccept", "CPacketConfirmTeleport");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "TileNBTQuery");
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "DifficultyChange");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x03, "Chat", "CPacketChatMessage");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x05, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x06, "TabComplete", "CPacketTabComplete");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x07, "Transaction", "CPacketConfirmTransaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x08, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x09, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0A, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0B, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0C, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0D, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0E, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x0F, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x10, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x11, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x12, "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x15, "Flying", "CPacketPlayer");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x16, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x17, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x18, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x19, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1A, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1B, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1C, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1D, "SteerVehicle", "CPacketInput");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x1E, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1F, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x20, "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x21, "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x22, "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x23, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x24, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x25, "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x26, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x27, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x28, "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x29, "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x2A, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x2B, "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2C, "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x2D, "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x2E, "UseItem", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2F, "BlockPlace", "CPacketPlayerTryUseItem");
private final static Client INSTANCE = new Client();
@@ -348,8 +372,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
private final static Sender SENDER = Sender.SERVER;
@ForceAsync
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "ServerInfo");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "Pong");
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "ServerInfo", "SPacketServerInfo");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "Pong", "SPacketPong");
/**
* @deprecated Renamed to {@link #SERVER_INFO}
@@ -378,8 +402,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "Ping");
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketServerQuery");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "Ping", "CPacketPing");
private final static Client INSTANCE = new Client();
@@ -413,11 +437,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Disconnect");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Success");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SetCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, -0x01, "CustomPayload");
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, "Disconnect", "SPacketDisconnect");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "SPacketEncryptionRequest");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "Success", "SPacketLoginSuccess");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "SetCompression", "SPacketEnableCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomPayload", "SPacketCustomPayload");
private final static Server INSTANCE = new Server();
@@ -439,86 +463,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, -0x01, "CustomPayload");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
public static Sender getSender() {
return SENDER;
}
public static Client getInstance() {
return INSTANCE;
}
}
public static Protocol getProtocol() {
return PROTOCOL;
}
}
/**
* Contains every packet Minecraft 1.6.4 packet removed in Minecraft 1.7.2.
* @author Kristian
*/
public static class Legacy {
private static final Protocol PROTOCOL = Protocol.LEGACY;
/**
* Outgoing packets.
* @author Kristian
*/
// Missing server packets: [10, 11, 12, 21, 107, 252]
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType PLAYER_FLYING = PacketType.newLegacy(SENDER, 10);
public static final PacketType PLAYER_POSITION = PacketType.newLegacy(SENDER, 11);
public static final PacketType PLAYER_POSITON_LOOK = PacketType.newLegacy(SENDER, 12);
/**
* Removed in Minecraft 1.4.6.
*/
public static final PacketType PICKUP_SPAWN = PacketType.newLegacy(SENDER, 21);
/**
* Removed in Minecraft 1.7.2
*/
public static final PacketType SET_CREATIVE_SLOT = PacketType.newLegacy(SENDER, 107);
/**
* Removed in Minecraft 1.7.2
*/
public static final PacketType KEY_RESPONSE = PacketType.newLegacy(SENDER, 252);
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() {
super();
}
public static Sender getSender() {
return SENDER;
}
public static Server getInstance() {
return INSTANCE;
}
}
/**
* Incoming packets.
* @author Kristian
*/
// Missing client packets: [1, 9, 255]
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType LOGIN = PacketType.newLegacy(SENDER, 1);
public static final PacketType RESPAWN = PacketType.newLegacy(SENDER, 9);
public static final PacketType DISCONNECT = PacketType.newLegacy(SENDER, 255);
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketLoginStart");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "CPacketEncryptionResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload", "CPacketCustomPayload");
private final static Client INSTANCE = new Client();
@@ -575,6 +522,10 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public String getPacketName() {
return WordUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
}
public String getMcpPacketName() {
return name().toLowerCase(Locale.ENGLISH);
}
}
/**
@@ -604,6 +555,10 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public String getPacketName() {
return this == CLIENT ? "In" : "Out";
}
public String getMcpPacketName() {
return name().toLowerCase(Locale.ENGLISH);
}
}
/**
@@ -619,20 +574,21 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
/**
* Protocol version of all the current IDs.
*/
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.EXPLORATION_UPDATE;
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.NETHER_UPDATE;
private final Protocol protocol;
private final Sender sender;
private final int currentId;
private final int legacyId;
private final MinecraftVersion version;
private final String[] classNames;
String[] names;
private String name;
private boolean deprecated;
private boolean forceAsync;
private boolean dynamic;
private int hashCode;
/**
* Retrieve the current packet/legacy lookup.
@@ -648,9 +604,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
addPacketTypes(Status.Client.getInstance()).
addPacketTypes(Status.Server.getInstance()).
addPacketTypes(Login.Client.getInstance()).
addPacketTypes(Login.Server.getInstance()).
addPacketTypes(Legacy.Client.getInstance()).
addPacketTypes(Legacy.Server.getInstance());
addPacketTypes(Login.Server.getInstance());
}
return LOOKUP;
}
@@ -670,11 +624,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
sources.add(Login.Client.getInstance());
sources.add(Login.Server.getInstance());
// Add the missing types in earlier versions
if (!MinecraftReflection.isUsingNetty()) {
sources.add(Legacy.Client.getInstance());
sources.add(Legacy.Server.getInstance());
}
return Iterables.concat(sources);
}
@@ -750,7 +699,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
public static PacketType findCurrent(Protocol protocol, Sender sender, String name) {
name = format(protocol, sender, name);
name = formatClassName(protocol, sender, name);
PacketType type = getLookup().getFromCurrent(protocol, sender, name);
if (type != null) {
@@ -761,11 +710,29 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
}
private static String format(Protocol protocol, Sender sender, String name) {
if (name.contains("Packet"))
private static String formatClassName(Protocol protocol, Sender sender, String name) {
String base = MinecraftReflection.getMinecraftPackage() + ".Packet";
if (name.startsWith(base)) {
return name;
}
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), WordUtils.capitalize(name));
if (name.contains("$")) {
String[] split = name.split("\\$");
String parent = split[0];
String child = split[1];
return base + protocol.getPacketName() + sender.getPacketName() + WordUtils.capitalize(parent)
+ "$Packet" + protocol.getPacketName() + sender.getPacketName() + WordUtils.capitalize(child);
}
return base + protocol.getPacketName() + sender.getPacketName() + WordUtils.capitalize(name);
}
private static boolean isMcpPacketName(String packetName) {
return packetName.startsWith("C00") || packetName.startsWith("CPacket") || packetName.startsWith("SPacket");
}
private static String formatMcpClassName(Protocol protocol, Sender sender, String name) {
return "net.minecraft.network." + protocol.getMcpPacketName() + "." + sender.getMcpPacketName() + "." + name;
}
/**
@@ -779,31 +746,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return getLookup().getFromCurrent(protocol, sender, packetId) != null;
}
/**
* Retrieve a packet type from a legacy ID.
* <p>
* If no associated packet type could be found, a new will be registered under LEGACY.
* @param id - the legacy ID.
* @param sender - the sender of the packet, or NULL if unknown.
* @return The packet type.
* @throws IllegalArgumentException If the sender is NULL and the packet doesn't exist.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType fromLegacy(int id, Sender sender) {
PacketType type = getLookup().getFromLegacy(id, sender);
if (type == null) {
if (sender == null)
throw new IllegalArgumentException("Cannot find legacy packet " + id);
type = newLegacy(sender, id);
// As below
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
}
return type;
}
/**
* Retrieve a packet type from a protocol, sender and packet ID, for pre-1.8.
* <p>
@@ -818,7 +760,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
if (type == null) {
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, packetClass.getName());
type = new PacketType(protocol, sender, packetId, PROTOCOL_VERSION, packetClass.getName());
type.dynamic = true;
// Many may be scheduled, but only the first will be executed
@@ -828,6 +770,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return type;
}
static Consumer<String> onDynamicCreate = x -> {};
/**
* Retrieve a packet type from a protocol, sender, ID, and class for 1.8+
* <p>
@@ -843,15 +787,16 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
Map<String, PacketType> map = lookup.getMap(protocol, sender);
// Check the map first
String clazz = packetClass.getSimpleName();
PacketType type = find(map, clazz);
String className = packetClass.getName();
PacketType type = find(map, className);
if (type == null) {
// Guess we don't support this packet :/
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, clazz);
type = new PacketType(protocol, sender, packetId, PROTOCOL_VERSION, className);
type.dynamic = true;
// Many may be scheduled, but only the first will be executed
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
onDynamicCreate.accept(className);
}
return type;
@@ -918,34 +863,29 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* Note that the registration will be performed on the main thread.
* @param type - the type to register.
* @param name - the name of the packet.
* @return A future telling us if our instance was registered.
*/
public static Future<Boolean> scheduleRegister(final PacketType type, final String name) {
Callable<Boolean> callable = new Callable<Boolean>() {
public static void scheduleRegister(final PacketType type, final String name) {
BukkitRunnable runnable = new BukkitRunnable() {
@Override
public Boolean call() throws Exception {
public void run() {
PacketTypeEnum objEnum;
// A bit ugly, but performance is critical
objEnum = getObjectEnum(type);
if (objEnum.registerMember(type, name)) {
getLookup().addPacketTypes(Arrays.asList(type));
return true;
getLookup().addPacketTypes(Collections.singletonList(type));
}
return false;
}
};
// Execute in the main thread if possible
if (Bukkit.getServer() == null || Bukkit.isPrimaryThread()) {
try {
return Futures.immediateFuture(callable.call());
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
runnable.run();
} catch (Exception ignored) { }
} else {
runnable.runTaskLater(ProtocolLibrary.getPlugin(), 0);
}
return ProtocolLibrary.getExecutorSync().submit(callable);
}
/**
@@ -963,8 +903,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return type.isClient() ? Status.Client.getInstance() : Status.Server.getInstance();
case LOGIN:
return type.isClient() ? Login.Client.getInstance() : Login.Server.getInstance();
case LEGACY:
return type.isClient() ? Legacy.Client.getInstance() : Legacy.Server.getInstance();
default:
throw new IllegalStateException("Unexpected protocol: " + type.getProtocol());
}
@@ -975,10 +913,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @param protocol - the current protocol.
* @param sender - client or server.
* @param currentId - the current packet ID, or
* @param legacyId - the legacy packet ID.
*/
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, String... names) {
this(protocol, sender, currentId, legacyId, PROTOCOL_VERSION, names);
public PacketType(Protocol protocol, Sender sender, int currentId, String... names) {
this(protocol, sender, currentId, PROTOCOL_VERSION, names);
}
/**
@@ -986,30 +923,24 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @param protocol - the current protocol.
* @param sender - client or server.
* @param currentId - the current packet ID.
* @param legacyId - the legacy packet ID.
* @param version - the version of the current ID.
*/
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version, String... names) {
public PacketType(Protocol protocol, Sender sender, int currentId, MinecraftVersion version, String... names) {
this.protocol = Preconditions.checkNotNull(protocol, "protocol cannot be NULL");
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
this.currentId = currentId;
this.legacyId = legacyId;
this.version = version;
this.classNames = new String[names.length];
for (int i = 0; i < classNames.length; i++) {
classNames[i] = format(protocol, sender, names[i]);
if (isMcpPacketName(names[i])) { // Minecraft MCP packets
classNames[i] = formatMcpClassName(protocol, sender, names[i]);
} else {
classNames[i] = formatClassName(protocol, sender, names[i]);
}
}
}
/**
* Construct a legacy packet type.
* @param sender - client or server.
* @param legacyId - the legacy packet ID.
* @return Legacy packet type
*/
public static PacketType newLegacy(Sender sender, int legacyId) {
return new PacketType(Protocol.LEGACY, sender, PacketType.UNKNOWN_PACKET, legacyId, MinecraftVersion.WORLD_UPDATE);
this.names = names;
}
/**
@@ -1059,7 +990,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* <p>
* It is unknown if the packet was removed at any point.
* @return The current ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated Don't rely on packet IDs, they change every version
*/
@Deprecated
public int getCurrentId() {
return currentId;
}
@@ -1131,18 +1064,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return version;
}
/**
* Retrieve the legacy (1.6.4 or below) protocol ID of the packet type.
* <p>
* This ID is globally unique.
* @return The legacy ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public int getLegacyId() {
return legacyId;
}
/**
* Whether or not this packet was dynamically created (i.e. we don't have it registered)
* @return True if dnyamic, false if not.
@@ -1153,7 +1074,14 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Override
public int hashCode() {
return Objects.hashCode(protocol, sender, currentId);
int hash = hashCode;
if (hash == 0) {
hash = protocol.hashCode();
hash = 31 * hash + sender.hashCode();
hash = 31 * hash + Integer.hashCode(currentId);
hashCode = hash;
}
return hash;
}
@Override
@@ -105,16 +105,6 @@ class PacketTypeLookup {
Preconditions.checkNotNull(types, "types cannot be NULL");
for (PacketType type : types) {
int legacy = type.getLegacyId();
// Skip unknown legacy packets
if (legacy != PacketType.UNKNOWN_PACKET) {
if (type.isServer())
serverLookup.put(type.getLegacyId(), type);
if (type.isClient())
clientLookup.put(type.getLegacyId(), type);
legacyLookup.put(type.getLegacyId(), type);
}
// Skip unknown current packets
if (type.getCurrentId() != PacketType.UNKNOWN_PACKET) {
idLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
@@ -26,21 +26,8 @@ import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import com.comphenix.executors.BukkitExecutors;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.DetailedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.error.*;
import com.comphenix.protocol.injector.DelayedSingleTask;
import com.comphenix.protocol.injector.InternalManager;
import com.comphenix.protocol.injector.PacketFilterManager;
@@ -50,12 +37,18 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
/**
* The main entry point for ProtocolLib.
@@ -112,10 +105,6 @@ public class ProtocolLib extends JavaPlugin {
// Metrics and statistics
private Statistics statistics;
// Executors
private static ListeningScheduledExecutorService executorAsync;
private static ListeningScheduledExecutorService executorSync;
// Structure compiler
private BackgroundCompiler backgroundCompiler;
@@ -155,11 +144,7 @@ public class ProtocolLib extends JavaPlugin {
ProtocolLogger.init(this);
// Initialize enhancer factory
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
// Initialize executors
executorAsync = BukkitExecutors.newAsynchronous(this);
executorSync = BukkitExecutors.newSynchronous(this);
ByteBuddyFactory.getInstance().setClassLoader(getClassLoader());
// Add global parameters
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
@@ -213,7 +198,7 @@ public class ProtocolLib extends JavaPlugin {
.build();
// Initialize the API
ProtocolLibrary.init(this, config, protocolManager, reporter, executorAsync, executorSync);
ProtocolLibrary.init(this, config, protocolManager, reporter);
// Setup error reporter
detailedReporter.addGlobalParameter("manager", protocolManager);
@@ -19,12 +19,11 @@ package com.comphenix.protocol;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.Validate;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import org.apache.commons.lang.Validate;
import org.bukkit.plugin.Plugin;
/**
* The main entry point for ProtocolLib.
@@ -39,12 +38,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.13.1";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.16.5";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.13.1) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.16.5) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2021-01-15";
/**
* Plugins that are currently incompatible with ProtocolLib.
@@ -56,21 +55,15 @@ public class ProtocolLibrary {
private static ProtocolManager manager;
private static ErrorReporter reporter = new BasicErrorReporter();
private static ListeningScheduledExecutorService executorAsync;
private static ListeningScheduledExecutorService executorSync;
private static boolean updatesDisabled;
private static boolean initialized;
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager, ErrorReporter reporter,
ListeningScheduledExecutorService executorAsync, ListeningScheduledExecutorService executorSync) {
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager, ErrorReporter reporter) {
Validate.isTrue(!initialized, "ProtocolLib has already been initialized.");
ProtocolLibrary.plugin = plugin;
ProtocolLibrary.config = config;
ProtocolLibrary.manager = manager;
ProtocolLibrary.reporter = reporter;
ProtocolLibrary.executorAsync = executorAsync;
ProtocolLibrary.executorSync = executorSync;
initialized = true;
}
@@ -106,26 +99,6 @@ public class ProtocolLibrary {
return reporter;
}
/**
* Retrieve an executor service for performing asynchronous tasks on the behalf of ProtocolLib.
* <p>
* Note that this service is NULL if ProtocolLib has not been initialized yet.
* @return The executor service, or NULL.
*/
public static ListeningScheduledExecutorService getExecutorAsync() {
return executorAsync;
}
/**
* Retrieve an executor service for performing synchronous tasks (main thread) on the behalf of ProtocolLib.
* <p>
* Note that this service is NULL if ProtocolLib has not been initialized yet.
* @return The executor service, or NULL.
*/
public static ListeningScheduledExecutorService getExecutorSync() {
return executorSync;
}
/**
* Disables the ProtocolLib update checker.
*/
@@ -18,6 +18,7 @@ package com.comphenix.protocol;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
@@ -25,18 +26,20 @@ import org.bukkit.plugin.Plugin;
* @author dmulloy2
*/
public class ProtocolLogger {
private static Plugin plugin;
private static boolean debugEnabled = false;
private static Logger logger = Logger.getLogger("Minecraft");
protected static void init(Plugin plugin) {
ProtocolLogger.plugin = plugin;
}
/**
* Don't call this method from any plugin. Currently only public to test if it fixes a weird error.
* See GH-740
* @param plugin ProtocolLib
*/
public static void init(ProtocolLib plugin) {
logger = plugin.getLogger();
public static boolean isDebugEnabled() {
try {
return plugin.getConfig().getBoolean("global.debug", false);
} catch (Throwable ex) { // Enable in testing environments
return true;
}
debugEnabled = plugin.getConfig().getBoolean("global.debug", false);
} catch (Throwable ignored) { }
}
/**
@@ -46,7 +49,7 @@ public class ProtocolLogger {
* @param args Arguments to format in
*/
public static void log(Level level, String message, Object... args) {
plugin.getLogger().log(level, MessageFormat.format(message, args));
logger.log(level, MessageFormat.format(message, args));
}
/**
@@ -65,18 +68,18 @@ public class ProtocolLogger {
* @param ex Exception to log
*/
public static void log(Level level, String message, Throwable ex) {
plugin.getLogger().log(level, message, ex);
logger.log(level, message, ex);
}
public static void debug(String message, Object... args) {
if (isDebugEnabled()) {
if (debugEnabled) {
log("[Debug] " + message, args);
}
}
public static void debug(String message, Throwable ex) {
if (isDebugEnabled()) {
plugin.getLogger().log(Level.WARNING, "[Debug] " + message, ex);
if (debugEnabled) {
logger.log(Level.WARNING, "[Debug] " + message, ex);
}
}
}
@@ -49,7 +49,7 @@ public interface ProtocolManager extends PacketStream {
* @param player - the player.
* @return The associated protocol version, or {@link Integer#MIN_VALUE} if unknown.
*/
public int getProtocolVersion(Player player);
int getProtocolVersion(Player player);
/**
* Send a packet to the given player.
@@ -63,7 +63,7 @@ public interface ProtocolManager extends PacketStream {
* @throws InvocationTargetException - if an error occurred when sending the packet.
*/
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
throws InvocationTargetException;
/**
@@ -79,7 +79,7 @@ public interface ProtocolManager extends PacketStream {
* @throws IllegalAccessException If the underlying method caused an error.
*/
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
throws IllegalAccessException, InvocationTargetException;
/**
@@ -87,7 +87,7 @@ public interface ProtocolManager extends PacketStream {
* @param packet - the packet to broadcast.
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
*/
public void broadcastServerPacket(PacketContainer packet);
void broadcastServerPacket(PacketContainer packet);
/**
* Broadcast a packet to every player that is receiving information about a given entity.
@@ -99,7 +99,7 @@ public interface ProtocolManager extends PacketStream {
* @param includeTracker - whether or not to also transmit the packet to the entity, if it is a tracker.
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
*/
public void broadcastServerPacket(PacketContainer packet, Entity entity, boolean includeTracker);
void broadcastServerPacket(PacketContainer packet, Entity entity, boolean includeTracker);
/**
* Broadcast a packet to every player within the given maximum observer distance.
@@ -107,13 +107,13 @@ public interface ProtocolManager extends PacketStream {
* @param origin - the origin to consider when calculating the distance to each observer.
* @param maxObserverDistance - the maximum distance to the origin.
*/
public void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
/**
* Retrieves a list of every registered packet listener.
* @return Every registered packet listener.
*/
public ImmutableSet<PacketListener> getPacketListeners();
ImmutableSet<PacketListener> getPacketListeners();
/**
* Adds a packet listener.
@@ -123,7 +123,7 @@ public interface ProtocolManager extends PacketStream {
* can register it again.
* @param listener - new packet listener.
*/
public void addPacketListener(PacketListener listener);
void addPacketListener(PacketListener listener);
/**
* Removes a given packet listener.
@@ -131,47 +131,20 @@ public interface ProtocolManager extends PacketStream {
* Attempting to remove a listener that doesn't exist has no effect.
* @param listener - the packet listener to remove.
*/
public void removePacketListener(PacketListener listener);
void removePacketListener(PacketListener listener);
/**
* Removes every listener associated with the given plugin.
* @param plugin - the plugin to unload.
*/
public void removePacketListeners(Plugin plugin);
void removePacketListeners(Plugin plugin);
/**
* Constructs a new encapsulated Minecraft packet with the given ID.
* <p>
* Deprecated: Use {@link #createPacket(PacketType)} instead.
* @param id - packet ID.
* @return New encapsulated Minecraft packet.
*/
@Deprecated
public PacketContainer createPacket(int id);
/**
* Constructs a new encapsulated Minecraft packet with the given ID.
* @param type - packet type.
* @return New encapsulated Minecraft packet.
*/
public PacketContainer createPacket(PacketType type);
/**
* Constructs a new encapsulated Minecraft packet with the given ID.
* <p>
* If set to true, the <i>forceDefaults</i> option will force the system to automatically
* give non-primitive fields in the packet sensible default values. For instance, certain
* packets - like Packet60Explosion - require a List or Set to be non-null. If the
* forceDefaults option is true, the List or Set will be automatically created.
* <p>
* Deprecated: Use {@link #createPacket(PacketType, boolean)} instead.
*
* @param id - packet ID.
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
* @return New encapsulated Minecraft packet.
*/
@Deprecated
public PacketContainer createPacket(int id, boolean forceDefaults);
PacketContainer createPacket(PacketType type);
/**
* Constructs a new encapsulated Minecraft packet with the given ID.
@@ -185,18 +158,7 @@ public interface ProtocolManager extends PacketStream {
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
* @return New encapsulated Minecraft packet.
*/
public PacketContainer createPacket(PacketType type, boolean forceDefaults);
/**
* Construct a packet using the special builtin Minecraft constructors.
* <p>
* Deprecated: Use {@link #createPacketConstructor(PacketType, Object...)} instead.
* @param id - the packet ID.
* @param arguments - arguments that will be passed to the constructor.
* @return The packet constructor.
*/
@Deprecated
public PacketConstructor createPacketConstructor(int id, Object... arguments);
PacketContainer createPacket(PacketType type, boolean forceDefaults);
/**
* Construct a packet using the special builtin Minecraft constructors.
@@ -204,7 +166,7 @@ public interface ProtocolManager extends PacketStream {
* @param arguments - arguments that will be passed to the constructor.
* @return The packet constructor.
*/
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments);
PacketConstructor createPacketConstructor(PacketType type, Object... arguments);
/**
* Completely resend an entity to a list of clients.
@@ -214,7 +176,7 @@ public interface ProtocolManager extends PacketStream {
* @param entity - entity to refresh.
* @param observers - the clients to update.
*/
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
/**
* Retrieve the associated entity.
@@ -223,7 +185,7 @@ public interface ProtocolManager extends PacketStream {
* @return The associated entity.
* @throws FieldAccessException Reflection failed.
*/
public Entity getEntityFromID(World container, int id) throws FieldAccessException;
Entity getEntityFromID(World container, int id) throws FieldAccessException;
/**
* Retrieve every client that is receiving information about a given entity.
@@ -231,55 +193,37 @@ public interface ProtocolManager extends PacketStream {
* @return Every client/player that is tracking the given entity.
* @throws FieldAccessException If reflection failed.
*/
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
/**
* Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners.
* <p>
* Deprecated: Use {@link #getSendingFilterTypes()} instead.
* @return Every filtered server packet.
*/
@Deprecated
public Set<Integer> getSendingFilters();
List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
/**
* Retrieves a immutable set containing the type of the sent server packets that will be observed by listeners.
* @return Every filtered server packet.
*/
public Set<PacketType> getSendingFilterTypes();
/**
* Retrieves a immutable set containing the ID of the received client packets that will be observed by listeners.
* <p>
* Deprecated: Use {@link #getReceivingFilterTypes()} instead.
* @return Every filtered client packet.
*/
@Deprecated
public Set<Integer> getReceivingFilters();
Set<PacketType> getSendingFilterTypes();
/**
* Retrieves a immutable set containing the type of the received client packets that will be observed by listeners.
* @return Every filtered client packet.
*/
public Set<PacketType> getReceivingFilterTypes();
Set<PacketType> getReceivingFilterTypes();
/**
* Retrieve the current Minecraft version.
* @return The current version.
*/
public MinecraftVersion getMinecraftVersion();
MinecraftVersion getMinecraftVersion();
/**
* Determines whether or not this protocol manager has been disabled.
* @return TRUE if it has, FALSE otherwise.
*/
public boolean isClosed();
boolean isClosed();
/**
* Retrieve the current asynchronous packet manager.
* @return Asynchronous packet manager.
*/
public AsynchronousManager getAsynchronousManager();
AsynchronousManager getAsynchronousManager();
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
}
@@ -23,10 +23,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.PacketType;
@@ -37,12 +33,15 @@ import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PrioritizedListener;
import com.comphenix.protocol.injector.SortedPacketListenerList;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
/**
* Represents a filter manager for asynchronous packets.
* <p>
@@ -88,7 +87,7 @@ public class AsyncFilterManager implements AsynchronousManager {
// Initialize timeout listeners
this.serverTimeoutListeners = new SortedPacketListenerList();
this.clientTimeoutListeners = new SortedPacketListenerList();
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<>());
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
@@ -318,31 +317,21 @@ public class AsyncFilterManager implements AsynchronousManager {
// The player is only be null when they're logged out,
// so this should be a pretty safe check
Player player = newEvent.getPlayer();
Player player = syncPacket.getPlayer();
if (player != null) {
// Start the process
getSendingQueue(syncPacket).enqueue(newEvent);
// We know this is occuring on the main thread, so pass TRUE
// We know this is occurring on the main thread, so pass TRUE
getProcessingQueue(syncPacket).enqueue(newEvent, true);
}
}
@Override
public Set<Integer> getSendingFilters() {
return PacketRegistry.toLegacy(serverProcessingQueue.keySet());
}
@Override
public Set<PacketType> getReceivingTypes() {
return serverProcessingQueue.keySet();
}
@Override
public Set<Integer> getReceivingFilters() {
return PacketRegistry.toLegacy(clientProcessingQueue.keySet());
}
@Override
public Set<PacketType> getSendingTypes() {
return clientProcessingQueue.keySet();
@@ -1,5 +1,17 @@
package com.comphenix.protocol.concurrency;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.UncheckedExecutionException;
import org.bukkit.entity.Player;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
@@ -7,18 +19,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.entity.Player;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Function;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.UncheckedExecutionException;
/**
* Represents a concurrent player map.
* <p>
@@ -37,7 +37,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
ADDRESS {
@Override
public Object apply(Player player) {
return player.getAddress();
return player == null ? null : player.getAddress();
}
},
@@ -47,7 +47,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
NAME {
@Override
public Object apply(Player player) {
return player.getName();
return player == null ? null : player.getName();
}
},
}
@@ -55,17 +55,17 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
/**
* An internal map of player keys to values.
*/
protected ConcurrentMap<Object, TValue> valueLookup = createValueMap();
private ConcurrentMap<Object, TValue> valueLookup = createValueMap();
/**
* A cache of the associated keys for each player.
*/
protected ConcurrentMap<Object, Player> keyLookup = createKeyCache();
private ConcurrentMap<Object, Player> keyLookup = createKeyCache();
/**
* The method used to retrieve a unique key for a player.
*/
protected final Function<Player, Object> keyMethod;
private final Function<Player, Object> keyMethod;
/**
* Construct a new concurrent player map that uses each player's address as key.
@@ -89,7 +89,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* Construct a new concurrent player map using the given standard key method.
* @param standardMethod - the standard key method.
*/
public ConcurrentPlayerMap(PlayerKey standardMethod) {
private ConcurrentPlayerMap(PlayerKey standardMethod) {
this.keyMethod = standardMethod;
}
@@ -107,7 +107,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* The default implementation uses a {@link ConcurrentHashMap}.
* @return The value map.
*/
protected ConcurrentMap<Object, TValue> createValueMap() {
private ConcurrentMap<Object, TValue> createValueMap() {
return Maps.newConcurrentMap();
}
@@ -115,19 +115,16 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* Construct a cache of keys and the associated player.
* @return The key map.
*/
protected ConcurrentMap<Object, Player> createKeyCache() {
private ConcurrentMap<Object, Player> createKeyCache() {
return SafeCacheBuilder.newBuilder().
weakValues().
removalListener(
new RemovalListener<Object, Player>() {
@Override
public void onRemoval(RemovalNotification<Object, Player> removed) {
// We ignore explicit removal
if (removed.wasEvicted()) {
onCacheEvicted(removed.getKey());
}
}
}).
(RemovalListener<Object, Player>) removed -> {
// We ignore explicit removal
if (removed.wasEvicted()) {
onCacheEvicted(removed.getKey());
}
}).
build(
new CacheLoader<Object, Player>() {
@Override
@@ -147,7 +144,6 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
/**
* Invoked when an entry in the cache has been evicted, typically by the garbage collector.
* @param key - the key.
* @param player - the value that was evicted or collected.
*/
private void onCacheEvicted(Object key) {
Player newPlayer = findOnlinePlayer(key);
@@ -165,7 +161,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* @param key - a non-null key.
* @return The player with the given key, or NULL if not found.
*/
protected Player findOnlinePlayer(Object key) {
private Player findOnlinePlayer(Object key) {
for (Player player : Util.getOnlinePlayers()) {
if (key.equals(keyMethod.apply(player))) {
return player;
@@ -179,7 +175,7 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* @param key - the key of the player we are locating.
* @return The player, or NULL if not found.
*/
protected Player lookupPlayer(Object key) {
private Player lookupPlayer(Object key) {
try {
return keyLookup.get(key);
} catch (UncheckedExecutionException e) {
@@ -192,7 +188,9 @@ public class ConcurrentPlayerMap<TValue> extends AbstractMap<Player, TValue> imp
* @param player - the player whose key we want to retrieve.
* @return The key.
*/
protected Object cachePlayerKey(Player player) {
private Object cachePlayerKey(Player player) {
Preconditions.checkNotNull(player, "player cannot be null");
Object key = keyMethod.apply(player);
keyLookup.put(key, player);
@@ -55,7 +55,7 @@ public class ReportType {
*/
public static Class<?> getSenderClass(Object sender) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");
throw new IllegalArgumentException("sender cannot be null.");
else if (sender instanceof Class<?>)
return (Class<?>) sender;
else
@@ -75,7 +75,7 @@ public class ReportType {
*/
public static String getReportName(Object sender, ReportType type) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");
throw new IllegalArgumentException("sender cannot be null.");
return getReportName(getSenderClass(sender), type);
}
@@ -83,13 +83,13 @@ public class ReportType {
* Retrieve the full canonical name of a given report type.
* <p>
* This is in the format <i>canonical_name_of_class#report_type</i>
* @param clazz - the sender class.
* @param sender - the sender class.
* @param type - the report instance.
* @return The full canonical name.
*/
private static String getReportName(Class<?> sender, ReportType type) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NUll.");
throw new IllegalArgumentException("sender cannot be null.");
// Whether or not we need to retrieve the report name again
if (type.reportName == null) {
@@ -138,11 +138,10 @@ public class ReportType {
* @return All associated report fields.
*/
private static List<Field> getReportFields(Class<?> clazz) {
return FuzzyReflection.fromClass(clazz).getFieldList(
FuzzyFieldContract.newBuilder().
requireModifier(Modifier.STATIC).
typeDerivedOf(ReportType.class).
build()
);
return FuzzyReflection.fromClass(clazz, true)
.getFieldList(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.typeDerivedOf(ReportType.class)
.build());
}
}
@@ -17,11 +17,7 @@
package com.comphenix.protocol.events;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.*;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.GamePhase;
@@ -44,93 +40,21 @@ public class ListeningWhitelist {
private final GamePhase gamePhase;
private final Set<ListenerOptions> options;
private final Set<PacketType> types;
// Cache whitelist
private transient Set<Integer> intWhitelist;
private ListeningWhitelist(Builder builder) {
this.priority = builder.priority;
this.types = builder.types;
this.gamePhase = builder.gamePhase;
this.options = builder.options;
}
/**
* Creates a packet whitelist for a given priority with a set of packet IDs.
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - set of IDs to observe/enable.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
this(priority, whitelist, GamePhase.PLAYING);
}
/**
* Creates a packet whitelist for a given priority with a set of packet IDs.
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - set of IDs to observe/enable.
* @param gamePhase - which game phase to receieve notifications on.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist, GamePhase gamePhase) {
private ListeningWhitelist(ListenerPriority priority) {
this.priority = priority;
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
this.gamePhase = gamePhase;
this.options = EnumSet.noneOf(ListenerOptions.class);
}
/**
* Creates a packet whitelist of a given priority for a list of packets.
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - list of packet IDs to observe/enable.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
this.priority = priority;
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
this.types = new HashSet<>();
this.gamePhase = GamePhase.PLAYING;
this.options = EnumSet.noneOf(ListenerOptions.class);
}
/**
* Creates a packet whitelist for a given priority with a set of packet IDs.
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - list of packet IDs to observe/enable.
* @param gamePhase - which game phase to receieve notifications on.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase) {
this.priority = priority;
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
this.gamePhase = gamePhase;
this.options = EnumSet.noneOf(ListenerOptions.class);
}
/**
* Creates a packet whitelist for a given priority with a set of packet IDs and options.
* <p>
* Deprecated: Use {@link #newBuilder()} instead.
* @param priority - the listener priority.
* @param whitelist - list of packet IDs to observe/enable.
* @param gamePhase - which game phase to receieve notifications on.
* @param options - every special option associated with this whitelist.
*/
@Deprecated
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase, ListenerOptions... options) {
this.priority = priority;
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
this.gamePhase = gamePhase;
this.options = safeEnumSet(Arrays.asList(options), ListenerOptions.class);
}
/**
* Whether or not this whitelist has any enabled packets.
* @return TRUE if there are any packets, FALSE otherwise.
@@ -147,19 +71,6 @@ public class ListeningWhitelist {
return priority;
}
/**
* Retrieves the list of packets that will be observed by the listeners.
* <p>
* Deprecated: Use {@link #getTypes()} instead.
* @return Packet whitelist.
*/
@Deprecated
public Set<Integer> getWhitelist() {
if (intWhitelist == null)
intWhitelist = PacketRegistry.toLegacy(types);
return intWhitelist;
}
/**
* Retrieves a set of the packets that will be observed by the listeners.
* @return Packet whitelist.
@@ -189,23 +100,6 @@ public class ListeningWhitelist {
return Objects.hashCode(priority, types, gamePhase, options);
}
/**
* Determine if any of the given IDs can be found in the whitelist.
* @param whitelist - whitelist to test.
* @param idList - list of packet IDs to find.
* @return TRUE if any of the packets in the list can be found in the whitelist, FALSE otherwise.
*/
public static boolean containsAny(ListeningWhitelist whitelist, int... idList) {
if (whitelist != null) {
for (int i = 0; i < idList.length; i++) {
if (whitelist.getWhitelist().contains(idList[i]))
return true;
}
}
return false;
}
/**
* Determine if the given whitelist is empty or not.
* @param whitelist - the whitelist to test.
@@ -273,7 +167,7 @@ public class ListeningWhitelist {
/**
* Construct a copy of a given set.
* @param list - the set to copy.
* @param set - the set to copy.
* @return The copied set.
*/
private static <T> Set<T> safeSet(Collection<T> set) {
@@ -364,20 +258,7 @@ public class ListeningWhitelist {
public Builder high() {
return priority(ListenerPriority.HIGH);
}
/**
* Set the whitelist of packet IDs to copy when constructing new whitelists.
* <p>
* Deprecated: Use {@link #types(Collection)} instead.
* @param whitelist - the whitelist of packets.
* @return This builder, for chaining.
*/
@Deprecated
public Builder whitelist(Collection<Integer> whitelist) {
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
return this;
}
/**
* Set the whitelist of packet types to copy when constructing new whitelists.
* @param types - the whitelist of packets.
@@ -17,15 +17,11 @@
package com.comphenix.protocol.events;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import org.bukkit.plugin.Plugin;
/**
* Represents a listener that is notified of every sent and received packet.
@@ -45,26 +41,17 @@ public abstract class MonitorAdapter implements PacketListener {
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
initialize(plugin, side, logger);
}
@SuppressWarnings("deprecation")
private void initialize(Plugin plugin, ConnectionSide side, Logger logger) {
this.plugin = plugin;
// Recover in case something goes wrong
try {
if (side.isForServer())
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
if (side.isForClient())
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
} catch (FieldAccessException e) {
if (side.isForServer())
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
if (side.isForClient())
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
}
if (side.isForServer())
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
if (side.isForClient())
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
}
/**
* Retrieve a logger, even if we're running in a CraftBukkit version that doesn't support it.
* @param plugin - the plugin to retrieve.
Loaded 100 of 347 files, more files were not shown because too many files have changed in this diff. Show more