Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16f4870714 | ||
|
|
e77ed96957 | ||
|
|
46f6e76f91 | ||
|
|
38bbd764cc | ||
|
|
2931af58db | ||
|
|
08ea2da642 | ||
|
|
448e9369de | ||
|
|
ac6f911f15 | ||
|
|
c69bcc36f5 | ||
|
|
0ee93acd65 | ||
|
|
d83dd9ab8a | ||
|
|
ab32f938d7 | ||
|
|
c7753a9d5b | ||
|
|
b51812655a | ||
|
|
a910edc16b | ||
|
|
fb2075b774 | ||
|
|
4b78bf6a34 | ||
|
|
edce5b6d0f | ||
|
|
7217b11ba7 | ||
|
|
18c2b389a4 | ||
|
|
1912a9c871 | ||
|
|
0c6fa46871 | ||
|
|
df3b68df4c | ||
|
|
aebefded86 | ||
|
|
64e1e7de24 | ||
|
|
05fa147b48 | ||
|
|
bba534d694 | ||
|
|
4f0fe72add | ||
|
|
365bb66d2a | ||
|
|
0a41cf9353 | ||
|
|
d0b274249c | ||
|
|
bdbbb7cb9c | ||
|
|
531f28cbaf | ||
|
|
30b69d3ecf | ||
|
|
9d183e85d7 | ||
|
|
eebb99fa37 | ||
|
|
6aaf0ec26b | ||
|
|
86e586da26 | ||
|
|
069783a353 | ||
|
|
cc95e19ba4 | ||
|
|
c31133c20f | ||
|
|
69ae3656b5 | ||
|
|
43145bd478 | ||
|
|
b7c1e096c4 | ||
|
|
6707c4811e | ||
|
|
2092b8f48e | ||
|
|
20e73369fa | ||
|
|
abc0db8281 | ||
|
|
1beb95115f | ||
|
|
7fcfcdc365 | ||
|
|
2be216899a | ||
|
|
8876ce323b | ||
|
|
575174580e | ||
|
|
7fd4ec3172 | ||
|
|
a75d383001 | ||
|
|
7ddfd4f347 | ||
|
|
7e137cbfc5 | ||
|
|
d40762e69d | ||
|
|
ae19478007 | ||
|
|
11a8184c3e | ||
|
|
c3dc00de05 | ||
|
|
84cb541866 | ||
|
|
624f6aaca6 | ||
|
|
c5f0550953 | ||
|
|
7f0bc7fd24 | ||
|
|
96155b1065 | ||
|
|
23dac3287b | ||
|
|
0bbbd961aa | ||
|
|
dd19e1040a | ||
|
|
e45c16d490 | ||
|
|
a19959cea4 | ||
|
|
4f18d37832 | ||
|
|
a2bf242097 | ||
|
|
4e105c59ed | ||
|
|
59ca841ed5 | ||
|
|
5e8f044a18 | ||
|
|
4db1e39ac7 | ||
|
|
e202503c09 | ||
|
|
aed98abac6 | ||
|
|
250f94e9cd | ||
|
|
d7bf43001f | ||
|
|
868b357527 | ||
|
|
9a609c2053 | ||
|
|
f3acce99d8 | ||
|
|
374e6cd5ee | ||
|
|
84a0b5ffdd | ||
|
|
4cc3957723 | ||
|
|
a0a5469988 | ||
|
|
240920d642 | ||
|
|
764195bd55 | ||
|
|
7bfee67a29 | ||
|
|
c87604cf0c | ||
|
|
4096952c16 | ||
|
|
b4eff32213 | ||
|
|
d361526371 | ||
|
|
0d4e4c818f | ||
|
|
073bfa2b86 | ||
|
|
f0059f39f6 | ||
|
|
41bb4bacb2 | ||
|
|
9487c42985 | ||
|
|
55f7b67f9d | ||
|
|
e44d1e6051 | ||
|
|
baecaf4ca4 | ||
|
|
151d4a289f | ||
|
|
1b4d79b302 | ||
|
|
74833f8680 | ||
|
|
e77f8ced4c | ||
|
|
1cd83e493d | ||
|
|
40b6c66491 | ||
|
|
723fc2c7ec | ||
|
|
dd687ce175 | ||
|
|
8774d87d59 | ||
|
|
8361cf078f | ||
|
|
dbedab0c14 | ||
|
|
5dda8c8ab1 | ||
|
|
4c0c18d7c6 | ||
|
|
9c20455bf6 | ||
|
|
8175443da0 | ||
|
|
95a884974a | ||
|
|
153dd61994 | ||
|
|
263ec8a36e | ||
|
|
af46ba4d1a | ||
|
|
dd85904642 | ||
|
|
7a8fce224e | ||
|
|
9ca7c91a76 | ||
|
|
466354cd2c | ||
|
|
99504dab8f | ||
|
|
9b6603e2eb | ||
|
|
4fc476a125 | ||
|
|
583ed4b58a | ||
|
|
638e81b9ce | ||
|
|
9a0703d05d | ||
|
|
9de096f783 | ||
|
|
c54a99945d | ||
|
|
90a38cc15c | ||
|
|
76930ae6e8 | ||
|
|
d745cfb184 | ||
|
|
4be2bf38ff | ||
|
|
0a32f24f08 | ||
|
|
1c2bc274dd | ||
|
|
9b54794f6b | ||
|
|
42bec5a858 | ||
|
|
fa317c1167 | ||
|
|
cc17b9ee6e | ||
|
|
190ca1ff6a | ||
|
|
c51930121f | ||
|
|
c7a8d734d4 | ||
|
|
b446cf2183 | ||
|
|
f11c246276 | ||
|
|
6f91bd23de | ||
|
|
45c293df7d | ||
|
|
5acdb2b3c5 | ||
|
|
72c1f3e26c | ||
|
|
b3ccf82597 | ||
|
|
0c01a11755 | ||
|
|
7ce3f471bf | ||
|
|
97972acee8 | ||
|
|
a0bb11e1bd | ||
|
|
ba74fceed6 | ||
|
|
ab0faab396 | ||
|
|
765fd9e987 | ||
|
|
8f7b530613 | ||
|
|
30fe81d366 | ||
|
|
b54dd49426 | ||
|
|
26274fed52 | ||
|
|
aa555f792e | ||
|
|
0d50905754 | ||
|
|
b7132196fb | ||
|
|
eb54b99856 | ||
|
|
13b3d8679d | ||
|
|
1545a88e06 | ||
|
|
2b22999b94 | ||
|
|
bbb053aa4e | ||
|
|
7bac4ec634 | ||
|
|
4bc9e8b7b7 | ||
|
|
f381f0a2f7 | ||
|
|
553e4b6813 | ||
|
|
bdaa843f2d | ||
|
|
13f5c14599 | ||
|
|
b871eb3d54 | ||
|
|
12e3a895b3 | ||
|
|
8c5fbe3298 | ||
|
|
0512215007 | ||
|
|
80f4c7b9a7 | ||
|
|
8c51b175c4 | ||
|
|
8d991ad5a7 | ||
|
|
f19bfc613e | ||
|
|
a7e702899a | ||
|
|
deb192b04d | ||
|
|
fbe46f7bac | ||
|
|
fd93c1c553 | ||
|
|
b2f6a56843 | ||
|
|
658da31d46 | ||
|
|
77feaa857e | ||
|
|
5f204d798c | ||
|
|
5183bd53b5 | ||
|
|
fdd30a7b87 | ||
|
|
7ac4ac696f | ||
|
|
c203fda391 | ||
|
|
9a65ddbc43 | ||
|
|
54c252a354 | ||
|
|
e92abda187 | ||
|
|
3f7b7f4bb3 | ||
|
|
944b3f8280 | ||
|
|
b04fca8324 | ||
|
|
bfa0eee91e | ||
|
|
308e3d3417 | ||
|
|
42e48aa9b8 | ||
|
|
18d0193288 | ||
|
|
0ab552a6ef | ||
|
|
d9fef94bc6 | ||
|
|
ab5fb40f8f | ||
|
|
4baa4aa724 | ||
|
|
7a4a285935 | ||
|
|
f38c393d6f | ||
|
|
22c2a4abcc | ||
|
|
1a434e9ea2 | ||
|
|
25a5f1d07f | ||
|
|
e2f949c3a7 | ||
|
|
c3c59337ff | ||
|
|
fdf315ba7d | ||
|
|
f46bd56e8f | ||
|
|
b1efed0c0c | ||
|
|
524fb1ba68 | ||
|
|
3ff2ccf1b3 | ||
|
|
e915fd0f9a | ||
|
|
6f8b2377b1 | ||
|
|
c893a3f11e | ||
|
|
73c71e0198 | ||
|
|
bb305fdaad | ||
|
|
b4d4eb29af | ||
|
|
6d9fe45fb4 | ||
|
|
b292c8485b | ||
|
|
1e4da2c93b | ||
|
|
84e31d032b | ||
|
|
59c8c8c9db | ||
|
|
a76ceb94cc | ||
|
|
9a108af219 | ||
|
|
2f09dec1c5 | ||
|
|
4c43f125f9 | ||
|
|
b5cff49358 | ||
|
|
446a1c8326 | ||
|
|
8c20d19339 | ||
|
|
9dfffe5366 | ||
|
|
19dd81ce94 | ||
|
|
dd9eac3d6d | ||
|
|
916434251d | ||
|
|
62e8d82e16 | ||
|
|
d7be712959 | ||
|
|
490b1dadcb | ||
|
|
502c5960e3 | ||
|
|
d297e373b4 | ||
|
|
9f5d95f617 | ||
|
|
b14b4cc345 | ||
|
|
e41bb8753b | ||
|
|
565f169e94 | ||
|
|
7b7449ee15 | ||
|
|
95bb026fa9 |
No files matched your search
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
@@ -19,7 +19,7 @@ This page will detail specific things that must be done if you intend to contrib
|
||||
|
||||
#### Ready? Let's get started!
|
||||
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
|
||||
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [freenode.net](http://freenode.net)
|
||||
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [libera.chat](http://libera.chat)
|
||||
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
|
||||
2. Search for usages of the now-defunct NMS package guard and change them.
|
||||
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
|
||||
@@ -35,4 +35,4 @@ and the release date.
|
||||
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
|
||||
If the protocol version has been incremented, add a new line to the map.
|
||||
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
|
||||
the build fails, debug!
|
||||
the build fails, debug!
|
||||
@@ -0,0 +1,2 @@
|
||||
github: [dmulloy2]
|
||||
custom: ["https://paypal.me/dmulloy2"]
|
||||
@@ -2,6 +2,7 @@ _Follow this template except for feature requests. Use pastebin when providing /
|
||||
|
||||
Make sure you've done the following:
|
||||
- [ ] You're using the latest build for your server version
|
||||
- [ ] This isn't fixed in a recent development build
|
||||
- [ ] This isn't an issue caused by another plugin
|
||||
- [ ] You've checked for duplicate issues
|
||||
- [ ] You didn't use `/reload`
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- [ ] This issue is not solved in a development build
|
||||
|
||||
**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.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- [ ] This feature is not currently present in a development build
|
||||
|
||||
**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.
|
||||
@@ -0,0 +1,28 @@
|
||||
name: ProtocolLib full build lifecycle
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '19'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Run gradle build lifecycle
|
||||
run: ./gradlew build shadowJar --no-daemon
|
||||
|
||||
- name: Upload plugin file
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ProtocolLib
|
||||
path: build/libs/ProtocolLib.jar
|
||||
@@ -0,0 +1,41 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '28 9 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '19'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: 'java'
|
||||
|
||||
- name: Run gradle build lifecycle
|
||||
run: ./gradlew build -x test --no-daemon
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
+6
-1
@@ -175,4 +175,9 @@ pip-log.txt
|
||||
.DS_Store
|
||||
|
||||
# Log4J files
|
||||
logs/
|
||||
logs/
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
logs/
|
||||
.java-version
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
script: mvn clean test
|
||||
#before_install: cd ProtocolLib
|
||||
install: true
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- "dmulloy2@live.com"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
@@ -1,32 +1,33 @@
|
||||
# ProtocolLib [](https://travis-ci.org/dmulloy2/ProtocolLib)
|
||||
# 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
|
||||
Certain tasks are impossible to perform with the standard Bukkit API, and may require
|
||||
working with and even modifying 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
|
||||
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)
|
||||
* [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/index.html)
|
||||
|
||||
### Compilation
|
||||
|
||||
ProtocolLib is built with Maven and requires Spigot and SpigotAPI, which can be found [here](http://www.spigotmc.org/wiki/buildtools/).
|
||||
ProtocolLib is built with [Gradle](https://gradle.org/). If you have it installed, just run
|
||||
`./gradlew build` in the root project folder. Other gradle targets you may be interested in
|
||||
include `clean`, `test`, and `shadowJar`. `shadowJar` will create a jar with all dependencies
|
||||
(ByteBuddy) included.
|
||||
|
||||
### 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.
|
||||
__ProtocolLib__ attempts to solve this problem by providing an 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
|
||||
@@ -35,7 +36,7 @@ To use this library, first add ProtocolLib.jar to your Java build path. Then, ad
|
||||
as a dependency or soft dependency to your plugin.yml file like any other plugin:
|
||||
|
||||
````yml
|
||||
depend: [ProtocolLib]
|
||||
depend: [ ProtocolLib ]
|
||||
````
|
||||
|
||||
You can also add ProtocolLib as a Maven dependency:
|
||||
@@ -44,16 +45,16 @@ You can also add ProtocolLib as a Maven dependency:
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
...
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-API</artifactId>
|
||||
<version>4.4.0</version>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
````
|
||||
@@ -62,11 +63,11 @@ Or use the maven dependency with gradle:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url "http://repo.dmulloy2.net/nexus/repository/public/" }
|
||||
maven { url "https://repo.dmulloy2.net/repository/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.4.0";
|
||||
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -84,16 +85,14 @@ 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) {
|
||||
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);
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
````
|
||||
@@ -103,20 +102,19 @@ censor by listening for Packet3Chat events:
|
||||
|
||||
````java
|
||||
// Censor
|
||||
protocolManager.addPacketListener(new PacketAdapter(this,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Client.CHAT) {
|
||||
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);
|
||||
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!");
|
||||
}
|
||||
if (message.contains("shit") || message.contains("damn")) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage("Bad manners!");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -127,43 +125,43 @@ protocolManager.addPacketListener(new PacketAdapter(this,
|
||||
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>();
|
||||
PacketPlayOutExplosion fakeExplosion = new PacketPlayOutExplosion(
|
||||
player.getLocation().getX(),
|
||||
player.getLocation().getY(),
|
||||
player.getLocation().getZ(),
|
||||
3.0F,
|
||||
new ArrayList<>(),
|
||||
new Vec3D(
|
||||
player.getVelocity().getX() + 1,
|
||||
player.getVelocity().getY() + 1,
|
||||
player.getVelocity().getZ() + 1
|
||||
)
|
||||
);
|
||||
|
||||
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(fakeExplosion);
|
||||
((CraftPlayer) player).getHandle().b.a(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:
|
||||
But with ProtocolLib, you can turn that into something more manageable:
|
||||
|
||||
````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.getDoubles()
|
||||
.write(0, player.getLocation().getX())
|
||||
.write(1, player.getLocation().getY())
|
||||
.write(2, player.getLocation().getZ());
|
||||
fakeExplosion.getFloat().write(0, 3.0F);
|
||||
fakeExplosion.getBlockPositionCollectionModifier().write(0, new ArrayList<>());
|
||||
fakeExplosion.getVectors().write(0, player.getVelocity().add(new Vector(1, 1, 1)));
|
||||
|
||||
try {
|
||||
protocolManager.sendServerPacket(player, fakeExplosion);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(
|
||||
"Cannot send packet " + fakeExplosion, e);
|
||||
}
|
||||
protocolManager.sendServerPacket(player, fakeExplosion);
|
||||
````
|
||||
|
||||
### 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.
|
||||
result is quite flexible. 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 -
|
||||
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.
|
||||
@@ -25,7 +25,7 @@
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
File renamed without changes.
+99
@@ -3,6 +3,8 @@ package com.comphenix.tinyprotocol;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -177,6 +179,31 @@ public final class Reflection {
|
||||
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a field with a given type and parameters. This is most useful
|
||||
* when dealing with Collections.
|
||||
*
|
||||
* @param target the target class.
|
||||
* @param fieldType Type of the field
|
||||
* @param params Variable length array of type parameters
|
||||
* @return The field
|
||||
*
|
||||
* @throws IllegalArgumentException If the field cannot be found
|
||||
*/
|
||||
public static Field getParameterizedField(Class<?> target, Class<?> fieldType, Class<?>... params) {
|
||||
for (Field field : target.getDeclaredFields()) {
|
||||
if (field.getType().equals(fieldType)) {
|
||||
Type type = field.getGenericType();
|
||||
if (type instanceof ParameterizedType) {
|
||||
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||
*
|
||||
@@ -301,6 +328,23 @@ public final class Reflection {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class from its full name with alternatives, without knowing its type on compile time.
|
||||
* <p>
|
||||
* This is useful when looking up fields by a NMS or OBC type.
|
||||
* <p>
|
||||
*
|
||||
* @see {@link #getClass()} for more information.
|
||||
* @param lookupName - the class name with variables.
|
||||
* @param aliases - alternative names for this class.
|
||||
* @return The class.
|
||||
*/
|
||||
public static Class<Object> getUntypedClass(String lookupName, String... aliases) {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
Class<Object> clazz = (Class) getClass(lookupName, aliases);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class from its full name.
|
||||
* <p>
|
||||
@@ -333,6 +377,61 @@ public final class Reflection {
|
||||
return getCanonicalClass(expandVariables(lookupName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first class that matches the full class name.
|
||||
* <p>
|
||||
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Variable</th>
|
||||
* <th>Content</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{nms}</td>
|
||||
* <td>Actual package name of net.minecraft.server.VERSION</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{obc}</td>
|
||||
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{version}</td>
|
||||
* <td>The current Minecraft package VERSION, if any.</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @param lookupName - the class name with variables.
|
||||
* @param aliases - alternative names for this class.
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find any of the given classes.
|
||||
*/
|
||||
public static Class<?> getClass(String lookupName, String... aliases) {
|
||||
try {
|
||||
// Try the main class first
|
||||
return getClass(lookupName);
|
||||
} catch (RuntimeException e) {
|
||||
Class<?> success = null;
|
||||
|
||||
// Try every alias too
|
||||
for (String alias : aliases) {
|
||||
try {
|
||||
success = getClass(alias);
|
||||
break;
|
||||
} catch (RuntimeException e1) {
|
||||
// e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (success != null) {
|
||||
return success;
|
||||
} else {
|
||||
// Hack failed
|
||||
throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class in the net.minecraft.server.VERSION.* package.
|
||||
*
|
||||
+28
-9
@@ -9,6 +9,7 @@ import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -44,21 +45,25 @@ import com.mojang.authlib.GameProfile;
|
||||
public abstract class TinyProtocol {
|
||||
private static final AtomicInteger ID = new AtomicInteger(0);
|
||||
|
||||
// Required Minecraft classes
|
||||
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
|
||||
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
|
||||
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
|
||||
|
||||
// Used in order to lookup a channel
|
||||
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
|
||||
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
|
||||
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
|
||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
|
||||
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
|
||||
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
|
||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
|
||||
|
||||
// Looking up ServerConnection
|
||||
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
|
||||
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
|
||||
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
|
||||
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
|
||||
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
|
||||
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
|
||||
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
||||
|
||||
// Packets we have to intercept
|
||||
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
|
||||
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
|
||||
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
|
||||
|
||||
// Speedup channel lookup
|
||||
@@ -72,7 +77,7 @@ public abstract class TinyProtocol {
|
||||
private List<Object> networkManagers;
|
||||
|
||||
// Injected channel handlers
|
||||
private List<Channel> serverChannels = Lists.newArrayList();
|
||||
private List<Channel> serverChannels = new ArrayList<>();
|
||||
private ChannelInboundHandlerAdapter serverChannelHandler;
|
||||
private ChannelInitializer<Channel> beginInitProtocol;
|
||||
private ChannelInitializer<Channel> endInitProtocol;
|
||||
@@ -199,8 +204,22 @@ public abstract class TinyProtocol {
|
||||
Object serverConnection = getServerConnection.get(mcServer);
|
||||
boolean looking = true;
|
||||
|
||||
try {
|
||||
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
|
||||
field.setAccessible(true);
|
||||
|
||||
networkManagers = (List<Object>) field.get(serverConnection);
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
|
||||
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
||||
|
||||
networkManagers = (List<Object>) method.invoke(null, serverConnection);
|
||||
}
|
||||
|
||||
if (networkManagers == null) {
|
||||
throw new IllegalArgumentException("Failed to obtain list of network managers");
|
||||
}
|
||||
// We need to synchronize against this list
|
||||
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
|
||||
createServerChannelHandler();
|
||||
|
||||
// Find the correct list, or implicitly throw an exception
|
||||
File renamed without changes.
+114
@@ -0,0 +1,114 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
}
|
||||
|
||||
group = 'com.comphenix.protocol'
|
||||
version = '5.0.0'
|
||||
description = 'Provides access to the Minecraft protocol'
|
||||
|
||||
def isSnapshot = version.endsWith('-SNAPSHOT')
|
||||
|
||||
repositories {
|
||||
// mavenLocal() // can speed up build, but may fail in CI
|
||||
mavenCentral()
|
||||
|
||||
maven {
|
||||
url 'https://repo.dmulloy2.net/repository/public/'
|
||||
}
|
||||
|
||||
maven {
|
||||
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
|
||||
}
|
||||
|
||||
maven {
|
||||
url 'https://libraries.minecraft.net/'
|
||||
metadataSources {
|
||||
mavenPom()
|
||||
artifact()
|
||||
ignoreGradleMetadataRedirection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.bytebuddy:byte-buddy:1.14.3'
|
||||
compileOnly 'org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
|
||||
compileOnly 'io.netty:netty-all:4.0.23.Final'
|
||||
compileOnly 'net.kyori:adventure-text-serializer-gson:4.13.0'
|
||||
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
|
||||
testImplementation 'org.mockito:mockito-core:4.11.0'
|
||||
testImplementation 'org.mockito:mockito-inline:4.11.0'
|
||||
testImplementation 'io.netty:netty-common:4.1.77.Final'
|
||||
testImplementation 'io.netty:netty-transport:4.1.77.Final'
|
||||
testImplementation 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-gson:4.13.0'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-plain:4.13.1'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependencies {
|
||||
include(dependency('net.bytebuddy:byte-buddy:.*'))
|
||||
}
|
||||
relocate 'net.bytebuddy', 'com.comphenix.net.bytebuddy'
|
||||
archiveFileName = 'ProtocolLib.jar'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
processResources {
|
||||
def includeBuild = isSnapshot && System.getenv('BUILD_NUMBER')
|
||||
def fullVersion = includeBuild
|
||||
? version + '-' + System.getenv('BUILD_NUMBER')
|
||||
: version
|
||||
|
||||
eachFile { expand(['version': fullVersion]) }
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
|
||||
afterEvaluate {
|
||||
artifactId = 'ProtocolLib'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url isSnapshot
|
||||
? 'https://repo.dmulloy2.net/repository/snapshots/'
|
||||
: 'https://repo.dmulloy2.net/repository/releases/'
|
||||
|
||||
credentials {
|
||||
username project.nexusUsername
|
||||
password project.nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
nexusUsername=""
|
||||
nexusPassword=""
|
||||
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,244 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,5 @@
|
||||
before_install:
|
||||
- source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
- sdk update
|
||||
- sdk install java 17.0.4-tem
|
||||
- sdk use java 17.0.4-tem
|
||||
@@ -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,114 +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.InvocationTargetException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
|
||||
/**
|
||||
* Represents a object capable of sending or receiving packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface PacketStream {
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* @param receiver - the reciever.
|
||||
* @param packet - packet to send.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* @param receiver - the reciever.
|
||||
* @param packet - packet to send.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @param marker - the network marker to use.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Send a wire packet to the given player.
|
||||
* @param receiver - the receiver.
|
||||
* @param id - packet id.
|
||||
* @param bytes - packet bytes.
|
||||
* @throws InvocationTargetException if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Send a wire packet to the given player.
|
||||
* @param receiver - the receiver.
|
||||
* @param packet - packet to send.
|
||||
* @throws InvocationTargetException if an error occured when sending the packet.
|
||||
*/
|
||||
public void sendWirePacket(Player receiver, WirePacket packet) throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Simulate recieving a certain packet from a given player.
|
||||
* @param sender - the sender.
|
||||
* @param packet - the packet that was sent.
|
||||
* @param marker - the network marker to use.
|
||||
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
|
||||
* @throws InvocationTargetException If the reflection machinery failed.
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
}
|
||||
@@ -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,143 +0,0 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2016 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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* The main entry point for ProtocolLib.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class ProtocolLibrary {
|
||||
/**
|
||||
* The minimum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MINIMUM_MINECRAFT_VERSION = "1.8";
|
||||
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.13.1";
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.13.1) was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22";
|
||||
|
||||
/**
|
||||
* Plugins that are currently incompatible with ProtocolLib.
|
||||
*/
|
||||
public static final List<String> INCOMPATIBLE = Arrays.asList("TagAPI");
|
||||
|
||||
private static Plugin plugin;
|
||||
private static ProtocolConfig config;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ProtocolLib plugin instance.
|
||||
* @return The plugin instance
|
||||
*/
|
||||
public static Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ProtocolLib's configuration
|
||||
* @return The config
|
||||
*/
|
||||
public static ProtocolConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the packet protocol manager.
|
||||
* @return Packet protocol manager
|
||||
*/
|
||||
public static ProtocolManager getProtocolManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current error reporter.
|
||||
* @return Current error reporter.
|
||||
*/
|
||||
public static ErrorReporter getErrorReporter() {
|
||||
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.
|
||||
*/
|
||||
public static void disableUpdates() {
|
||||
updatesDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not updates are currently disabled.
|
||||
* @return True if it is, false if not
|
||||
*/
|
||||
public static boolean updatesDisabled() {
|
||||
return updatesDisabled;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +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.concurrency;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a very quick integer set that uses a lookup table to store membership.
|
||||
* <p>
|
||||
* This class is intentionally missing a size method.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class IntegerSet {
|
||||
private final boolean[] array;
|
||||
|
||||
/**
|
||||
* Initialize a lookup table with the given maximum number of elements.
|
||||
* <p>
|
||||
* This creates a set for elements in the range [0, count).
|
||||
* <p>
|
||||
* Formally, the current set must be a subset of [0, 1, 2, ..., count - 1].
|
||||
* @param maximumCount - maximum element value and count.
|
||||
*/
|
||||
public IntegerSet(int maximumCount) {
|
||||
this.array = new boolean[maximumCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a lookup table with a given maximum and value list.
|
||||
* <p>
|
||||
* The provided elements must be in the range [0, count).
|
||||
* @param maximumCount - the maximum element value and count.
|
||||
* @param values - the elements to add to the set.
|
||||
*/
|
||||
public IntegerSet(int maximumCount, Collection<Integer> values) {
|
||||
this.array = new boolean[maximumCount];
|
||||
addAll(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the given element exists in the set.
|
||||
* @param element - the element to check. Must be in the range [0, count).
|
||||
* @return TRUE if the given element exists, FALSE otherwise.
|
||||
*/
|
||||
public boolean contains(int element) {
|
||||
return array[element];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given element to the set, or do nothing if it already exists.
|
||||
* @param element - element to add.
|
||||
* @throws ArrayIndexOutOfBoundsException If the given element is not in the range [0, count).
|
||||
*/
|
||||
public void add(int element) {
|
||||
array[element] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given collection of elements to the set.
|
||||
* @param packets - elements to add.
|
||||
*/
|
||||
public void addAll(Collection<Integer> packets) {
|
||||
for (Integer id : packets) {
|
||||
add(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given element from the set, or do nothing if it's already removed.
|
||||
* @param element - element to remove.
|
||||
*/
|
||||
public void remove(int element) {
|
||||
// We don't actually care if the caller tries to remove an element outside the valid set
|
||||
if (element >= 0 && element < array.length)
|
||||
array[element] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove every element from the set.
|
||||
*/
|
||||
public void clear() {
|
||||
Arrays.fill(array, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the current IntegerSet to an equivalent HashSet.
|
||||
* @return The resulting HashSet.
|
||||
*/
|
||||
public Set<Integer> toSet() {
|
||||
Set<Integer> elements = new HashSet<Integer>();
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i])
|
||||
elements.add(i);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
-244
@@ -1,244 +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.concurrency;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
|
||||
* @param <T> - type of the elements in the list.
|
||||
*/
|
||||
public class SortedCopyOnWriteArray<T extends Comparable<T>> implements Collection<T> {
|
||||
// Prevent reordering
|
||||
private volatile List<T> list;
|
||||
|
||||
/**
|
||||
* Construct an empty sorted array.
|
||||
*/
|
||||
public SortedCopyOnWriteArray() {
|
||||
list = new ArrayList<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sorted array from the given list. The elements will be automatically sorted.
|
||||
* @param wrapped - the collection whose elements are to be placed into the list.
|
||||
*/
|
||||
public SortedCopyOnWriteArray(Collection<T> wrapped) {
|
||||
this.list = new ArrayList<T>(wrapped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sorted array from the given list.
|
||||
* @param wrapped - the collection whose elements are to be placed into the list.
|
||||
* @param sort - TRUE to automatically sort the collection, FALSE if it is already sorted.
|
||||
*/
|
||||
public SortedCopyOnWriteArray(Collection<T> wrapped, boolean sort) {
|
||||
this.list = new ArrayList<T>(wrapped);
|
||||
|
||||
if (sort) {
|
||||
Collections.sort(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given element in the proper location.
|
||||
* @param value - element to insert.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean add(T value) {
|
||||
// We use NULL as a special marker, so we don't allow it
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("value cannot be NULL");
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
for (T element : list) {
|
||||
// If the value is now greater than the current element, it should be placed right before it
|
||||
if (value != null && value.compareTo(element) < 0) {
|
||||
copy.add(value);
|
||||
value = null;
|
||||
}
|
||||
copy.add(element);
|
||||
}
|
||||
|
||||
// Don't forget to add it
|
||||
if (value != null)
|
||||
copy.add(value);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends T> values) {
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
// Insert the new content and sort it
|
||||
copy.addAll(list);
|
||||
copy.addAll(values);
|
||||
Collections.sort(copy);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a new list with every element except the one given.
|
||||
* <p>
|
||||
* Objects will be compared using the given objects equals() method.
|
||||
* @param value - value to remove.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean remove(Object value) {
|
||||
List<T> copy = new ArrayList<T>();
|
||||
boolean result = false;
|
||||
|
||||
// Note that there's not much to be gained from using BinarySearch, as we
|
||||
// have to copy (and thus read) the entire list regardless.
|
||||
|
||||
// Copy every element except the one given to us.
|
||||
for (T element : list) {
|
||||
if (!Objects.equal(value, element)) {
|
||||
copy.add(element);
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
list = copy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
copy.addAll(list);
|
||||
copy.removeAll(values);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null)
|
||||
throw new IllegalArgumentException("values cannot be NULL");
|
||||
if (values.size() == 0)
|
||||
return false;
|
||||
|
||||
List<T> copy = new ArrayList<T>();
|
||||
|
||||
copy.addAll(list);
|
||||
copy.removeAll(values);
|
||||
|
||||
list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a copy of every element except the one with the given index.
|
||||
* @param index - index of the element to remove.
|
||||
*/
|
||||
public synchronized void remove(int index) {
|
||||
List<T> copy = new ArrayList<T>(list);
|
||||
|
||||
copy.remove(index);
|
||||
list = copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an element by index.
|
||||
* @param index - index of element to retrieve.
|
||||
* @return The element at the given location.
|
||||
*/
|
||||
public T get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator over the elements in the given list.
|
||||
* Warning: No not attempt to remove elements using the iterator.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return Iterables.unmodifiableIterable(list).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
list = new ArrayList<T>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> values) {
|
||||
return list.containsAll(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return list.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,96 +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.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;
|
||||
|
||||
/**
|
||||
* Represents a listener that is notified of every sent and received packet.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class MonitorAdapter implements PacketListener {
|
||||
|
||||
private Plugin plugin;
|
||||
private ListeningWhitelist sending = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
private ListeningWhitelist receiving = ListeningWhitelist.EMPTY_WHITELIST;
|
||||
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side) {
|
||||
initialize(plugin, side, getLogger(plugin));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a logger, even if we're running in a CraftBukkit version that doesn't support it.
|
||||
* @param plugin - the plugin to retrieve.
|
||||
* @return The logger.
|
||||
*/
|
||||
private Logger getLogger(Plugin plugin) {
|
||||
try {
|
||||
return plugin.getLogger();
|
||||
} catch (NoSuchMethodError e) {
|
||||
return Logger.getLogger("Minecraft");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sending;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getReceivingWhitelist() {
|
||||
return receiving;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.utility.ByteBufferInputStream;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
/**
|
||||
* Marker containing the serialized packet data seen from the network,
|
||||
* or output handlers that will serialize the current packet.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class NetworkMarker {
|
||||
public static class EmptyBufferMarker extends NetworkMarker {
|
||||
public EmptyBufferMarker(@Nonnull ConnectionSide side) {
|
||||
super(side, (byte[]) null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Custom network handler
|
||||
private PriorityQueue<PacketOutputHandler> outputHandlers;
|
||||
// Post listeners
|
||||
private List<PacketPostListener> postListeners;
|
||||
// Post packets
|
||||
private List<ScheduledPacket> scheduledPackets;
|
||||
|
||||
// The input buffer
|
||||
private ByteBuffer inputBuffer;
|
||||
private final ConnectionSide side;
|
||||
private final PacketType type;
|
||||
|
||||
// Cache serializer too
|
||||
private StreamSerializer serializer;
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* @param side - which side this marker belongs to.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param type - packet type
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.inputBuffer = inputBuffer;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new network marker.
|
||||
* <p>
|
||||
* The input buffer is only non-null for client-side packets.
|
||||
* @param side - which side this marker belongs to.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param type - packet type
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.type = type;
|
||||
|
||||
if (inputBuffer != null) {
|
||||
this.inputBuffer = ByteBuffer.wrap(inputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not this marker belongs to a client or a server side packet.
|
||||
* @return The side the parent packet belongs to.
|
||||
*/
|
||||
public ConnectionSide getSide() {
|
||||
return side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a utility class for serializing and deserializing Minecraft objects.
|
||||
* @return Serialization utility class.
|
||||
*/
|
||||
public StreamSerializer getSerializer() {
|
||||
if (serializer == null)
|
||||
serializer = new StreamSerializer();
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header by default) from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
* <p>
|
||||
* It returns NULL if the packet was transmitted by a plugin locally.
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer() {
|
||||
return getInputBuffer(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
* <p>
|
||||
* It returns NULL if the packet was transmitted by a plugin locally.
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
|
||||
if (inputBuffer != null) {
|
||||
ByteBuffer result = inputBuffer.asReadOnlyBuffer();
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
result = skipHeader(result);
|
||||
else
|
||||
result = addHeader(result, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header by default) as an input stream.
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
public DataInputStream getInputStream() {
|
||||
return getInputStream(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data as an input stream.
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public DataInputStream getInputStream(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
if (inputBuffer == null)
|
||||
return null;
|
||||
|
||||
DataInputStream input = new DataInputStream(
|
||||
new ByteArrayInputStream(inputBuffer.array())
|
||||
);
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
input = skipHeader(input);
|
||||
else
|
||||
input = addHeader(input, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the output handlers have to write a packet header.
|
||||
* @return TRUE if they do, FALSE otherwise.
|
||||
*/
|
||||
public boolean requireOutputHeader() {
|
||||
return MinecraftReflection.isUsingNetty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the given output handler for managing how the current packet will be written to the network stream.
|
||||
* <p>
|
||||
* Note that output handlers are not serialized, as most consumers will probably implement them using anonymous classes.
|
||||
* It is not safe to serialize anonymous classes, as their name depend on the order in which they are declared in the parent class.
|
||||
* <p>
|
||||
* This can only be invoked on server side packet events.
|
||||
* @param handler - the handler that will take part in serializing the packet.
|
||||
* @return TRUE if it was added, FALSE if it has already been added.
|
||||
*/
|
||||
public boolean addOutputHandler(@Nonnull PacketOutputHandler handler) {
|
||||
checkServerSide();
|
||||
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
|
||||
|
||||
// Lazy initialization - it's imperative that we save space and time here
|
||||
if (outputHandlers == null) {
|
||||
outputHandlers = new PriorityQueue<PacketOutputHandler>(10, new Comparator<PacketOutputHandler>() {
|
||||
@Override
|
||||
public int compare(PacketOutputHandler o1, PacketOutputHandler o2) {
|
||||
return Ints.compare(o1.getPriority().getSlot(), o2.getPriority().getSlot());
|
||||
}
|
||||
});
|
||||
}
|
||||
return outputHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given output handler from the serialization queue.
|
||||
* <p>
|
||||
* This can only be invoked on server side packet events.
|
||||
* @param handler - the handler to remove.
|
||||
* @return TRUE if the handler was removed, FALSE otherwise.
|
||||
*/
|
||||
public boolean removeOutputHandler(@Nonnull PacketOutputHandler handler) {
|
||||
checkServerSide();
|
||||
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
|
||||
|
||||
if (outputHandlers != null) {
|
||||
return outputHandlers.remove(handler);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every registered output handler in no particular order.
|
||||
* @return Every registered output handler.
|
||||
*/
|
||||
@Nonnull
|
||||
public Collection<PacketOutputHandler> getOutputHandlers() {
|
||||
if (outputHandlers != null) {
|
||||
return outputHandlers;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener that is invoked after a packet has been successfully sent to the client, or received
|
||||
* by the server.
|
||||
* <p>
|
||||
* Received packets are not guarenteed to have been fully processed, but packets passed
|
||||
* to {@link ProtocolManager#recieveClientPacket(Player, PacketContainer)} will be processed after the
|
||||
* current packet event.
|
||||
* <p>
|
||||
* Note that post listeners will be executed asynchronously off the main thread. They are not executed
|
||||
* in any defined order.
|
||||
* @param listener - the listener that will be invoked.
|
||||
* @return TRUE if it was added.
|
||||
*/
|
||||
public boolean addPostListener(PacketPostListener listener) {
|
||||
if (postListeners == null)
|
||||
postListeners = Lists.newArrayList();
|
||||
return postListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first instance of the given listener.
|
||||
* @param listener - listener to remove.
|
||||
* @return TRUE if it was removed, FALSE otherwise.
|
||||
*/
|
||||
public boolean removePostListener(PacketPostListener listener) {
|
||||
if (postListeners != null) {
|
||||
return postListeners.remove(listener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of all the listeners that will be invoked once the packet has been sent or received.
|
||||
* @return Every post packet listener. Never NULL.
|
||||
*/
|
||||
public List<PacketPostListener> getPostListeners() {
|
||||
return postListeners != null ? Collections.unmodifiableList(postListeners) : Collections.<PacketPostListener>emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of packets that will be schedule (in-order) when the current packet has been successfully transmitted.
|
||||
* <p>
|
||||
* This list is modifiable.
|
||||
* @return List of packets that will be scheduled.
|
||||
*/
|
||||
public List<ScheduledPacket> getScheduledPackets() {
|
||||
if (scheduledPackets == null)
|
||||
scheduledPackets = Lists.newArrayList();
|
||||
return scheduledPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the packet event is server side.
|
||||
*/
|
||||
private void checkServerSide() {
|
||||
if (side.isForClient()) {
|
||||
throw new IllegalStateException("Must be a server side packet.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a byte buffer without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the position of the buffer.
|
||||
* @param buffer - a read-only byte source.
|
||||
* @return A byte buffer without the header in the current packet.
|
||||
* @throws IOException If integer reading fails
|
||||
*/
|
||||
protected ByteBuffer skipHeader(ByteBuffer buffer) throws IOException {
|
||||
skipHeader(new DataInputStream(new ByteBufferInputStream(buffer)));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an input stream without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the input stream.
|
||||
* @param input - input stream
|
||||
* @return An input stream without the header
|
||||
* @throws IOException If integer reading fails
|
||||
*/
|
||||
protected abstract DataInputStream skipHeader(DataInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the byte buffer prepended with the packet header.
|
||||
* @param buffer - the read-only byte buffer.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract ByteBuffer addHeader(ByteBuffer buffer, PacketType type);
|
||||
|
||||
/**
|
||||
* Return the input stream prepended with the packet header.
|
||||
* @param input - the input stream.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract DataInputStream addHeader(DataInputStream input, PacketType type);
|
||||
|
||||
/**
|
||||
* Determine if the given marker has any output handlers.
|
||||
* @param marker - the marker to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasOutputHandlers(NetworkMarker marker) {
|
||||
return marker != null && !marker.getOutputHandlers().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given marker has any post listeners.
|
||||
* @param marker - the marker to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public static boolean hasPostListeners(NetworkMarker marker) {
|
||||
return marker != null && !marker.getPostListeners().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the byte buffer stored in the given marker.
|
||||
* @param marker - the marker.
|
||||
* @return The byte buffer, or NULL if not found.
|
||||
*/
|
||||
public static byte[] getByteBuffer(NetworkMarker marker) {
|
||||
if (marker != null) {
|
||||
ByteBuffer buffer = marker.getInputBuffer();
|
||||
|
||||
if (buffer != null) {
|
||||
byte[] data = new byte[buffer.remaining()];
|
||||
|
||||
buffer.get(data, 0, data.length);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the network marker of a particular event without creating it.
|
||||
* <p>
|
||||
* This is an internal method that should not be used by API users.
|
||||
* @param event - the event.
|
||||
* @return The network marker.
|
||||
*/
|
||||
public static NetworkMarker getNetworkMarker(PacketEvent event) {
|
||||
return event.networkMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the scheduled packets of a particular network marker without constructing the list.
|
||||
* <p>
|
||||
* This is an internal method that should not be used by API users.
|
||||
* @param marker - the marker.
|
||||
* @return The list, or NULL if not found or initialized.
|
||||
*/
|
||||
public static List<ScheduledPacket> readScheduledPackets(NetworkMarker marker) {
|
||||
return marker.scheduledPackets;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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,31 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
/**
|
||||
* Sets the inject hook type. Different types allow for maximum compatibility.
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum PlayerInjectHooks {
|
||||
/**
|
||||
* The injection hook that does nothing. Set when every other inject hook fails.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Override the network handler object itself. Only works in 1.3.
|
||||
* <p>
|
||||
* Cannot intercept MapChunk packets.
|
||||
*/
|
||||
NETWORK_MANAGER_OBJECT,
|
||||
|
||||
/**
|
||||
* Override the packet queue lists in NetworkHandler.
|
||||
* <p>
|
||||
* Cannot intercept MapChunk packets.
|
||||
*/
|
||||
NETWORK_HANDLER_FIELDS,
|
||||
|
||||
/**
|
||||
* Override the server handler object. Versatile, but a tad slower.
|
||||
*/
|
||||
NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
-58
@@ -1,58 +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;
|
||||
|
||||
/**
|
||||
* Invoked when attempting to use a player that has already logged out.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PlayerLoggedOutException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = 4889257862160145234L;
|
||||
|
||||
public PlayerLoggedOutException() {
|
||||
// Default error message
|
||||
super("Cannot inject a player that has already logged out.");
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlayerLoggedOutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception from a formatted message.
|
||||
* @param message - the message to format.
|
||||
* @param params - parameters.
|
||||
* @return The formated exception
|
||||
*/
|
||||
public static PlayerLoggedOutException fromFormat(String message, Object... params) {
|
||||
return new PlayerLoggedOutException(String.format(message, params));
|
||||
}
|
||||
}
|
||||
@@ -1,177 +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.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.compiler.CompileListener;
|
||||
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Caches structure modifiers.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StructureCache {
|
||||
// Structure modifiers
|
||||
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers =
|
||||
new ConcurrentHashMap<PacketType, StructureModifier<Object>>();
|
||||
|
||||
private static Set<PacketType> compiling = new HashSet<PacketType>();
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given id.
|
||||
* <p>
|
||||
* Decreated: Use {@link #newPacket(PacketType)} instead.
|
||||
* @param legacyId - legacy (1.6.4) packet id.
|
||||
* @return Created packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Object newPacket(int legacyId) {
|
||||
return newPacket(PacketType.findLegacy(legacyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given type.
|
||||
* @param type - packet type.
|
||||
* @return Created packet.
|
||||
*/
|
||||
public static Object newPacket(PacketType type) {
|
||||
Class<?> clazz = PacketRegistry.getPacketClassFromType(type, true);
|
||||
|
||||
// Check the return value
|
||||
if (clazz != null) {
|
||||
// TODO: Optimize DefaultInstances
|
||||
Object result = DefaultInstances.DEFAULT.create(clazz);
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("Failed to create packet for type: " + type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet id.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getStructure(PacketType)} instead.
|
||||
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
@Deprecated
|
||||
public static StructureModifier<Object> getStructure(int legacyId) {
|
||||
return getStructure(PacketType.findLegacy(legacyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(PacketType type) {
|
||||
// Compile structures by default
|
||||
return getStructure(type, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier given a packet type.
|
||||
* @param packetType - packet type.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(Class<?> packetType) {
|
||||
// Compile structures by default
|
||||
return getStructure(packetType, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier given a packet type.
|
||||
* @param packetType - packet type.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
|
||||
// Get the ID from the class
|
||||
return getStructure(PacketRegistry.getPacketType(packetType), compile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getStructure(PacketType, boolean)} instead.
|
||||
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
@Deprecated
|
||||
public static StructureModifier<Object> getStructure(final int legacyId, boolean compile) {
|
||||
return getStructure(PacketType.findLegacy(legacyId), compile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(final PacketType type, boolean compile) {
|
||||
StructureModifier<Object> result = structureModifiers.get(type);
|
||||
|
||||
// We don't want to create this for every lookup
|
||||
if (result == null) {
|
||||
// Use the vanilla class definition
|
||||
final StructureModifier<Object> value = new StructureModifier<Object>(
|
||||
PacketRegistry.getPacketClassFromType(type, true), MinecraftReflection.getPacketClass(), true);
|
||||
|
||||
result = structureModifiers.putIfAbsent(type, value);
|
||||
|
||||
// We may end up creating multiple modifiers, but we'll agree on which to use
|
||||
if (result == null) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically compile the structure modifier
|
||||
if (compile && !(result instanceof CompiledStructureModifier)) {
|
||||
// Compilation is many orders of magnitude slower than synchronization
|
||||
synchronized (compiling) {
|
||||
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
|
||||
|
||||
if (!compiling.contains(type) && compiler != null) {
|
||||
compiler.scheduleCompilation(result, new CompileListener<Object>() {
|
||||
@Override
|
||||
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
||||
structureModifiers.put(type, compiledModifier);
|
||||
}
|
||||
});
|
||||
compiling.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-108
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a way of accessing the new netty Protocol enum.
|
||||
* @author Kristian
|
||||
*/
|
||||
|
||||
public abstract class ProtocolRegistry {
|
||||
/**
|
||||
* Represents a register we are currently building.
|
||||
* @author Kristian
|
||||
*/
|
||||
protected static class Register {
|
||||
// The main lookup table
|
||||
public BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
|
||||
public volatile Set<PacketType> serverPackets = Sets.newHashSet();
|
||||
public volatile Set<PacketType> clientPackets = Sets.newHashSet();
|
||||
public List<MapContainer> containers = Lists.newArrayList();
|
||||
|
||||
public Register() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current register is outdated.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isOutdated() {
|
||||
for (MapContainer container : containers) {
|
||||
if (container.hasChanged()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Class<?> enumProtocol;
|
||||
|
||||
// Current register
|
||||
protected volatile Register register;
|
||||
|
||||
public ProtocolRegistry() {
|
||||
enumProtocol = MinecraftReflection.getEnumProtocolClass();
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<PacketType, Class<?>> getPacketTypeLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the class to packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<Class<?>, PacketType> getPacketClassLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass.inverse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known client packet, from every protocol.
|
||||
* @return Every client packet.
|
||||
*/
|
||||
public Set<PacketType> getClientPackets() {
|
||||
return Collections.unmodifiableSet(register.clientPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known server packet, from every protocol.
|
||||
* @return Every server packet.
|
||||
*/
|
||||
public Set<PacketType> getServerPackets() {
|
||||
return Collections.unmodifiableSet(register.serverPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that our local register is up-to-date with Minecraft.
|
||||
* <p>
|
||||
* This operation may block the calling thread.
|
||||
*/
|
||||
public synchronized void synchronize() {
|
||||
// Check if the packet registry has changed
|
||||
if (register.isOutdated()) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the packet lookup tables in each protocol.
|
||||
*/
|
||||
protected abstract void initialize();
|
||||
|
||||
protected abstract void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender);
|
||||
|
||||
/**
|
||||
* Retrieve the number of mapping in all the maps.
|
||||
* @param maps - iterable of maps.
|
||||
* @return The sum of all the entries.
|
||||
*/
|
||||
protected final int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
||||
int count = 0;
|
||||
|
||||
for (Map<Integer, Class<?>> map : maps)
|
||||
count += map.size();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -1,339 +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.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Static packet registry in Minecraft.
|
||||
* @author Kristian
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PacketRegistry {
|
||||
public static final ReportType REPORT_CANNOT_CORRECT_TROVE_MAP = new ReportType("Unable to correct no entry value.");
|
||||
|
||||
public static final ReportType REPORT_INSUFFICIENT_SERVER_PACKETS = new ReportType("Too few server packets detected: %s");
|
||||
public static final ReportType REPORT_INSUFFICIENT_CLIENT_PACKETS = new ReportType("Too few client packets detected: %s");
|
||||
|
||||
// The Netty packet registry
|
||||
private static volatile ProtocolRegistry NETTY;
|
||||
|
||||
// Cached for Netty
|
||||
private static volatile Set<Integer> LEGACY_SERVER_PACKETS;
|
||||
private static volatile Set<Integer> LEGACY_CLIENT_PACKETS;
|
||||
private static volatile Map<Integer, Class> LEGACY_PREVIOUS_PACKETS;
|
||||
|
||||
// Whether or not the registry has been initialized
|
||||
private static volatile boolean INITIALIZED = false;
|
||||
|
||||
/**
|
||||
* Initializes the packet registry.
|
||||
*/
|
||||
private static void initialize() {
|
||||
if (INITIALIZED) {
|
||||
if (NETTY == null) {
|
||||
throw new IllegalStateException("Failed to initialize packet registry.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NETTY = new NettyProtocolRegistry();
|
||||
INITIALIZED = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet type is supported on the current server.
|
||||
* @param type - the type to check.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isSupported(PacketType type) {
|
||||
initialize();
|
||||
return NETTY.getPacketTypeLookup().containsKey(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a map of every packet class to every ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getPacketToType()} instead.
|
||||
* @return A map of packet classes and their corresponding ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Class, Integer> getPacketToID() {
|
||||
initialize();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Class, Integer> result = (Map) Maps.transformValues(
|
||||
NETTY.getPacketClassLookup(),
|
||||
new Function<PacketType, Integer>() {
|
||||
@Override
|
||||
public Integer apply(PacketType type) {
|
||||
return type.getLegacyId();
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a map of every packet class to the respective packet type.
|
||||
* @return A map of packet classes and their corresponding packet type.
|
||||
*/
|
||||
public static Map<Class, PacketType> getPacketToType() {
|
||||
initialize();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Class, PacketType> result = (Map) NETTY.getPacketClassLookup();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the injected proxy classes handlig each packet ID.
|
||||
* <p>
|
||||
* This is not supported in 1.7.2 and later.
|
||||
* @return Injected classes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Integer, Class> getOverwrittenPackets() {
|
||||
initialize();
|
||||
throw new IllegalStateException("Not supported on Netty.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the vanilla classes handling each packet ID.
|
||||
* @return Vanilla classes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Integer, Class> getPreviousPackets() {
|
||||
initialize();
|
||||
|
||||
// Construct it first
|
||||
if (LEGACY_PREVIOUS_PACKETS == null) {
|
||||
Map<Integer, Class> map = Maps.newHashMap();
|
||||
|
||||
for (Entry<PacketType, Class<?>> entry : NETTY.getPacketTypeLookup().entrySet()) {
|
||||
map.put(entry.getKey().getLegacyId(), entry.getValue());
|
||||
}
|
||||
LEGACY_PREVIOUS_PACKETS = Collections.unmodifiableMap(map);
|
||||
}
|
||||
return LEGACY_PREVIOUS_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getServerPacketTypes()} instead.
|
||||
* @return An immutable set of every known server packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||
if (LEGACY_SERVER_PACKETS == null) {
|
||||
LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes());
|
||||
}
|
||||
|
||||
return LEGACY_SERVER_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
* @return Every server packet type.
|
||||
*/
|
||||
public static Set<PacketType> getServerPacketTypes() {
|
||||
initialize();
|
||||
NETTY.synchronize();
|
||||
|
||||
return NETTY.getServerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported client packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getClientPacketTypes()} instead.
|
||||
* @return An immutable set of every known client packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||
if (LEGACY_CLIENT_PACKETS == null) {
|
||||
LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes());
|
||||
}
|
||||
|
||||
return LEGACY_CLIENT_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
* @return Every server packet type.
|
||||
*/
|
||||
public static Set<PacketType> getClientPacketTypes() {
|
||||
initialize();
|
||||
NETTY.synchronize();
|
||||
|
||||
return NETTY.getClientPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of packet types to a set of integers based on the legacy packet ID.
|
||||
* @param types - packet type.
|
||||
* @return Set of integers.
|
||||
*/
|
||||
public static Set<Integer> toLegacy(Set<PacketType> types) {
|
||||
Set<Integer> result = Sets.newHashSet();
|
||||
|
||||
for (PacketType type : types)
|
||||
result.add(type.getLegacyId());
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of legacy packet IDs to packet types.
|
||||
* @param ids - legacy packet IDs.
|
||||
* @return Set of packet types.
|
||||
*/
|
||||
public static Set<PacketType> toPacketTypes(Set<Integer> ids) {
|
||||
return toPacketTypes(ids, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of legacy packet IDs to packet types.
|
||||
* @param ids - legacy packet IDs.
|
||||
* @param preference - the sender preference, if any.
|
||||
* @return Set of packet types.
|
||||
*/
|
||||
public static Set<PacketType> toPacketTypes(Set<Integer> ids, Sender preference) {
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
|
||||
for (int id : ids)
|
||||
result.add(PacketType.fromLegacy(id, preference));
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getPacketClassFromType(PacketType)} instead.
|
||||
* @param packetID - the packet ID.
|
||||
* @return The associated class.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class getPacketClassFromID(int packetID) {
|
||||
initialize();
|
||||
return NETTY.getPacketTypeLookup().get(PacketType.findLegacy(packetID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given type.
|
||||
* @param type - the packet type.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public static Class getPacketClassFromType(PacketType type) {
|
||||
return getPacketClassFromType(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given type.
|
||||
* <p>
|
||||
* Note that forceVanillla will be ignored on MC 1.7.2 and later.
|
||||
* @param type - the packet type.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
|
||||
initialize();
|
||||
|
||||
// Try the lookup first
|
||||
Class<?> clazz = NETTY.getPacketTypeLookup().get(type);
|
||||
if (clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
// Then try looking up the class names
|
||||
for (String name : type.getClassNames()) {
|
||||
try {
|
||||
clazz = MinecraftReflection.getMinecraftClass(name);
|
||||
break;
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Cache the result?
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* <p>
|
||||
* This method has been deprecated.
|
||||
* @param packetID - the packet ID.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||
initialize();
|
||||
return getPacketClassFromID(packetID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet ID of a given packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getPacketType(Class)}.
|
||||
* @param packet - the type of packet to check.
|
||||
* @return The legacy ID of the given packet.
|
||||
* @throws IllegalArgumentException If this is not a valid packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int getPacketID(Class<?> packet) {
|
||||
initialize();
|
||||
return NETTY.getPacketClassLookup().get(packet).getLegacyId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet type of a given packet.
|
||||
* @param packet - the class of the packet.
|
||||
* @return The packet type, or NULL if not found.
|
||||
*/
|
||||
public static PacketType getPacketType(Class<?> packet) {
|
||||
return getPacketType(packet, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet type of a given packet.
|
||||
* @param packet - the class of the packet.
|
||||
* @param sender - the sender of the packet, or NULL.
|
||||
* @return The packet type, or NULL if not found.
|
||||
*/
|
||||
public static PacketType getPacketType(Class<?> packet, Sender sender) {
|
||||
initialize();
|
||||
return NETTY.getPacketClassLookup().get(packet);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an injector that only gives access to a player's socket.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface SocketInjector {
|
||||
/**
|
||||
* Retrieve the associated socket of this player.
|
||||
* @return The associated socket.
|
||||
* @throws IllegalAccessException If we're unable to read the socket field.
|
||||
*/
|
||||
public abstract Socket getSocket() throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Retrieve the associated address of this player.
|
||||
* @return The associated address.
|
||||
* @throws IllegalAccessException If we're unable to read the socket field.
|
||||
*/
|
||||
public abstract SocketAddress getAddress() throws IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Attempt to disconnect the current client.
|
||||
* @param message - the message to display.
|
||||
* @throws InvocationTargetException If disconnection failed.
|
||||
*/
|
||||
public abstract void disconnect(String message) throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player.
|
||||
* @return The hooked player.
|
||||
*/
|
||||
public abstract Player getPlayer();
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player object OR the more up-to-date player instance.
|
||||
* @return The hooked player, or a more up-to-date instance.
|
||||
*/
|
||||
public abstract Player getUpdatedPlayer();
|
||||
|
||||
/**
|
||||
* Invoked when a delegated socket injector transfers the state of one injector to the next.
|
||||
* @param delegate - the new injector.
|
||||
*/
|
||||
public abstract void transferState(SocketInjector delegate);
|
||||
|
||||
/**
|
||||
* Set the real Bukkit player that we will use.
|
||||
* @param updatedPlayer - the real Bukkit player.
|
||||
*/
|
||||
public abstract void setUpdatedPlayer(Player updatedPlayer);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.sf.cglib.asm.*;
|
||||
|
||||
public class ClassAnalyser {
|
||||
/**
|
||||
* Represents a method in ASM.
|
||||
* <p>
|
||||
* Keep in mind that this may also invoke a constructor.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class AsmMethod {
|
||||
public enum AsmOpcodes {
|
||||
INVOKE_VIRTUAL,
|
||||
INVOKE_SPECIAL,
|
||||
INVOKE_STATIC,
|
||||
INVOKE_INTERFACE,
|
||||
INVOKE_DYNAMIC;
|
||||
|
||||
public static AsmOpcodes fromIntOpcode(int opcode) {
|
||||
switch (opcode) {
|
||||
case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
||||
case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
||||
case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
||||
case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
||||
case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
||||
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final AsmOpcodes opcode;
|
||||
private final String ownerClass;
|
||||
private final String methodName;
|
||||
private final String signature;
|
||||
|
||||
public AsmMethod(AsmOpcodes opcode, String ownerClass, String methodName, String signature) {
|
||||
this.opcode = opcode;
|
||||
this.ownerClass = ownerClass;
|
||||
this.methodName = methodName;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getOwnerName() {
|
||||
return ownerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the opcode used to invoke this method or constructor.
|
||||
* @return The opcode.
|
||||
*/
|
||||
public AsmOpcodes getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated owner class.
|
||||
* @return The owner class.
|
||||
* @throws ClassNotFoundException If the class was not found
|
||||
*/
|
||||
public Class<?> getOwnerClass() throws ClassNotFoundException {
|
||||
return AsmMethod.class.getClassLoader().loadClass(getOwnerName().replace('/', '.'));
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
private static final ClassAnalyser DEFAULT = new ClassAnalyser();
|
||||
|
||||
/**
|
||||
* Retrieve the default instance.
|
||||
* @return The default.
|
||||
*/
|
||||
public static ClassAnalyser getDefault() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every method calls in the given method.
|
||||
* @param method - the method to analyse.
|
||||
* @return The method calls.
|
||||
* @throws IOException Cannot access the parent class.
|
||||
*/
|
||||
public List<AsmMethod> getMethodCalls(Method method) throws IOException {
|
||||
return getMethodCalls(method.getDeclaringClass(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every method calls in the given method.
|
||||
* @param clazz - the parent class.
|
||||
* @param method - the method to analyse.
|
||||
* @return The method calls.
|
||||
* @throws IOException Cannot access the parent class.
|
||||
*/
|
||||
private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
||||
final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
|
||||
final List<AsmMethod> output = Lists.newArrayList();
|
||||
|
||||
// The method we are looking for
|
||||
final String methodName = method.getName();
|
||||
final String methodDescription = $Type.getMethodDescriptor(method);
|
||||
|
||||
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if (methodName.equals(name) && methodDescription.equals(desc)) {
|
||||
return new $MethodVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
|
||||
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, $ClassReader.EXPAND_FRAMES);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class ExactReflection {
|
||||
// The class we're actually representing
|
||||
private Class<?> source;
|
||||
private boolean forceAccess;
|
||||
|
||||
private ExactReflection(Class<?> source, boolean forceAccess) {
|
||||
this.source = Preconditions.checkNotNull(source, "source class cannot be NULL");
|
||||
this.forceAccess = forceAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an exact reflection instance from a given class.
|
||||
* @param source - the class we'll use.
|
||||
* @return A fuzzy reflection instance.
|
||||
*/
|
||||
public static ExactReflection fromClass(Class<?> source) {
|
||||
return fromClass(source, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an exact reflection instance from a given class.
|
||||
* @param source - the class we'll use.
|
||||
* @param forceAccess - whether or not to override scope restrictions.
|
||||
* @return A fuzzy reflection instance.
|
||||
*/
|
||||
public static ExactReflection fromClass(Class<?> source, boolean forceAccess) {
|
||||
return new ExactReflection(source, forceAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an exact reflection instance from an object.
|
||||
* @param reference - the object we'll use.
|
||||
* @return A fuzzy reflection instance that uses the class of the given object.
|
||||
*/
|
||||
public static ExactReflection fromObject(Object reference) {
|
||||
return new ExactReflection(reference.getClass(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an exact reflection instance from an object.
|
||||
* @param reference - the object we'll use.
|
||||
* @param forceAccess - whether or not to override scope restrictions.
|
||||
* @return A fuzzy reflection instance that uses the class of the given object.
|
||||
*/
|
||||
public static ExactReflection fromObject(Object reference, boolean forceAccess) {
|
||||
return new ExactReflection(reference.getClass(), forceAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first method in the class hierachy with the given name and parameters.
|
||||
* <p>
|
||||
* If {@link #isForceAccess()} is TRUE, we will also search for protected and private methods.
|
||||
* @param methodName - the method name to find, or NULL to look for everything.
|
||||
* @param parameters - the parameters.
|
||||
* @return The first matched method.
|
||||
* @throws IllegalArgumentException If we cannot find a method by this name.
|
||||
*/
|
||||
public Method getMethod(String methodName, Class<?>... parameters) {
|
||||
return getMethod(source, methodName, parameters);
|
||||
}
|
||||
|
||||
// For recursion
|
||||
private Method getMethod(Class<?> instanceClass, String methodName, Class<?>... parameters) {
|
||||
for (Method method : instanceClass.getDeclaredMethods()) {
|
||||
if ((forceAccess || Modifier.isPublic(method.getModifiers())) &&
|
||||
(methodName == null || method.getName().equals(methodName)) &&
|
||||
Arrays.equals(method.getParameterTypes(), parameters)) {
|
||||
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
// Search in every superclass
|
||||
if (instanceClass.getSuperclass() != null)
|
||||
return getMethod(instanceClass.getSuperclass(), methodName, parameters);
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Unable to find method %s (%s) in %s.", methodName, Arrays.asList(parameters), source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field in the class hierachy by the given name.
|
||||
* <p>
|
||||
* If {@link #isForceAccess()} is TRUE, we will also search for protected and private fields.
|
||||
* @param fieldName - the field name. Cannot be NULL.
|
||||
* @return The first matched field.
|
||||
*/
|
||||
public Field getField(String fieldName) {
|
||||
return getField(source, fieldName);
|
||||
}
|
||||
|
||||
// For recursion
|
||||
private Field getField(Class<?> instanceClass, @Nonnull String fieldName) {
|
||||
// Ignore access rules
|
||||
for (Field field : instanceClass.getDeclaredFields()) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively fild the correct field
|
||||
if (instanceClass.getSuperclass() != null)
|
||||
return getField(instanceClass.getSuperclass(), fieldName);
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Unable to find field %s in %s.", fieldName, source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an {@link ExactReflection} object where scope restrictions are ignored.
|
||||
* @return A copy of the current object.
|
||||
*/
|
||||
public ExactReflection forceAccess() {
|
||||
return new ExactReflection(source, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are overriding scope restrictions and will also find
|
||||
* private, protected or package members.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
public boolean isForceAccess() {
|
||||
return forceAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the source class we are searching.
|
||||
* @return The source.
|
||||
*/
|
||||
public Class<?> getSource() {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -1,527 +0,0 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for working with fields by reflection. Adapted and refactored from
|
||||
* the dormant [reflect] Commons sandbox component.
|
||||
* <p>
|
||||
* The ability is provided to break the scoping restrictions coded by the
|
||||
* programmer. This can allow fields to be changed that shouldn't be. This
|
||||
* facility should be used with care.
|
||||
*
|
||||
* @author Apache Software Foundation
|
||||
* @author Matt Benson
|
||||
* @since 2.5
|
||||
* @version $Id: FieldUtils.java 1057009 2011-01-09 19:48:06Z niallp $
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class FieldUtils {
|
||||
|
||||
/**
|
||||
* FieldUtils instances should NOT be constructed in standard programming.
|
||||
* <p>
|
||||
* This constructor is public to permit tools that require a JavaBean
|
||||
* instance to operate.
|
||||
*/
|
||||
public FieldUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an accessible <code>Field</code> by name respecting scope.
|
||||
* Superclasses/interfaces will be considered.
|
||||
*
|
||||
* @param cls the class to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @return the Field object
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
*/
|
||||
public static Field getField(Class cls, String fieldName) {
|
||||
Field field = getField(cls, fieldName, false);
|
||||
MemberUtils.setAccessibleWorkaround(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an accessible <code>Field</code> by name breaking scope if
|
||||
* requested. Superclasses/interfaces will be considered.
|
||||
*
|
||||
* @param cls the class to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @return the Field object
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
*/
|
||||
public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
|
||||
if (cls == null) {
|
||||
throw new IllegalArgumentException("The class must not be null");
|
||||
}
|
||||
if (fieldName == null) {
|
||||
throw new IllegalArgumentException("The field name must not be null");
|
||||
}
|
||||
// Sun Java 1.3 has a bugged implementation of getField hence we write
|
||||
// the
|
||||
// code ourselves
|
||||
|
||||
// getField() will return the Field object with the declaring class
|
||||
// set correctly to the class that declares the field. Thus requesting
|
||||
// the
|
||||
// field on a subclass will return the field from the superclass.
|
||||
//
|
||||
// priority order for lookup:
|
||||
// searchclass private/protected/package/public
|
||||
// superclass protected/package/public
|
||||
// private/different package blocks access to further superclasses
|
||||
// implementedinterface public
|
||||
|
||||
// check up the superclass hierarchy
|
||||
for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
|
||||
try {
|
||||
Field field = acls.getDeclaredField(fieldName);
|
||||
// getDeclaredField checks for non-public scopes as well
|
||||
// and it returns accurate results
|
||||
if (!Modifier.isPublic(field.getModifiers())) {
|
||||
if (forceAccess) {
|
||||
field.setAccessible(true);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return field;
|
||||
} catch (NoSuchFieldException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
// check the public interface case. This must be manually searched for
|
||||
// in case there is a public supersuperclass field hidden by a
|
||||
// private/package
|
||||
// superclass field.
|
||||
Field match = null;
|
||||
for (Iterator intf = getAllInterfaces(cls).iterator(); intf.hasNext();) {
|
||||
try {
|
||||
Field test = ((Class) intf.next()).getField(fieldName);
|
||||
if (match != null) {
|
||||
throw new IllegalArgumentException("Reference to field " + fieldName
|
||||
+ " is ambiguous relative to " + cls
|
||||
+ "; a matching field exists on two or more implemented interfaces.");
|
||||
}
|
||||
match = test;
|
||||
} catch (NoSuchFieldException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a <code>List</code> of all interfaces implemented by the given
|
||||
* class and its superclasses.</p>
|
||||
*
|
||||
* <p>The order is determined by looking through each interface in turn as
|
||||
* declared in the source file and following its hierarchy up. Then each
|
||||
* superclass is considered in the same way. Later duplicates are ignored,
|
||||
* so the order is maintained.</p>
|
||||
*
|
||||
* @param cls the class to look up, may be <code>null</code>
|
||||
* @return the <code>List</code> of interfaces in order,
|
||||
* <code>null</code> if null input
|
||||
*/
|
||||
private static List getAllInterfaces(Class cls) {
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
List<Class> list = new ArrayList<Class>();
|
||||
|
||||
while (cls != null) {
|
||||
Class[] interfaces = cls.getInterfaces();
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
if (list.contains(interfaces[i]) == false) {
|
||||
list.add(interfaces[i]);
|
||||
}
|
||||
List superInterfaces = getAllInterfaces(interfaces[i]);
|
||||
for (Iterator it = superInterfaces.iterator(); it.hasNext();) {
|
||||
Class intface = (Class) it.next();
|
||||
if (list.contains(intface) == false) {
|
||||
list.add(intface);
|
||||
}
|
||||
}
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an accessible static Field.
|
||||
*
|
||||
* @param field to read
|
||||
* @return the field value
|
||||
* @throws IllegalArgumentException if the field is null or not static
|
||||
* @throws IllegalAccessException if the field is not accessible
|
||||
*/
|
||||
public static Object readStaticField(Field field) throws IllegalAccessException {
|
||||
return readStaticField(field, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a static Field.
|
||||
*
|
||||
* @param field to read
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method.
|
||||
* @return the field value
|
||||
* @throws IllegalArgumentException if the field is null or not static
|
||||
* @throws IllegalAccessException if the field is not made accessible
|
||||
*/
|
||||
public static Object readStaticField(Field field, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("The field must not be null");
|
||||
}
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
|
||||
}
|
||||
return readField(field, (Object) null, forceAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the named public static field. Superclasses will be considered.
|
||||
*
|
||||
* @param cls the class to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @return the value of the field
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
* @throws IllegalAccessException if the field is not accessible
|
||||
*/
|
||||
public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException {
|
||||
return readStaticField(cls, fieldName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the named static field. Superclasses will be considered.
|
||||
*
|
||||
* @param cls the class to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @return the Field object
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
* @throws IllegalAccessException if the field is not made accessible
|
||||
*/
|
||||
public static Object readStaticField(Class cls, String fieldName, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
Field field = getField(cls, fieldName, forceAccess);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||
}
|
||||
// already forced access above, don't repeat it here:
|
||||
return readStaticField(field, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an accessible Field.
|
||||
*
|
||||
* @param field the field to use
|
||||
* @param target the object to call on, may be null for static fields
|
||||
* @return the field value
|
||||
* @throws IllegalArgumentException if the field is null
|
||||
* @throws IllegalAccessException if the field is not accessible
|
||||
*/
|
||||
public static Object readField(Field field, Object target) throws IllegalAccessException {
|
||||
return readField(field, target, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Field.
|
||||
*
|
||||
* @param field the field to use
|
||||
* @param target the object to call on, may be null for static fields
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method.
|
||||
* @return the field value
|
||||
* @throws IllegalArgumentException if the field is null
|
||||
* @throws IllegalAccessException if the field is not made accessible
|
||||
*/
|
||||
public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException("The field must not be null");
|
||||
|
||||
if (forceAccess && !field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
} else {
|
||||
MemberUtils.setAccessibleWorkaround(field);
|
||||
}
|
||||
return field.get(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the named public field. Superclasses will be considered.
|
||||
*
|
||||
* @param target the object to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @return the value of the field
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
* @throws IllegalAccessException if the named field is not public
|
||||
*/
|
||||
public static Object readField(Object target, String fieldName) throws IllegalAccessException {
|
||||
return readField(target, fieldName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the named field. Superclasses will be considered.
|
||||
*
|
||||
* @param target the object to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @return the field value
|
||||
* @throws IllegalArgumentException if the class or field name is null
|
||||
* @throws IllegalAccessException if the named field is not made accessible
|
||||
*/
|
||||
public static Object readField(Object target, String fieldName, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("target object must not be null");
|
||||
}
|
||||
Class cls = target.getClass();
|
||||
Field field = getField(cls, fieldName, forceAccess);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||
}
|
||||
// already forced access above, don't repeat it here:
|
||||
return readField(field, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a public static Field.
|
||||
*
|
||||
* @param field to write
|
||||
* @param value to set
|
||||
* @throws IllegalArgumentException if the field is null or not static
|
||||
* @throws IllegalAccessException if the field is not public or is final
|
||||
*/
|
||||
public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
|
||||
writeStaticField(field, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a static Field.
|
||||
*
|
||||
* @param field to write
|
||||
* @param value to set
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @throws IllegalArgumentException if the field is null or not static
|
||||
* @throws IllegalAccessException if the field is not made accessible or is
|
||||
* final
|
||||
*/
|
||||
public static void writeStaticField(Field field, Object value, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("The field must not be null");
|
||||
}
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
|
||||
}
|
||||
writeField(field, (Object) null, value, forceAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a named public static Field. Superclasses will be considered.
|
||||
*
|
||||
* @param cls Class on which the Field is to be found
|
||||
* @param fieldName to write
|
||||
* @param value to set
|
||||
* @throws IllegalArgumentException if the field cannot be located or is not
|
||||
* static
|
||||
* @throws IllegalAccessException if the field is not public or is final
|
||||
*/
|
||||
public static void writeStaticField(Class cls, String fieldName, Object value)
|
||||
throws IllegalAccessException {
|
||||
writeStaticField(cls, fieldName, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a named static Field. Superclasses will be considered.
|
||||
*
|
||||
* @param cls Class on which the Field is to be found
|
||||
* @param fieldName to write
|
||||
* @param value to set
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @throws IllegalArgumentException if the field cannot be located or is not
|
||||
* static
|
||||
* @throws IllegalAccessException if the field is not made accessible or is
|
||||
* final
|
||||
*/
|
||||
public static void writeStaticField(Class cls, String fieldName, Object value,
|
||||
boolean forceAccess) throws IllegalAccessException {
|
||||
Field field = getField(cls, fieldName, forceAccess);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||
}
|
||||
// already forced access above, don't repeat it here:
|
||||
writeStaticField(field, value);
|
||||
}
|
||||
|
||||
public static void writeStaticFinalField(Class<?> clazz, String fieldName, Object value, boolean forceAccess) throws Exception {
|
||||
Field field = getField(clazz, fieldName, forceAccess);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Cannot locate field " + fieldName + " in " + clazz);
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(null, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an accessible field.
|
||||
*
|
||||
* @param field to write
|
||||
* @param target the object to call on, may be null for static fields
|
||||
* @param value to set
|
||||
* @throws IllegalArgumentException if the field is null
|
||||
* @throws IllegalAccessException if the field is not accessible or is final
|
||||
*/
|
||||
public static void writeField(Field field, Object target, Object value)
|
||||
throws IllegalAccessException {
|
||||
writeField(field, target, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field.
|
||||
*
|
||||
* @param field to write
|
||||
* @param target the object to call on, may be null for static fields
|
||||
* @param value to set
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @throws IllegalArgumentException if the field is null
|
||||
* @throws IllegalAccessException if the field is not made accessible or is
|
||||
* final
|
||||
*/
|
||||
public static void writeField(Field field, Object target, Object value, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("The field must not be null");
|
||||
}
|
||||
if (forceAccess && !field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
} else {
|
||||
MemberUtils.setAccessibleWorkaround(field);
|
||||
}
|
||||
field.set(target, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a public field. Superclasses will be considered.
|
||||
*
|
||||
* @param target the object to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @param value to set
|
||||
* @throws IllegalArgumentException if <code>target</code> or
|
||||
* <code>fieldName</code> is null
|
||||
* @throws IllegalAccessException if the field is not accessible
|
||||
*/
|
||||
public static void writeField(Object target, String fieldName, Object value)
|
||||
throws IllegalAccessException {
|
||||
writeField(target, fieldName, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field. Superclasses will be considered.
|
||||
*
|
||||
* @param target the object to reflect, must not be null
|
||||
* @param fieldName the field name to obtain
|
||||
* @param value to set
|
||||
* @param forceAccess whether to break scope restrictions using the
|
||||
* <code>setAccessible</code> method. <code>False</code> will
|
||||
* only match public fields.
|
||||
* @throws IllegalArgumentException if <code>target</code> or
|
||||
* <code>fieldName</code> is null
|
||||
* @throws IllegalAccessException if the field is not made accessible
|
||||
*/
|
||||
public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
|
||||
throws IllegalAccessException {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("target object must not be null");
|
||||
}
|
||||
Class cls = target.getClass();
|
||||
Field field = getField(cls, fieldName, forceAccess);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName()
|
||||
+ "." + fieldName);
|
||||
}
|
||||
// already forced access above, don't repeat it here:
|
||||
writeField(field, target, value);
|
||||
}
|
||||
|
||||
// Useful member methods
|
||||
private static class MemberUtils {
|
||||
|
||||
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED
|
||||
| Modifier.PRIVATE;
|
||||
|
||||
public static void setAccessibleWorkaround(AccessibleObject o) {
|
||||
if (o == null || o.isAccessible()) {
|
||||
return;
|
||||
}
|
||||
Member m = (Member) o;
|
||||
if (Modifier.isPublic(m.getModifiers())
|
||||
&& isPackageAccess(m.getDeclaringClass().getModifiers())) {
|
||||
try {
|
||||
o.setAccessible(true);
|
||||
} catch (SecurityException e) { // NOPMD
|
||||
// ignore in favor of subsequent IllegalAccessException
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a given set of modifiers implies package access.
|
||||
*
|
||||
* @param modifiers to test
|
||||
* @return true unless package/protected/private modifier detected
|
||||
*/
|
||||
public static boolean isPackageAccess(int modifiers) {
|
||||
return (modifiers & ACCESS_TEST) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +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.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
|
||||
/**
|
||||
* Represents a traditional int field enum.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class IntEnum {
|
||||
|
||||
// Used to convert between IDs and names
|
||||
protected BiMap<Integer, String> members = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* Registers every declared integer field.
|
||||
*/
|
||||
public IntEnum() {
|
||||
registerAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers every public int field as a member.
|
||||
*/
|
||||
protected void registerAll() {
|
||||
try {
|
||||
// Register every int field
|
||||
for (Field entry : this.getClass().getFields()) {
|
||||
if (entry.getType().equals(int.class)) {
|
||||
registerMember(entry.getInt(this), entry.getName());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a member.
|
||||
* @param id - id of member.
|
||||
* @param name - name of member.
|
||||
*/
|
||||
protected void registerMember(int id, String name) {
|
||||
members.put(id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the given member exists.
|
||||
* @param id - the ID of the member to find.
|
||||
* @return TRUE if a member with the given ID exists, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasMember(int id) {
|
||||
return members.containsKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ID of the member with the given name.
|
||||
* @param name - name of member to retrieve.
|
||||
* @return ID of the member, or NULL if not found.
|
||||
*/
|
||||
public Integer valueOf(String name) {
|
||||
return members.inverse().get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the member with the given id.
|
||||
* @param id - id of the member to retrieve.
|
||||
* @return Declared name of the member, or NULL if not found.
|
||||
*/
|
||||
public String getDeclaredName(Integer id) {
|
||||
return members.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ID of every registered member.
|
||||
* @return Enumeration of every value.
|
||||
*/
|
||||
public Set<Integer> values() {
|
||||
return new HashSet<Integer>(members.keySet());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,709 +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.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.error.PluginContext;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.instances.BannedGenerator;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.InstanceProvider;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Provides list-oriented access to the fields of a Minecraft packet.
|
||||
* <p>
|
||||
* Implemented by using reflection. Use a CompiledStructureModifier, if speed is essential.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TField> Type of the fields to retrieve.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class StructureModifier<TField> {
|
||||
|
||||
// Object and its type
|
||||
protected Class targetType;
|
||||
protected Object target;
|
||||
|
||||
// Converter. May be NULL.
|
||||
protected EquivalentConverter<TField> converter;
|
||||
|
||||
// The fields to read in order
|
||||
protected Class fieldType;
|
||||
protected List<Field> data = new ArrayList<Field>();
|
||||
|
||||
// Improved default values
|
||||
protected Map<Field, Integer> defaultFields;
|
||||
|
||||
// Cache of previous types
|
||||
protected Map<Class, StructureModifier> subtypeCache;
|
||||
|
||||
// Whether or subclasses should handle conversion
|
||||
protected boolean customConvertHandling;
|
||||
|
||||
// Whether or not to automatically compile the structure modifier
|
||||
protected boolean useStructureCompiler;
|
||||
|
||||
// Instance generator we wil use
|
||||
private static DefaultInstances DEFAULT_GENERATOR = getDefaultGenerator();
|
||||
|
||||
private static DefaultInstances getDefaultGenerator() {
|
||||
List<InstanceProvider> providers = Lists.newArrayList();
|
||||
|
||||
// Prevent certain classes from being generated
|
||||
providers.add(new BannedGenerator(MinecraftReflection.getItemStackClass(), MinecraftReflection.getBlockClass()));
|
||||
providers.addAll(DefaultInstances.DEFAULT.getRegistered());
|
||||
return DefaultInstances.fromCollection(providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
*/
|
||||
public StructureModifier(Class targetType) {
|
||||
this(targetType, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
* @param useStructureCompiler - whether or not to use a structure compiler.
|
||||
*/
|
||||
public StructureModifier(Class targetType, boolean useStructureCompiler) {
|
||||
this(targetType, null, true, useStructureCompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
* @param superclassExclude - a superclass to exclude.
|
||||
* @param requireDefault - whether or not we will be using writeDefaults().
|
||||
*/
|
||||
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault) {
|
||||
this(targetType, superclassExclude, requireDefault, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
* @param superclassExclude - a superclass to exclude.
|
||||
* @param requireDefault - whether or not we will be using writeDefaults().
|
||||
* @param useStructureCompiler - whether or not to automatically compile this structure modifier.
|
||||
*/
|
||||
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault, boolean useStructureCompiler) {
|
||||
List<Field> fields = getFields(targetType, superclassExclude);
|
||||
Map<Field, Integer> defaults = requireDefault ? generateDefaultFields(fields) : new HashMap<Field, Integer>();
|
||||
|
||||
initialize(targetType, Object.class, fields, defaults, null,
|
||||
new ConcurrentHashMap<Class, StructureModifier>(), useStructureCompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumers of this method should call "initialize".
|
||||
*/
|
||||
protected StructureModifier() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize using the same field types.
|
||||
* @param other - information to set.
|
||||
*/
|
||||
protected void initialize(StructureModifier<TField> other) {
|
||||
initialize(other.targetType, other.fieldType, other.data,
|
||||
other.defaultFields, other.converter, other.subtypeCache,
|
||||
other.useStructureCompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize every field of this class.
|
||||
* @param targetType - type of the object we're reading and writing from.
|
||||
* @param fieldType - the common type of the fields we're modifying.
|
||||
* @param data - list of fields to modify.
|
||||
* @param defaultFields - list of fields that will be automatically initialized.
|
||||
* @param converter - converts between the common field type and the actual type the consumer expects.
|
||||
* @param subTypeCache - a structure modifier cache.
|
||||
*/
|
||||
protected void initialize(Class targetType, Class fieldType,
|
||||
List<Field> data, Map<Field, Integer> defaultFields,
|
||||
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache) {
|
||||
initialize(targetType, fieldType, data, defaultFields, converter, subTypeCache, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize every field of this class.
|
||||
* @param targetType - type of the object we're reading and writing from.
|
||||
* @param fieldType - the common type of the fields we're modifying.
|
||||
* @param data - list of fields to modify.
|
||||
* @param defaultFields - list of fields that will be automatically initialized.
|
||||
* @param converter - converts between the common field type and the actual type the consumer expects.
|
||||
* @param subTypeCache - a structure modifier cache.
|
||||
* @param useStructureCompiler - whether or not to automatically compile this structure modifier.
|
||||
*/
|
||||
protected void initialize(Class targetType, Class fieldType,
|
||||
List<Field> data, Map<Field, Integer> defaultFields,
|
||||
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache,
|
||||
boolean useStructureCompiler) {
|
||||
|
||||
this.targetType = targetType;
|
||||
this.fieldType = fieldType;
|
||||
this.data = data;
|
||||
this.defaultFields = defaultFields;
|
||||
this.converter = converter;
|
||||
this.subtypeCache = subTypeCache;
|
||||
this.useStructureCompiler = useStructureCompiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value of a field given its index.
|
||||
* <p>
|
||||
* Note: This method is prone to exceptions (there are currently 5 total throw statements). It is recommended that you
|
||||
* use {@link #readSafely(int)}, which returns {@code null} if the field doesn't exist, instead of throwing an exception.
|
||||
*
|
||||
* @param fieldIndex - index of the field.
|
||||
* @return Value of the field.
|
||||
* @throws FieldAccessException if the field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
public TField read(int fieldIndex) throws FieldAccessException {
|
||||
try {
|
||||
return readInternal(fieldIndex);
|
||||
} catch (FieldAccessException ex) {
|
||||
String plugin = PluginContext.getPluginCaller(ex);
|
||||
if (ProtocolLibrary.INCOMPATIBLE.contains(plugin)) {
|
||||
ProtocolLogger.log(Level.WARNING, "Encountered an exception caused by incompatible plugin {0}.", plugin);
|
||||
ProtocolLogger.log(Level.WARNING, "It is advised that you remove it.");
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private TField readInternal(int fieldIndex) throws FieldAccessException {
|
||||
if (target == null)
|
||||
throw new IllegalStateException("Cannot read from a null target!");
|
||||
|
||||
if (fieldIndex < 0)
|
||||
throw new FieldAccessException(String.format("Field index (%s) cannot be negative.", fieldIndex));
|
||||
|
||||
if (data.size() == 0)
|
||||
throw new FieldAccessException(String.format("No field with type %s exists in class %s.", fieldType.getName(),
|
||||
target.getClass().getSimpleName()));
|
||||
|
||||
if (fieldIndex >= data.size())
|
||||
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
|
||||
|
||||
try {
|
||||
Object result = FieldUtils.readField(data.get(fieldIndex), target, true);
|
||||
|
||||
// Use the converter, if we have it
|
||||
if (needConversion()) {
|
||||
return converter.getSpecific(result);
|
||||
} else {
|
||||
return (TField) result;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot read field due to a security limitation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value of a field only if it exists. If the field does not exist, {@code null} is returned.
|
||||
* <p>
|
||||
* As its name implies, this method is a much safer alternative to {@link #read(int)}.
|
||||
* In addition to throwing less exceptions and thereby causing less console spam, this
|
||||
* method makes providing backwards compatiblity signficiantly easier, as shown below:
|
||||
*
|
||||
* <pre><code>
|
||||
* BlockPosition position = packet.getBlockPositionModifier().readSafely(0);
|
||||
* if (position != null) {
|
||||
* // Handle 1.8+
|
||||
* } else {
|
||||
* // Handle 1.7-
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* @param fieldIndex - index of the field.
|
||||
* @return Value of the field, or NULL if it doesn't exist.
|
||||
* @throws FieldAccessException if the field cannot be accessed under the current security constraints.
|
||||
*/
|
||||
public TField readSafely(int fieldIndex) throws FieldAccessException {
|
||||
if (fieldIndex >= 0 && fieldIndex < data.size()) {
|
||||
return read(fieldIndex);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the value of a field only if it exists. If the field does not exist, an empty {@link Optional} is returned.
|
||||
* <p>
|
||||
* This method has the same functionality as {@link #readSafely(int)}, but enforces null checks by way of an Optional.
|
||||
* It will eventually become the preferred method of reading fields.
|
||||
*
|
||||
* @param fieldIndex index of the field
|
||||
* @return An optional that may contain the value of the field
|
||||
* @see #readSafely(int)
|
||||
*/
|
||||
public Optional<TField> optionRead(int fieldIndex) {
|
||||
try {
|
||||
return Optional.ofNullable(read(fieldIndex));
|
||||
} catch (FieldAccessException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not a field is read-only (final).
|
||||
* @param fieldIndex - index of the field.
|
||||
* @return TRUE if the field by the given index is read-only, FALSE otherwise.
|
||||
*/
|
||||
public boolean isReadOnly(int fieldIndex) {
|
||||
return Modifier.isFinal(getField(fieldIndex).getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given field is public or not.
|
||||
* @param fieldIndex - field index.
|
||||
* @return TRUE if the field is public, FALSE otherwise.
|
||||
*/
|
||||
public boolean isPublic(int fieldIndex) {
|
||||
return Modifier.isPublic(getField(fieldIndex).getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not a field should be treated as read only.
|
||||
* <p>
|
||||
* Note that changing the read-only state to TRUE will only work if the current
|
||||
* field was recently read-only or the current structure modifier hasn't been compiled yet.
|
||||
*
|
||||
* @param fieldIndex - index of the field.
|
||||
* @param value - TRUE if this field should be read only, FALSE otherwise.
|
||||
* @throws FieldAccessException If we cannot modify the read-only status.
|
||||
*/
|
||||
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
|
||||
if (fieldIndex < 0 || fieldIndex >= data.size())
|
||||
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
|
||||
|
||||
try {
|
||||
StructureModifier.setFinalState(data.get(fieldIndex), value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot write read only status due to a security limitation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the final status of a field.
|
||||
* @param field - the field to change.
|
||||
* @param isReadOnly - TRUE if the field should be read only, FALSE otherwise.
|
||||
* @throws IllegalAccessException If an error occured.
|
||||
*/
|
||||
protected static void setFinalState(Field field, boolean isReadOnly) throws IllegalAccessException {
|
||||
if (isReadOnly)
|
||||
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() | Modifier.FINAL, true);
|
||||
else
|
||||
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() & ~Modifier.FINAL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the value of a field given its index.
|
||||
* @param fieldIndex - index of the field.
|
||||
* @param value - new value of the field.
|
||||
* @return This structure modifier - for chaining.
|
||||
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
public StructureModifier<TField> write(int fieldIndex, TField value) throws FieldAccessException {
|
||||
try {
|
||||
return writeInternal(fieldIndex, value);
|
||||
} catch (FieldAccessException ex) {
|
||||
String plugin = PluginContext.getPluginCaller(ex);
|
||||
if (ProtocolLibrary.INCOMPATIBLE.contains(plugin)) {
|
||||
ProtocolLogger.log(Level.WARNING, "Encountered an exception caused by incompatible plugin {0}.", plugin);
|
||||
ProtocolLogger.log(Level.WARNING, "It is advised that you remove it.");
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private StructureModifier<TField> writeInternal(int fieldIndex, TField value) throws FieldAccessException {
|
||||
if (target == null)
|
||||
throw new IllegalStateException("Cannot read from a null target!");
|
||||
|
||||
if (fieldIndex < 0)
|
||||
throw new FieldAccessException(String.format("Field index (%s) cannot be negative.", fieldIndex));
|
||||
|
||||
if (data.size() == 0)
|
||||
throw new FieldAccessException(String.format("No field with type %s exists in class %s.", fieldType.getName(),
|
||||
target.getClass().getSimpleName()));
|
||||
|
||||
if (fieldIndex >= data.size())
|
||||
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
|
||||
|
||||
// Use the converter, if it exists
|
||||
Object obj = needConversion() ? converter.getGeneric(value) : value;
|
||||
|
||||
try {
|
||||
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot read field due to a security limitation.", e);
|
||||
}
|
||||
|
||||
// Make this method chainable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the type of a specified field.
|
||||
* @param index - the index.
|
||||
* @return The type of the given field.
|
||||
*/
|
||||
protected Class<?> getFieldType(int index) {
|
||||
return data.get(index).getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not we should use the converter instance.
|
||||
* @return TRUE if we should, FALSE otherwise.
|
||||
*/
|
||||
private final boolean needConversion() {
|
||||
return converter != null && !customConvertHandling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the value of a given field IF and ONLY if it exists.
|
||||
* @param fieldIndex - index of the potential field.
|
||||
* @param value - new value of the field.
|
||||
* @return This structure modifer - for chaining.
|
||||
* @throws FieldAccessException The field cannot be accessed under the current security contraints.
|
||||
*/
|
||||
public StructureModifier<TField> writeSafely(int fieldIndex, TField value) throws FieldAccessException {
|
||||
if (fieldIndex >= 0 && fieldIndex < data.size()) {
|
||||
write(fieldIndex, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correctly modifies the value of a field.
|
||||
* @param fieldIndex - index of the field to modify.
|
||||
* @param select - the function that modifies the field value.
|
||||
* @return This structure modifier - for chaining.
|
||||
* @throws FieldAccessException The field cannot be accessed under the current security contraints.
|
||||
*/
|
||||
public StructureModifier<TField> modify(int fieldIndex, Function<TField, TField> select) throws FieldAccessException {
|
||||
TField value = read(fieldIndex);
|
||||
return write(fieldIndex, select.apply(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a structure modifier that only reads and writes fields of a given type.
|
||||
* @param <T> Type
|
||||
* @param fieldType - the type, or supertype, of every field to modify.
|
||||
* @return A structure modifier for fields of this type.
|
||||
*/
|
||||
public <T> StructureModifier<T> withType(Class fieldType) {
|
||||
return withType(fieldType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all non-primitive fields to a more fitting default value. See {@link DefaultInstances#getDefault(Class)}.
|
||||
* @return The current structure modifier - for chaining.
|
||||
* @throws FieldAccessException If we're unable to write to the fields due to a security limitation.
|
||||
*/
|
||||
public StructureModifier<TField> writeDefaults() throws FieldAccessException {
|
||||
DefaultInstances generator = DefaultInstances.DEFAULT;
|
||||
|
||||
// Write a default instance to every field
|
||||
for (Field field : defaultFields.keySet()) {
|
||||
try {
|
||||
// Special case for Spigot's custom chat components
|
||||
// They must be null or messages will be blank
|
||||
if (field.getType().getCanonicalName().equals("net.md_5.bungee.api.chat.BaseComponent[]")) {
|
||||
FieldUtils.writeField(field, target, null, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldUtils.writeField(field, target, generator.getDefault(field.getType()), true);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot write to field due to a security limitation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a structure modifier that only reads and writes fields of a given type.
|
||||
* @param <T> Type
|
||||
* @param fieldType - the type, or supertype, of every field to modify.
|
||||
* @param converter - converts objects into the given type.
|
||||
* @return A structure modifier for fields of this type.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
|
||||
if (fieldType == null) {
|
||||
// It's not supported in this version, so return an empty modifier
|
||||
return new StructureModifier<T>() {
|
||||
@Override
|
||||
public T read(int index) { return null; }
|
||||
|
||||
@Override
|
||||
public StructureModifier<T> write(int index, T value) { return this; }
|
||||
};
|
||||
}
|
||||
|
||||
StructureModifier<T> result = subtypeCache.get(fieldType);
|
||||
|
||||
// Do we need to update the cache?
|
||||
if (result == null) {
|
||||
List<Field> filtered = new ArrayList<Field>();
|
||||
Map<Field, Integer> defaults = new HashMap<Field, Integer>();
|
||||
int index = 0;
|
||||
|
||||
for (Field field : data) {
|
||||
if (fieldType.isAssignableFrom(field.getType())) {
|
||||
filtered.add(field);
|
||||
|
||||
// Don't use the original index
|
||||
if (defaultFields.containsKey(field))
|
||||
defaults.put(field, index);
|
||||
}
|
||||
|
||||
// Keep track of the field index
|
||||
index++;
|
||||
}
|
||||
|
||||
// Cache structure modifiers
|
||||
result = withFieldType(fieldType, filtered, defaults);
|
||||
|
||||
subtypeCache.put(fieldType, result);
|
||||
|
||||
// Automatically compile the structure modifier
|
||||
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
|
||||
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
|
||||
}
|
||||
|
||||
// Add the target too
|
||||
result = result.withTarget(target);
|
||||
|
||||
// And the converter, if it's needed
|
||||
if (!Objects.equal(result.converter, converter)) {
|
||||
result = result.withConverter(converter);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the common type of each field.
|
||||
* @return Common type of each field.
|
||||
*/
|
||||
public Class getFieldType() {
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of the object we're modifying.
|
||||
* @return Type of the object.
|
||||
*/
|
||||
public Class getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object we're currently modifying.
|
||||
* @return Object we're modifying.
|
||||
*/
|
||||
public Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of readable types.
|
||||
* @return Readable types.
|
||||
*/
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new structure modifier for the new field type.
|
||||
* @param <T> Type
|
||||
* @param fieldType - common type of each field.
|
||||
* @param filtered - list of fields after filtering the original modifier.
|
||||
* @param defaults - list of default values after filtering the original.
|
||||
* @return A new structure modifier.
|
||||
*/
|
||||
protected <T> StructureModifier<T> withFieldType(
|
||||
Class fieldType, List<Field> filtered, Map<Field, Integer> defaults) {
|
||||
return withFieldType(fieldType, filtered, defaults, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new structure modifier for the new field type.
|
||||
* @param <T> Type
|
||||
* @param fieldType - common type of each field.
|
||||
* @param filtered - list of fields after filtering the original modifier.
|
||||
* @param defaults - list of default values after filtering the original.
|
||||
* @param converter - the new converter, or NULL.
|
||||
* @return A new structure modifier.
|
||||
*/
|
||||
protected <T> StructureModifier<T> withFieldType(
|
||||
Class fieldType, List<Field> filtered,
|
||||
Map<Field, Integer> defaults, EquivalentConverter<T> converter) {
|
||||
|
||||
StructureModifier<T> result = new StructureModifier<T>();
|
||||
result.initialize(targetType, fieldType, filtered, defaults,
|
||||
converter, new ConcurrentHashMap<Class, StructureModifier>(),
|
||||
useStructureCompiler);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a structure modifier of the same type for a different object target.
|
||||
* @param target - different target of the same type.
|
||||
* @return Structure modifier with the new target.
|
||||
*/
|
||||
public StructureModifier<TField> withTarget(Object target) {
|
||||
StructureModifier<TField> copy = new StructureModifier<TField>();
|
||||
|
||||
// Create a new instance
|
||||
copy.initialize(this);
|
||||
copy.target = target;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a structure modifier with the same type and target, but using a new object converter.
|
||||
* @param converter - the object converter to use.
|
||||
* @return Structure modifier with the new converter.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> StructureModifier<T> withConverter(EquivalentConverter<T> converter) {
|
||||
StructureModifier copy = withTarget(target);
|
||||
|
||||
copy.setConverter(converter);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current object converter. Should only be called during construction.
|
||||
* @param converter - current object converter.
|
||||
*/
|
||||
protected void setConverter(EquivalentConverter<TField> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of the fields matching the constraints of this structure modifier.
|
||||
* @return List of fields.
|
||||
*/
|
||||
public List<Field> getFields() {
|
||||
return ImmutableList.copyOf(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field by index.
|
||||
* @param fieldIndex - index of the field to retrieve.
|
||||
* @return The field represented with the given index.
|
||||
* @throws IllegalArgumentException If no field with the given index can be found.
|
||||
*/
|
||||
public Field getField(int fieldIndex) {
|
||||
if (fieldIndex < 0 || fieldIndex >= data.size())
|
||||
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
|
||||
|
||||
return data.get(fieldIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every value stored in the fields of the current type.
|
||||
* @return Every field value.
|
||||
* @throws FieldAccessException Unable to access one or all of the fields
|
||||
*/
|
||||
public List<TField> getValues() throws FieldAccessException {
|
||||
List<TField> values = new ArrayList<TField>();
|
||||
|
||||
for (int i = 0; i < size(); i++) {
|
||||
values.add(read(i));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
// Used to generate plausible default values
|
||||
private static Map<Field, Integer> generateDefaultFields(List<Field> fields) {
|
||||
|
||||
Map<Field, Integer> requireDefaults = new HashMap<Field, Integer>();
|
||||
DefaultInstances generator = DEFAULT_GENERATOR;
|
||||
int index = 0;
|
||||
|
||||
for (Field field : fields) {
|
||||
Class<?> type = field.getType();
|
||||
int modifier = field.getModifiers();
|
||||
|
||||
// First, ignore primitive fields and final fields
|
||||
if (!type.isPrimitive() && !Modifier.isFinal(modifier)) {
|
||||
// Next, see if we actually can generate a default value
|
||||
if (generator.getDefault(type) != null) {
|
||||
// If so, require it
|
||||
requireDefaults.put(field, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment field index
|
||||
index++;
|
||||
}
|
||||
|
||||
return requireDefaults;
|
||||
}
|
||||
|
||||
// Used to filter out irrelevant fields
|
||||
private static List<Field> getFields(Class type, Class superclassExclude) {
|
||||
List<Field> result = new ArrayList<Field>();
|
||||
|
||||
// Retrieve every private and public field
|
||||
for (Field field : FuzzyReflection.fromClass(type, true).getDeclaredFields(superclassExclude)) {
|
||||
int mod = field.getModifiers();
|
||||
|
||||
// Ignore static and "abstract packet" fields
|
||||
if (!Modifier.isStatic(mod) &&
|
||||
(superclassExclude == null || !field.getDeclaringClass().equals(superclassExclude)
|
||||
)) {
|
||||
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StructureModifier[fieldType=" + fieldType + ", data=" + data + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,243 +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.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents a field that will revert to its original state when this class is garbaged collected.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class VolatileField {
|
||||
private FieldAccessor accessor;
|
||||
private Object container;
|
||||
|
||||
// The current and previous values
|
||||
private Object previous;
|
||||
private Object current;
|
||||
|
||||
// Whether or not we must reset or load
|
||||
private boolean previousLoaded;
|
||||
private boolean currentSet;
|
||||
|
||||
// Whether or not to break access restrictions
|
||||
private boolean forceAccess;
|
||||
|
||||
/**
|
||||
* Initializes a volatile field with an associated object.
|
||||
* @param field - the field.
|
||||
* @param container - the object this field belongs to.
|
||||
*/
|
||||
public VolatileField(Field field, Object container) {
|
||||
this.accessor = Accessors.getFieldAccessor(field);
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a volatile field with an associated object.
|
||||
* @param field - the field.
|
||||
* @param container - the object this field belongs to.
|
||||
* @param forceAccess - whether or not to override any scope restrictions.
|
||||
*/
|
||||
public VolatileField(Field field, Object container, boolean forceAccess) {
|
||||
this.accessor = Accessors.getFieldAccessor(field, true);
|
||||
this.container = container;
|
||||
this.forceAccess = forceAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a volatile field with the given accessor and associated object.
|
||||
* @param accessor - the field accessor.
|
||||
* @param container - the object this field belongs to.
|
||||
*/
|
||||
public VolatileField(FieldAccessor accessor, Object container) {
|
||||
this.accessor = accessor;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current field.
|
||||
* @return The stored field.
|
||||
*/
|
||||
public Field getField() {
|
||||
return accessor.getField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object the field is stored.
|
||||
* @return The reference object.
|
||||
*/
|
||||
public Object getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves whether or not not to override any scope restrictions.
|
||||
* @return TRUE if we override scope, FALSE otherwise.
|
||||
*/
|
||||
public boolean isForceAccess() {
|
||||
return forceAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not not to override any scope restrictions.
|
||||
* @param forceAccess - TRUE if we override scope, FALSE otherwise.
|
||||
*/
|
||||
public void setForceAccess(boolean forceAccess) {
|
||||
this.forceAccess = forceAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current field value.
|
||||
* @return The current field value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
// Retrieve the correct value
|
||||
if (!currentSet) {
|
||||
ensureLoaded();
|
||||
return previous;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the field value before the previous setValue(), unless saveValue() has been called.
|
||||
* @return Previous value.
|
||||
*/
|
||||
public Object getOldValue() {
|
||||
ensureLoaded();
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current value. This will be reverted unless saveValue() is called.
|
||||
* @param newValue - new field value.
|
||||
*/
|
||||
public void setValue(Object newValue) {
|
||||
// Remember to safe the previous value
|
||||
ensureLoaded();
|
||||
|
||||
writeFieldValue(newValue);
|
||||
current = newValue;
|
||||
currentSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reapply the current changed value.
|
||||
* <p>
|
||||
* Also refresh the previously set value.
|
||||
*/
|
||||
public void refreshValue() {
|
||||
Object fieldValue = readFieldValue();
|
||||
|
||||
if (currentSet) {
|
||||
// If they differ, we need to set them again
|
||||
if (!Objects.equal(current, fieldValue)) {
|
||||
previous = readFieldValue();
|
||||
previousLoaded = true;
|
||||
writeFieldValue(current);
|
||||
}
|
||||
} else if (previousLoaded) {
|
||||
// Update that too
|
||||
previous = fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the current value is still set after this class has been garbaged collected.
|
||||
*/
|
||||
public void saveValue() {
|
||||
previous = current;
|
||||
currentSet = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert to the previously set value.
|
||||
*/
|
||||
public void revertValue() {
|
||||
// Reset value if it hasn't been changed by anyone else
|
||||
if (currentSet) {
|
||||
if (getValue() == current) {
|
||||
setValue(previous);
|
||||
currentSet = false;
|
||||
} else {
|
||||
// This can be a bad sign
|
||||
ProtocolLogger.log("Unable to switch {0} to {1}. Expected {2}, but got {3}.", getField().toGenericString(), previous, current, getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a synchronized version of the current field.
|
||||
* @return A synchronized volatile field.
|
||||
*/
|
||||
public VolatileField toSynchronized() {
|
||||
return new VolatileField(Accessors.getSynchronized(accessor), container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not we'll need to revert the value.
|
||||
* @return True if it is set, false if not.
|
||||
*/
|
||||
public boolean isCurrentSet() {
|
||||
return currentSet;
|
||||
}
|
||||
|
||||
private void ensureLoaded() {
|
||||
// Load the value if we haven't already
|
||||
if (!previousLoaded) {
|
||||
previous = readFieldValue();
|
||||
previousLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the content of the underlying field.
|
||||
* @return The field value.
|
||||
*/
|
||||
private Object readFieldValue() {
|
||||
return accessor.get(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given value to the underlying field.
|
||||
* @param newValue - the new value.
|
||||
*/
|
||||
private void writeFieldValue(Object newValue) {
|
||||
accessor.set(container, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
revertValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VolatileField [accessor=" + accessor + ", container=" + container + ", previous="
|
||||
+ previous + ", current=" + current + ", previousLoaded=" + previousLoaded
|
||||
+ ", currentSet=" + currentSet + ", forceAccess=" + forceAccess + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.reflect.ExactReflection;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
public final class Accessors {
|
||||
/**
|
||||
* Represents a field accessor that synchronizes access to the underlying field.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static final class SynchronizedFieldAccessor implements FieldAccessor {
|
||||
private final FieldAccessor accessor;
|
||||
private SynchronizedFieldAccessor(FieldAccessor accessor) {
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
Object lock = accessor.get(instance);
|
||||
|
||||
if (lock != null) {
|
||||
synchronized (lock) {
|
||||
accessor.set(instance, value);
|
||||
}
|
||||
} else {
|
||||
accessor.set(instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
return accessor.get(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return accessor.getField();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an accessor for the first field of the given type.
|
||||
* @param instanceClass - the type of the instance to retrieve.
|
||||
* @param fieldClass - type of the field to retrieve.
|
||||
* @param forceAccess - whether or not to look for private and protected fields.
|
||||
* @return The field accessor.
|
||||
* @throws IllegalArgumentException If the field cannot be found.
|
||||
*/
|
||||
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||
// Get a field accessor
|
||||
Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass);
|
||||
return Accessors.getFieldAccessor(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an accessor (in declared order) for every field of the givne type.
|
||||
* @param instanceClass - the type of the instance to retrieve.
|
||||
* @param fieldClass - type of the field(s) to retrieve.
|
||||
* @param forceAccess - whether or not to look for private and protected fields.
|
||||
* @return The accessors.
|
||||
*/
|
||||
public static FieldAccessor[] getFieldAccessorArray(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
|
||||
List<Field> fields = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldListByType(fieldClass);
|
||||
FieldAccessor[] accessors = new FieldAccessor[fields.size()];
|
||||
|
||||
for (int i = 0; i < accessors.length; i++) {
|
||||
accessors[i] = getFieldAccessor(fields.get(i));
|
||||
}
|
||||
return accessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an accessor for the first field of the given type.
|
||||
* @param instanceClass - the type of the instance to retrieve.
|
||||
* @param fieldName - name of the field to retrieve.
|
||||
* @param forceAccess - whether or not to look for private and protected fields.
|
||||
* @return The value of that field.
|
||||
* @throws IllegalArgumentException If the field cannot be found.
|
||||
*/
|
||||
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, String fieldName, boolean forceAccess) {
|
||||
return Accessors.getFieldAccessor(ExactReflection.fromClass(instanceClass, true).getField(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor from a given field that uses unchecked exceptions.
|
||||
* @param field - the field.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static FieldAccessor getFieldAccessor(final Field field) {
|
||||
return Accessors.getFieldAccessor(field, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor from a given field that uses unchecked exceptions.
|
||||
* @param field - the field.
|
||||
* @param forceAccess - whether or not to skip Java access checking.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static FieldAccessor getFieldAccessor(final Field field, boolean forceAccess) {
|
||||
field.setAccessible(true);
|
||||
return new DefaultFieldAccessor(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor for a field with the given name and equivalent type, or NULL.
|
||||
* @param clazz - the declaration class.
|
||||
* @param fieldName - the field name.
|
||||
* @param fieldType - assignable field type.
|
||||
* @return The field accessor, or NULL if not found.
|
||||
*/
|
||||
public static FieldAccessor getFieldAcccessorOrNull(Class<?> clazz, String fieldName, Class<?> fieldType) {
|
||||
try {
|
||||
FieldAccessor accessor = Accessors.getFieldAccessor(clazz, fieldName, true);
|
||||
|
||||
// Verify the type
|
||||
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
|
||||
return accessor;
|
||||
}
|
||||
return null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method accessor for a field with the given name and equivalent type, or NULL.
|
||||
* @param clazz - the declaration class.
|
||||
* @param methodName - the method name.
|
||||
* @return The method accessor, or NULL if not found.
|
||||
*/
|
||||
public static MethodAccessor getMethodAcccessorOrNull(Class<?> clazz, String methodName) {
|
||||
try {
|
||||
return Accessors.getMethodAccessor(clazz, methodName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific constructor in a class.
|
||||
* @param clazz - the class.
|
||||
* @param parameters - the signature of the constructor to find.
|
||||
* @return The constructor, or NULL if not found.
|
||||
*/
|
||||
public static ConstructorAccessor getConstructorAccessorOrNull(Class<?> clazz, Class<?>... parameters) {
|
||||
try {
|
||||
return Accessors.getConstructorAccessor(clazz, parameters);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null; // Not found
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor that will cache the content of the field.
|
||||
* <p>
|
||||
* Note that we don't check if the underlying field has changed after the value has been cached,
|
||||
* so it's best to use this on final fields.
|
||||
* @param inner - the accessor.
|
||||
* @return A cached field accessor.
|
||||
*/
|
||||
public static FieldAccessor getCached(final FieldAccessor inner) {
|
||||
return new FieldAccessor() {
|
||||
private final Object EMPTY = new Object();
|
||||
private volatile Object value = EMPTY;
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
inner.set(instance, value);
|
||||
update(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
Object cache = value;
|
||||
|
||||
if (cache != EMPTY)
|
||||
return cache;
|
||||
return update(inner.get(instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cached value.
|
||||
* @param value - the value to cache.
|
||||
* @return The cached value.
|
||||
*/
|
||||
private Object update(Object value) {
|
||||
return this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return inner.getField();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor where the write operation is synchronized on the current field value.
|
||||
* @param accessor - the accessor.
|
||||
* @return The field accessor.
|
||||
*/
|
||||
public static FieldAccessor getSynchronized(final FieldAccessor accessor) {
|
||||
// Only wrap once
|
||||
if (accessor instanceof SynchronizedFieldAccessor)
|
||||
return accessor;
|
||||
return new SynchronizedFieldAccessor(accessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method accessor that always return a constant value, regardless if input.
|
||||
* @param returnValue - the constant return value.
|
||||
* @param method - the method.
|
||||
* @return A constant method accessor.
|
||||
*/
|
||||
public static MethodAccessor getConstantAccessor(final Object returnValue, final Method method) {
|
||||
return new MethodAccessor() {
|
||||
@Override
|
||||
public Object invoke(Object target, Object... args) {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method accessor for a method with the given name and signature.
|
||||
* @param instanceClass - the parent class.
|
||||
* @param methodName - the method name.
|
||||
* @param parameters - the parameters.
|
||||
* @return The method accessor.
|
||||
*/
|
||||
public static MethodAccessor getMethodAccessor(Class<?> instanceClass, String methodName, Class<?>... parameters) {
|
||||
return new DefaultMethodAccessor(ExactReflection.fromClass(instanceClass, true).getMethod(methodName, parameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method accessor for a particular method, avoding checked exceptions.
|
||||
* @param method - the method to access.
|
||||
* @return The method accessor.
|
||||
*/
|
||||
public static MethodAccessor getMethodAccessor(final Method method) {
|
||||
return getMethodAccessor(method, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method accessor for a particular method, avoding checked exceptions.
|
||||
* @param method - the method to access.
|
||||
* @param forceAccess - whether or not to skip Java access checking.
|
||||
* @return The method accessor.
|
||||
*/
|
||||
public static MethodAccessor getMethodAccessor(final Method method, boolean forceAccess) {
|
||||
method.setAccessible(forceAccess);
|
||||
return new DefaultMethodAccessor(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a constructor accessor for a constructor with the given signature.
|
||||
* @param instanceClass - the parent class.
|
||||
* @param parameters - the parameters.
|
||||
* @return The constructor accessor.
|
||||
* @throws IllegalArgumentException If we cannot find this constructor.
|
||||
* @throws IllegalStateException If we cannot access reflection.
|
||||
*/
|
||||
public static ConstructorAccessor getConstructorAccessor(Class<?> instanceClass, Class<?>... parameters) {
|
||||
try {
|
||||
return getConstructorAccessor(instanceClass.getDeclaredConstructor(parameters));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Unable to find constructor %s(%s).", instanceClass, Joiner.on(",").join(parameters))
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalStateException("Cannot access constructors.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a constructor accessor for a particular constructor, avoding checked exceptions.
|
||||
* @param constructor - the constructor to access.
|
||||
* @return The method accessor.
|
||||
*/
|
||||
public static ConstructorAccessor getConstructorAccessor(final Constructor<?> constructor) {
|
||||
return new DefaultConstrutorAccessor(constructor);
|
||||
}
|
||||
|
||||
// Seal this class
|
||||
private Accessors() {
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
final class DefaultConstrutorAccessor implements ConstructorAccessor {
|
||||
private final Constructor<?> constructor;
|
||||
|
||||
public DefaultConstrutorAccessor(Constructor<?> method) {
|
||||
this.constructor = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object... args) {
|
||||
try {
|
||||
return constructor.newInstance(args);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot use reflection.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("An internal error occured.", e.getCause());
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("Cannot instantiate object.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<?> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return constructor != null ? constructor.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (obj instanceof DefaultConstrutorAccessor) {
|
||||
DefaultConstrutorAccessor other = (DefaultConstrutorAccessor) obj;
|
||||
return other.constructor == constructor;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultConstrutorAccessor [constructor=" + constructor + "]";
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
final class DefaultFieldAccessor implements FieldAccessor {
|
||||
private final Field field;
|
||||
|
||||
public DefaultFieldAccessor(Field field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
try {
|
||||
return field.get(instance);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Cannot read " + field, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot use reflection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
try {
|
||||
field.set(instance, value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Cannot set field " + field + " to value " + value, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot use reflection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return field != null ? field.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (obj instanceof DefaultFieldAccessor) {
|
||||
DefaultFieldAccessor other = (DefaultFieldAccessor) obj;
|
||||
return other.field == field;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultFieldAccessor [field=" + field + "]";
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
final class DefaultMethodAccessor implements MethodAccessor {
|
||||
private final Method method;
|
||||
|
||||
public DefaultMethodAccessor(Method method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object target, Object... args) {
|
||||
try {
|
||||
return method.invoke(target, args);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot use reflection.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("An internal error occured.", e.getCause());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return method != null ? method.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (obj instanceof DefaultMethodAccessor) {
|
||||
DefaultMethodAccessor other = (DefaultMethodAccessor) obj;
|
||||
return other.method == method;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultMethodAccessor [method=" + method + "]";
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.accessors;
|
||||
|
||||
public abstract class ReadOnlyFieldAccessor implements FieldAccessor {
|
||||
@Override
|
||||
public final void set(Object instance, Object value) {
|
||||
throw new UnsupportedOperationException("Cannot update the content of a read-only field accessor.");
|
||||
}
|
||||
}
|
||||
-33
@@ -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;
|
||||
}
|
||||
}
|
||||
-373
@@ -1,373 +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.reflect.compiler;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryPoolMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.compiler.StructureCompiler.StructureKey;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
/**
|
||||
* Compiles structure modifiers on a background thread.
|
||||
* <p>
|
||||
* This is necessary as we cannot block the main thread.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class BackgroundCompiler {
|
||||
public static final ReportType REPORT_CANNOT_COMPILE_STRUCTURE_MODIFIER = new ReportType("Cannot compile structure. Disabing compiler.");
|
||||
public static final ReportType REPORT_CANNOT_SCHEDULE_COMPILATION = new ReportType("Unable to schedule compilation task.");
|
||||
|
||||
/**
|
||||
* The default format for the name of new worker threads.
|
||||
*/
|
||||
public static final String THREAD_FORMAT = "ProtocolLib-StructureCompiler %s";
|
||||
|
||||
// How long to wait for a shutdown
|
||||
public static final int SHUTDOWN_DELAY_MS = 2000;
|
||||
|
||||
/**
|
||||
* The default fraction of perm gen space after which the background compiler will be disabled.
|
||||
*/
|
||||
public static final double DEFAULT_DISABLE_AT_PERM_GEN = 0.65;
|
||||
|
||||
// The single background compiler we're using
|
||||
private static BackgroundCompiler backgroundCompiler;
|
||||
|
||||
// Classes we're currently compiling
|
||||
private Map<StructureKey, List<CompileListener<?>>> listeners = Maps.newHashMap();
|
||||
private Object listenerLock = new Object();
|
||||
|
||||
private StructureCompiler compiler;
|
||||
private boolean enabled;
|
||||
private boolean shuttingDown;
|
||||
|
||||
private ExecutorService executor;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
private double disablePermGenFraction = DEFAULT_DISABLE_AT_PERM_GEN;
|
||||
|
||||
/**
|
||||
* Retrieves the current background compiler.
|
||||
* @return Current background compiler.
|
||||
*/
|
||||
public static BackgroundCompiler getInstance() {
|
||||
return backgroundCompiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the single background compiler we're using.
|
||||
* @param backgroundCompiler - current background compiler, or NULL if the library is not loaded.
|
||||
*/
|
||||
public static void setInstance(BackgroundCompiler backgroundCompiler) {
|
||||
BackgroundCompiler.backgroundCompiler = backgroundCompiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a background compiler.
|
||||
* <p>
|
||||
* Uses the default {@link #THREAD_FORMAT} to name worker threads.
|
||||
* @param loader - class loader from Bukkit.
|
||||
* @param reporter - current error reporter.
|
||||
*/
|
||||
public BackgroundCompiler(ClassLoader loader, ErrorReporter reporter) {
|
||||
ThreadFactory factory = new ThreadFactoryBuilder().
|
||||
setDaemon(true).
|
||||
setNameFormat(THREAD_FORMAT).
|
||||
build();
|
||||
initializeCompiler(loader, reporter, Executors.newSingleThreadExecutor(factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a background compiler utilizing the given thread pool.
|
||||
* @param loader - class loader from Bukkit.
|
||||
* @param reporter - current error reporter.
|
||||
* @param executor - thread pool we'll use.
|
||||
*/
|
||||
public BackgroundCompiler(ClassLoader loader, ErrorReporter reporter, ExecutorService executor) {
|
||||
initializeCompiler(loader, reporter, executor);
|
||||
}
|
||||
|
||||
// Avoid "Constructor call must be the first statement".
|
||||
private void initializeCompiler(ClassLoader loader, ErrorReporter reporter, ExecutorService executor) {
|
||||
if (loader == null)
|
||||
throw new IllegalArgumentException("loader cannot be NULL");
|
||||
if (executor == null)
|
||||
throw new IllegalArgumentException("executor cannot be NULL");
|
||||
if (reporter == null)
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
|
||||
this.compiler = new StructureCompiler(loader);
|
||||
this.reporter = reporter;
|
||||
this.executor = executor;
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the indirectly given structure modifier is eventually compiled.
|
||||
* @param cache - store of structure modifiers.
|
||||
* @param key - key of the structure modifier to compile.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void scheduleCompilation(final Map<Class, StructureModifier> cache, final Class key) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final StructureModifier<Object> uncompiled = cache.get(key);
|
||||
|
||||
if (uncompiled != null) {
|
||||
scheduleCompilation(uncompiled, new CompileListener<Object>() {
|
||||
@Override
|
||||
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
||||
// Update cache
|
||||
cache.put(key, compiledModifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the given structure modifier is eventually compiled.
|
||||
* @param <TKey> Type
|
||||
* @param uncompiled - structure modifier to compile.
|
||||
* @param listener - listener responsible for responding to the compilation.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <TKey> void scheduleCompilation(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
|
||||
// Only schedule if we're enabled
|
||||
if (enabled && !shuttingDown) {
|
||||
// Check perm gen
|
||||
if (getPermGenUsage() > disablePermGenFraction)
|
||||
return;
|
||||
|
||||
// Don't try to schedule anything
|
||||
if (executor == null || executor.isShutdown())
|
||||
return;
|
||||
|
||||
// Use to look up structure modifiers
|
||||
final StructureKey key = new StructureKey(uncompiled);
|
||||
|
||||
// Allow others to listen in too
|
||||
synchronized (listenerLock) {
|
||||
List list = listeners.get(key);
|
||||
|
||||
if (!listeners.containsKey(key)) {
|
||||
listeners.put(key, (List) Lists.newArrayList(listener));
|
||||
} else {
|
||||
// We're currently compiling
|
||||
list.add(listener);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the worker that will compile our modifier
|
||||
Callable<?> worker = new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
StructureModifier<TKey> modifier = uncompiled;
|
||||
List list = null;
|
||||
|
||||
// Do our compilation
|
||||
try {
|
||||
modifier = compiler.compile(modifier);
|
||||
|
||||
synchronized (listenerLock) {
|
||||
list = listeners.get(key);
|
||||
|
||||
// Prevent ConcurrentModificationExceptions
|
||||
if (list != null) {
|
||||
list = Lists.newArrayList(list);
|
||||
}
|
||||
}
|
||||
|
||||
// Only execute the listeners if there is a list
|
||||
if (list != null) {
|
||||
for (Object compileListener : list) {
|
||||
((CompileListener<TKey>) compileListener).onCompiled(modifier);
|
||||
}
|
||||
|
||||
// Remove it when we're done
|
||||
synchronized (listenerLock) {
|
||||
list = listeners.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
setEnabled(false);
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
setEnabled(false);
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Disable future compilations!
|
||||
setEnabled(false);
|
||||
|
||||
// Inform about this error as best as we can
|
||||
reporter.reportDetailed(BackgroundCompiler.this,
|
||||
Report.newBuilder(REPORT_CANNOT_COMPILE_STRUCTURE_MODIFIER).callerParam(uncompiled).error(e)
|
||||
);
|
||||
}
|
||||
|
||||
// We'll also return the new structure modifier
|
||||
return modifier;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// Lookup the previous class name on the main thread.
|
||||
// This is necessary as the Bukkit class loaders are not thread safe
|
||||
if (compiler.lookupClassLoader(uncompiled)) {
|
||||
try {
|
||||
worker.call();
|
||||
} catch (Exception e) {
|
||||
// Impossible!
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Perform the compilation on a seperate thread
|
||||
executor.submit(worker);
|
||||
}
|
||||
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Occures when the underlying queue is overflowing. Since the compilation
|
||||
// is only an optmization and not really essential we'll just log this failure
|
||||
// and move on.
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SCHEDULE_COMPILATION).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a compile listener if we are still waiting for the structure modifier to be compiled.
|
||||
* @param <TKey> Type
|
||||
* @param uncompiled - the structure modifier that may get compiled.
|
||||
* @param listener - the listener to invoke in that case.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TKey> void addListener(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
|
||||
synchronized (listenerLock) {
|
||||
StructureKey key = new StructureKey(uncompiled);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
List list = listeners.get(key);
|
||||
|
||||
if (list != null) {
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current usage of the Perm Gen space in percentage.
|
||||
* @return Usage of the perm gen space.
|
||||
*/
|
||||
private double getPermGenUsage() {
|
||||
for (MemoryPoolMXBean item : ManagementFactory.getMemoryPoolMXBeans()) {
|
||||
if (item.getName().contains("Perm Gen")) {
|
||||
MemoryUsage usage = item.getUsage();
|
||||
return usage.getUsed() / (double) usage.getCommitted();
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up after ourselves using the default timeout.
|
||||
*/
|
||||
public void shutdownAll() {
|
||||
shutdownAll(SHUTDOWN_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up after ourselves.
|
||||
* @param timeout - the maximum time to wait.
|
||||
* @param unit - the time unit of the timeout argument.
|
||||
*/
|
||||
public void shutdownAll(long timeout, TimeUnit unit) {
|
||||
setEnabled(false);
|
||||
shuttingDown = true;
|
||||
executor.shutdown();
|
||||
|
||||
try {
|
||||
executor.awaitTermination(timeout, unit);
|
||||
} catch (InterruptedException e) {
|
||||
// Unlikely to ever occur - it's the main thread
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not the background compiler is enabled.
|
||||
* @return TRUE if it is enabled, FALSE otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the background compiler is enabled.
|
||||
* @param enabled - TRUE to enable it, FALSE otherwise.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the fraction of perm gen space used after which the background compiler will be disabled.
|
||||
* @return The fraction after which the background compiler is disabled.
|
||||
*/
|
||||
public double getDisablePermGenFraction() {
|
||||
return disablePermGenFraction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fraction of perm gen space used after which the background compiler will be disabled.
|
||||
* @param fraction - the maximum use of perm gen space.
|
||||
*/
|
||||
public void setDisablePermGenFraction(double fraction) {
|
||||
this.disablePermGenFraction = fraction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current structure compiler.
|
||||
* @return Current structure compiler.
|
||||
*/
|
||||
public StructureCompiler getCompiler() {
|
||||
return compiler;
|
||||
}
|
||||
}
|
||||
@@ -1,294 +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.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Used by the compiler to automatically box and unbox values.
|
||||
*/
|
||||
class BoxingHelper {
|
||||
|
||||
private final static $Type BYTE_$Type = $Type.getObjectType("java/lang/Byte");
|
||||
private final static $Type BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean");
|
||||
private final static $Type SHORT_$Type = $Type.getObjectType("java/lang/Short");
|
||||
private final static $Type CHARACTER_$Type = $Type.getObjectType("java/lang/Character");
|
||||
private final static $Type INTEGER_$Type = $Type.getObjectType("java/lang/Integer");
|
||||
private final static $Type FLOAT_$Type = $Type.getObjectType("java/lang/Float");
|
||||
private final static $Type LONG_$Type = $Type.getObjectType("java/lang/Long");
|
||||
private final static $Type DOUBLE_$Type = $Type.getObjectType("java/lang/Double");
|
||||
private final static $Type NUMBER_$Type = $Type.getObjectType("java/lang/Number");
|
||||
private final static $Type OBJECT_$Type = $Type.getObjectType("java/lang/Object");
|
||||
|
||||
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
|
||||
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()");
|
||||
private final static MethodDescriptor INT_VALUE = MethodDescriptor.getMethod("int intValue()");
|
||||
private final static MethodDescriptor FLOAT_VALUE = MethodDescriptor.getMethod("float floatValue()");
|
||||
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
|
||||
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
|
||||
|
||||
private $MethodVisitor mv;
|
||||
|
||||
public BoxingHelper($MethodVisitor mv) {
|
||||
this.mv = mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instructions to box the top stack value. This value is
|
||||
* replaced by its boxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type the $Type of the top stack value.
|
||||
*/
|
||||
public void box(final $Type type){
|
||||
if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == $Type.VOID_TYPE) {
|
||||
push((String) null);
|
||||
} else {
|
||||
$Type boxed = type;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case $Type.BYTE:
|
||||
boxed = BYTE_$Type;
|
||||
break;
|
||||
case $Type.BOOLEAN:
|
||||
boxed = BOOLEAN_$Type;
|
||||
break;
|
||||
case $Type.SHORT:
|
||||
boxed = SHORT_$Type;
|
||||
break;
|
||||
case $Type.CHAR:
|
||||
boxed = CHARACTER_$Type;
|
||||
break;
|
||||
case $Type.INT:
|
||||
boxed = INTEGER_$Type;
|
||||
break;
|
||||
case $Type.FLOAT:
|
||||
boxed = FLOAT_$Type;
|
||||
break;
|
||||
case $Type.LONG:
|
||||
boxed = LONG_$Type;
|
||||
break;
|
||||
case $Type.DOUBLE:
|
||||
boxed = DOUBLE_$Type;
|
||||
break;
|
||||
}
|
||||
|
||||
newInstance(boxed);
|
||||
if(type.getSize() == 2) {
|
||||
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
|
||||
dupX2();
|
||||
dupX2();
|
||||
pop();
|
||||
} else {
|
||||
// p -> po -> opo -> oop -> o
|
||||
dupX1();
|
||||
swap();
|
||||
}
|
||||
|
||||
invokeConstructor(boxed, new MethodDescriptor("<init>", $Type.VOID_TYPE, new $Type[] {type}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to invoke a constructor.
|
||||
*
|
||||
* @param $Type the class in which the constructor is defined.
|
||||
* @param method the constructor to be invoked.
|
||||
*/
|
||||
public void invokeConstructor(final $Type $Type, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKESPECIAL, $Type, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X1 instruction.
|
||||
*/
|
||||
public void dupX1(){
|
||||
mv.visitInsn($Opcodes.DUP_X1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X2 instruction.
|
||||
*/
|
||||
public void dupX2(){
|
||||
mv.visitInsn($Opcodes.DUP_X2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a POP instruction.
|
||||
*/
|
||||
public void pop(){
|
||||
mv.visitInsn($Opcodes.POP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a SWAP instruction.
|
||||
*/
|
||||
public void swap(){
|
||||
mv.visitInsn($Opcodes.SWAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to push the given value on the stack.
|
||||
*
|
||||
* @param value the value to be pushed on the stack.
|
||||
*/
|
||||
public void push(final boolean value){
|
||||
push(value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to push the given value on the stack.
|
||||
*
|
||||
* @param value the value to be pushed on the stack.
|
||||
*/
|
||||
public void push(final int value) {
|
||||
if (value >= -1 && value <= 5) {
|
||||
mv.visitInsn($Opcodes.ICONST_0 + value);
|
||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn($Opcodes.BIPUSH, value);
|
||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn($Opcodes.SIPUSH, value);
|
||||
} else {
|
||||
mv.visitLdcInsn(new Integer(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to create a new object.
|
||||
*
|
||||
* @param $Type the class of the object to be created.
|
||||
*/
|
||||
public void newInstance(final $Type $Type){
|
||||
$TypeInsn($Opcodes.NEW, $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to push the given value on the stack.
|
||||
*
|
||||
* @param value the value to be pushed on the stack. May be <tt>null</tt>.
|
||||
*/
|
||||
public void push(final String value) {
|
||||
if (value == null) {
|
||||
mv.visitInsn($Opcodes.ACONST_NULL);
|
||||
} else {
|
||||
mv.visitLdcInsn(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instructions to unbox the top stack value. This value is
|
||||
* replaced by its unboxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type
|
||||
* the $Type of the top stack value.
|
||||
*/
|
||||
public void unbox(final $Type type){
|
||||
$Type t = NUMBER_$Type;
|
||||
MethodDescriptor sig = null;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case $Type.VOID:
|
||||
return;
|
||||
case $Type.CHAR:
|
||||
t = CHARACTER_$Type;
|
||||
sig = CHAR_VALUE;
|
||||
break;
|
||||
case $Type.BOOLEAN:
|
||||
t = BOOLEAN_$Type;
|
||||
sig = BOOLEAN_VALUE;
|
||||
break;
|
||||
case $Type.DOUBLE:
|
||||
sig = DOUBLE_VALUE;
|
||||
break;
|
||||
case $Type.FLOAT:
|
||||
sig = FLOAT_VALUE;
|
||||
break;
|
||||
case $Type.LONG:
|
||||
sig = LONG_VALUE;
|
||||
break;
|
||||
case $Type.INT:
|
||||
case $Type.SHORT:
|
||||
case $Type.BYTE:
|
||||
sig = INT_VALUE;
|
||||
}
|
||||
|
||||
if(sig == null) {
|
||||
checkCast(type);
|
||||
} else {
|
||||
checkCast(t);
|
||||
invokeVirtual(t, sig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to check that the top stack value is of the
|
||||
* given $Type.
|
||||
*
|
||||
* @param $Type a class or interface $Type.
|
||||
*/
|
||||
public void checkCast(final $Type $Type){
|
||||
if(!$Type.equals(OBJECT_$Type)) {
|
||||
$TypeInsn($Opcodes.CHECKCAST, $Type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to invoke a normal method.
|
||||
*
|
||||
* @param owner the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
public void invokeVirtual(final $Type owner, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an invoke method instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param $Type the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){
|
||||
String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName();
|
||||
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a $Type dependent instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param $Type the instruction's operand.
|
||||
*/
|
||||
private void $TypeInsn(final int opcode, final $Type $Type){
|
||||
String desc;
|
||||
|
||||
if($Type.getSort() == $Type.ARRAY) {
|
||||
desc = $Type.getDescriptor();
|
||||
} else {
|
||||
desc = $Type.getInternalName();
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(opcode, desc);
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +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.reflect.compiler;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
||||
/**
|
||||
* Used to save the result of an compilation.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TKey> - type of the structure modifier field.
|
||||
*/
|
||||
public interface CompileListener<TKey> {
|
||||
/**
|
||||
* Invoked when a structure modifier has been successfully compiled.
|
||||
* @param compiledModifier - the compiled structure modifier.
|
||||
*/
|
||||
public void onCompiled(StructureModifier<TKey> compiledModifier);
|
||||
}
|
||||
-136
@@ -1,136 +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.reflect.compiler;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a compiled structure modifier.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class CompiledStructureModifier extends StructureModifier<Object> {
|
||||
// Used to compile instances of structure modifiers
|
||||
protected StructureCompiler compiler;
|
||||
|
||||
// Fields that originally were read only
|
||||
private Set<Integer> exempted;
|
||||
|
||||
public CompiledStructureModifier() {
|
||||
super();
|
||||
customConvertHandling = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
|
||||
// We can remove the read-only status
|
||||
if (isReadOnly(fieldIndex) && !value) {
|
||||
if (exempted == null)
|
||||
exempted = Sets.newHashSet();
|
||||
exempted.add(fieldIndex);
|
||||
}
|
||||
|
||||
// We can only make a certain kind of field read only
|
||||
if (!isReadOnly(fieldIndex) && value) {
|
||||
if (exempted == null || !exempted.contains(fieldIndex)) {
|
||||
throw new IllegalStateException("Cannot make compiled field " + fieldIndex + " read only.");
|
||||
}
|
||||
}
|
||||
|
||||
super.setReadOnly(fieldIndex, value);
|
||||
}
|
||||
|
||||
// Speed up the default writer
|
||||
@Override
|
||||
public StructureModifier<Object> writeDefaults() throws FieldAccessException {
|
||||
DefaultInstances generator = DefaultInstances.DEFAULT;
|
||||
|
||||
// Write a default instance to every field
|
||||
for (Map.Entry<Field, Integer> entry : defaultFields.entrySet()) {
|
||||
Integer index = entry.getValue();
|
||||
Field field = entry.getKey();
|
||||
|
||||
// Special case for Spigot's custom chat components
|
||||
// They must be null or messages will be blank
|
||||
if (field.getType().getCanonicalName().equals("net.md_5.bungee.api.chat.BaseComponent[]")) {
|
||||
write(index, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
write(index, generator.getDefault(field.getType()));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object read(int fieldIndex) throws FieldAccessException {
|
||||
Object result = readGenerated(fieldIndex);
|
||||
|
||||
if (converter != null)
|
||||
return converter.getSpecific(result);
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the given field index using reflection.
|
||||
* @param index - index of field.
|
||||
* @return Resulting value.
|
||||
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
protected Object readReflected(int index) throws FieldAccessException {
|
||||
return super.read(index);
|
||||
}
|
||||
|
||||
protected abstract Object readGenerated(int fieldIndex) throws FieldAccessException;
|
||||
|
||||
@Override
|
||||
public StructureModifier<Object> write(int index, Object value) throws FieldAccessException {
|
||||
if (converter != null)
|
||||
value = converter.getGeneric(value);
|
||||
return writeGenerated(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given field using reflection.
|
||||
* @param index - index of field.
|
||||
* @param value - new value.
|
||||
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
|
||||
*/
|
||||
protected void writeReflected(int index, Object value) throws FieldAccessException {
|
||||
super.write(index, value);
|
||||
}
|
||||
|
||||
protected abstract StructureModifier<Object> writeGenerated(int index, Object value) throws FieldAccessException;
|
||||
|
||||
@Override
|
||||
public StructureModifier<Object> withTarget(Object target) {
|
||||
if (compiler != null)
|
||||
return compiler.compile(super.withTarget(target));
|
||||
else
|
||||
return super.withTarget(target);
|
||||
}
|
||||
}
|
||||
-237
@@ -1,237 +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.reflect.compiler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Represents a method.
|
||||
*/
|
||||
class MethodDescriptor {
|
||||
|
||||
/**
|
||||
* The method name.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The method descriptor.
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* Maps primitive Java type names to their descriptors.
|
||||
*/
|
||||
private static final Map<String, String> DESCRIPTORS;
|
||||
|
||||
static {
|
||||
DESCRIPTORS = new HashMap<String, String>();
|
||||
DESCRIPTORS.put("void", "V");
|
||||
DESCRIPTORS.put("byte", "B");
|
||||
DESCRIPTORS.put("char", "C");
|
||||
DESCRIPTORS.put("double", "D");
|
||||
DESCRIPTORS.put("float", "F");
|
||||
DESCRIPTORS.put("int", "I");
|
||||
DESCRIPTORS.put("long", "J");
|
||||
DESCRIPTORS.put("short", "S");
|
||||
DESCRIPTORS.put("boolean", "Z");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param desc the method's descriptor.
|
||||
*/
|
||||
public MethodDescriptor(final String name, final String desc) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param returnType the method's return type.
|
||||
* @param argumentTypes the method's argument types.
|
||||
*/
|
||||
public MethodDescriptor(
|
||||
final String name,
|
||||
final $Type returnType,
|
||||
final $Type[] argumentTypes)
|
||||
{
|
||||
this(name, $Type.getMethodDescriptor(returnType, argumentTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
* form "returnType name (argumentType1, ... argumentTypeN)", where
|
||||
* the types are in plain Java (e.g. "int", "float",
|
||||
* "java.util.List", ...). Classes of the java.lang package can be
|
||||
* specified by their unqualified name; all other classes names must
|
||||
* be fully qualified.
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
*/
|
||||
public static MethodDescriptor getMethod(final String method)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
return getMethod(method, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
* form "returnType name (argumentType1, ... argumentTypeN)", where
|
||||
* the types are in plain Java (e.g. "int", "float",
|
||||
* "java.util.List", ...). Classes of the java.lang package may be
|
||||
* specified by their unqualified name, depending on the
|
||||
* defaultPackage argument; all other classes names must be fully
|
||||
* qualified.
|
||||
* @param defaultPackage true if unqualified class names belong to the
|
||||
* default package, or false if they correspond to java.lang classes.
|
||||
* For instance "Object" means "Object" if this option is true, or
|
||||
* "java.lang.Object" otherwise.
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
*/
|
||||
public static MethodDescriptor getMethod(
|
||||
final String method,
|
||||
final boolean defaultPackage) throws IllegalArgumentException
|
||||
{
|
||||
int space = method.indexOf(' ');
|
||||
int start = method.indexOf('(', space) + 1;
|
||||
int end = method.indexOf(')', start);
|
||||
if (space == -1 || start == -1 || end == -1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
String returnType = method.substring(0, space);
|
||||
String methodName = method.substring(space + 1, start - 1).trim();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
int p;
|
||||
do {
|
||||
String s;
|
||||
p = method.indexOf(',', start);
|
||||
if (p == -1) {
|
||||
s = map(method.substring(start, end).trim(), defaultPackage);
|
||||
} else {
|
||||
s = map(method.substring(start, p).trim(), defaultPackage);
|
||||
start = p + 1;
|
||||
}
|
||||
sb.append(s);
|
||||
} while (p != -1);
|
||||
sb.append(')');
|
||||
sb.append(map(returnType, defaultPackage));
|
||||
return new MethodDescriptor(methodName, sb.toString());
|
||||
}
|
||||
|
||||
private static String map(final String type, final boolean defaultPackage) {
|
||||
if ("".equals(type)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int index = 0;
|
||||
while ((index = type.indexOf("[]", index) + 1) > 0) {
|
||||
sb.append('[');
|
||||
}
|
||||
|
||||
String t = type.substring(0, type.length() - sb.length() * 2);
|
||||
String desc = DESCRIPTORS.get(t);
|
||||
if (desc != null) {
|
||||
sb.append(desc);
|
||||
} else {
|
||||
sb.append('L');
|
||||
if (t.indexOf('.') < 0) {
|
||||
if (!defaultPackage) {
|
||||
sb.append("java/lang/");
|
||||
}
|
||||
sb.append(t);
|
||||
} else {
|
||||
sb.append(t.replace('.', '/'));
|
||||
}
|
||||
sb.append(';');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the method described by this object.
|
||||
*
|
||||
* @return the name of the method described by this object.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the descriptor of the method described by this object.
|
||||
*
|
||||
* @return the descriptor of the method described by this object.
|
||||
*/
|
||||
public String getDescriptor() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return type of the method described by this object.
|
||||
*
|
||||
* @return the return type of the method described by this object.
|
||||
*/
|
||||
public $Type getReturnType() {
|
||||
return $Type.getReturnType(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the argument types of the method described by this object.
|
||||
*
|
||||
* @return the argument types of the method described by this object.
|
||||
*/
|
||||
public $Type[] getArgumentTypes() {
|
||||
return $Type.getArgumentTypes(desc);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name + desc;
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (!(o instanceof MethodDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
MethodDescriptor other = (MethodDescriptor) o;
|
||||
return name.equals(other.name) && desc.equals(other.desc);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return name.hashCode() ^ desc.hashCode();
|
||||
}
|
||||
}
|
||||
-548
@@ -1,548 +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.reflect.compiler;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sf.cglib.asm.$ClassWriter;
|
||||
import net.sf.cglib.asm.$FieldVisitor;
|
||||
import net.sf.cglib.asm.$Label;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
// public class CompiledStructureModifierPacket20<TField> extends CompiledStructureModifier<TField> {
|
||||
//
|
||||
// private Packet20NamedEntitySpawn typedTarget;
|
||||
//
|
||||
// public CompiledStructureModifierPacket20(StructureModifier<TField> other, StructureCompiler compiler) {
|
||||
// super();
|
||||
// initialize(other);
|
||||
// this.target = other.getTarget();
|
||||
// this.typedTarget = (Packet20NamedEntitySpawn) target;
|
||||
// this.compiler = compiler;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected Object readGenerated(int fieldIndex) throws FieldAccessException {
|
||||
//
|
||||
// Packet20NamedEntitySpawn target = typedTarget;
|
||||
//
|
||||
// switch (fieldIndex) {
|
||||
// case 0: return (Object) target.a;
|
||||
// case 1: return (Object) target.b;
|
||||
// case 2: return (Object) target.c;
|
||||
// case 3: return super.readReflected(fieldIndex);
|
||||
// case 4: return super.readReflected(fieldIndex);
|
||||
// case 5: return (Object) target.f;
|
||||
// case 6: return (Object) target.g;
|
||||
// case 7: return (Object) target.h;
|
||||
// default:
|
||||
// throw new FieldAccessException("Invalid index " + fieldIndex);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected StructureModifier<TField> writeGenerated(int index, Object value) throws FieldAccessException {
|
||||
//
|
||||
// Packet20NamedEntitySpawn target = typedTarget;
|
||||
//
|
||||
// switch (index) {
|
||||
// case 0: target.a = (Integer) value; break;
|
||||
// case 1: target.b = (String) value; break;
|
||||
// case 2: target.c = (Integer) value; break;
|
||||
// case 3: target.d = (Integer) value; break;
|
||||
// case 4: super.writeReflected(index, value); break;
|
||||
// case 5: super.writeReflected(index, value); break;
|
||||
// case 6: target.g = (Byte) value; break;
|
||||
// case 7: target.h = (Integer) value; break;
|
||||
// default:
|
||||
// throw new FieldAccessException("Invalid index " + index);
|
||||
// }
|
||||
//
|
||||
// // Chaining
|
||||
// return this;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Represents a StructureModifier compiler.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class StructureCompiler {
|
||||
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)");
|
||||
|
||||
// Used to store generated classes of different types
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class StructureKey {
|
||||
private Class targetType;
|
||||
private Class fieldType;
|
||||
|
||||
public StructureKey(StructureModifier<?> source) {
|
||||
this(source.getTargetType(), source.getFieldType());
|
||||
}
|
||||
|
||||
public StructureKey(Class targetType, Class fieldType) {
|
||||
this.targetType = targetType;
|
||||
this.fieldType = fieldType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(targetType, fieldType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof StructureKey) {
|
||||
StructureKey other = (StructureKey) obj;
|
||||
return Objects.equal(targetType, other.targetType) &&
|
||||
Objects.equal(fieldType, other.fieldType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Used to load classes
|
||||
private volatile static Method defineMethod;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
|
||||
|
||||
// The class loader we'll store our classes
|
||||
private ClassLoader loader;
|
||||
|
||||
// References to other classes
|
||||
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
|
||||
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
|
||||
private static String COMPILED_CLASS = PACKAGE_NAME + "/CompiledStructureModifier";
|
||||
private static String FIELD_EXCEPTION_CLASS = "com/comphenix/protocol/reflect/FieldAccessException";
|
||||
|
||||
public static boolean attemptClassLoad = false;
|
||||
|
||||
/**
|
||||
* Construct a structure compiler.
|
||||
* @param loader - main class loader.
|
||||
*/
|
||||
StructureCompiler(ClassLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the current class loader for any previously generated classes before we attempt to generate something.
|
||||
* @param <TField> Type
|
||||
* @param source - the structure modifier to look up.
|
||||
* @return TRUE if we successfully found a previously generated class, FALSE otherwise.
|
||||
*/
|
||||
public <TField> boolean lookupClassLoader(StructureModifier<TField> source) {
|
||||
StructureKey key = new StructureKey(source);
|
||||
|
||||
// See if there's a need to lookup the class name
|
||||
if (compiledCache.containsKey(key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! attemptClassLoad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This causes a ton of lag and doesn't seem to work
|
||||
|
||||
try {
|
||||
String className = getCompiledName(source);
|
||||
|
||||
// This class might have been generated before. Try to load it.
|
||||
Class<?> before = loader.loadClass(PACKAGE_NAME.replace('/', '.') + "." + className);
|
||||
|
||||
if (before != null) {
|
||||
compiledCache.put(key, before);
|
||||
return true;
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// That's ok.
|
||||
}
|
||||
|
||||
// We need to compile the class
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the given structure modifier.
|
||||
* <p>
|
||||
* WARNING: Do NOT call this method in the main thread. Compiling may easily take 10 ms, which is already
|
||||
* over 1/4 of a tick (50 ms). Let the background thread automatically compile the structure modifiers instead.
|
||||
* @param <TField> Type
|
||||
* @param source - structure modifier to compile.
|
||||
* @return A compiled structure modifier.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized <TField> StructureModifier<TField> compile(StructureModifier<TField> source) {
|
||||
|
||||
// We cannot optimize a structure modifier with no public fields
|
||||
if (!isAnyPublic(source.getFields())) {
|
||||
return source;
|
||||
}
|
||||
|
||||
StructureKey key = new StructureKey(source);
|
||||
Class<?> compiledClass = compiledCache.get(key);
|
||||
|
||||
if (!compiledCache.containsKey(key)) {
|
||||
compiledClass = generateClass(source);
|
||||
compiledCache.put(key, compiledClass);
|
||||
}
|
||||
|
||||
// Next, create an instance of this class
|
||||
try {
|
||||
return (StructureModifier<TField>) compiledClass.getConstructor(
|
||||
StructureModifier.class, StructureCompiler.class).
|
||||
newInstance(source, this);
|
||||
} catch (OutOfMemoryError e) {
|
||||
// Print the number of generated classes by the current instances
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(
|
||||
this, Report.newBuilder(REPORT_TOO_MANY_GENERATED_CLASSES).messageParam(compiledCache.size())
|
||||
);
|
||||
throw e;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException("Used invalid parameters in instance creation", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException("Security limitation!", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("Error occured while instancing generated class.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Security limitation! Cannot create instance of dynamic class.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error occured while instancing generated class.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException("Cannot happen.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a variable identifier that can uniquely represent the given type.
|
||||
* @param type - a type.
|
||||
* @return A unique and legal identifier for the given type.
|
||||
*/
|
||||
private String getSafeTypeName(Class<?> type) {
|
||||
return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the compiled name of a given structure modifier.
|
||||
* @param source - the structure modifier.
|
||||
* @return The unique, compiled name of a compiled structure modifier.
|
||||
*/
|
||||
private String getCompiledName(StructureModifier<?> source) {
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
// Concat class and field type
|
||||
return "CompiledStructure$" +
|
||||
getSafeTypeName(targetType) + "$" +
|
||||
getSafeTypeName(source.getFieldType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a structure modifier.
|
||||
* @param source - structure modifier.
|
||||
* @return The compiled structure modifier.
|
||||
*/
|
||||
private <TField> Class<?> generateClass(StructureModifier<TField> source) {
|
||||
|
||||
$ClassWriter cw = new $ClassWriter(0);
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
String className = getCompiledName(source);
|
||||
String targetSignature = $Type.getDescriptor(targetType);
|
||||
String targetName = targetType.getName().replace('.', '/');
|
||||
|
||||
// Define class
|
||||
cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
|
||||
null, COMPILED_CLASS, null);
|
||||
|
||||
createFields(cw, targetSignature);
|
||||
createConstructor(cw, className, targetSignature, targetName);
|
||||
createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
cw.visitEnd();
|
||||
|
||||
byte[] data = cw.toByteArray();
|
||||
|
||||
// Call the define method
|
||||
try {
|
||||
if (defineMethod == null) {
|
||||
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
new Class<?>[] { String.class, byte[].class, int.class, int.class });
|
||||
|
||||
// Awesome. Now, create and return it.
|
||||
defined.setAccessible(true);
|
||||
defineMethod = defined;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
|
||||
|
||||
// DEBUG CODE: Print the content of the generated class.
|
||||
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
|
||||
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
|
||||
|
||||
return clazz;
|
||||
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException("Incompatible JVM.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException("Cannot call defineMethod - wrong JVM?", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Security limitation! Cannot dynamically load class.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error occured in code generator.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if at least one of the given fields is public.
|
||||
* @param fields - field to test.
|
||||
* @return TRUE if one or more field is publically accessible, FALSE otherwise.
|
||||
*/
|
||||
private boolean isAnyPublic(List<Field> fields) {
|
||||
// Are any of the fields public?
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
if (isPublic(fields.get(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPublic(Field field) {
|
||||
return Modifier.isPublic(field.getModifiers());
|
||||
}
|
||||
|
||||
private boolean isNonFinal(Field field) {
|
||||
return !Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
private void createFields($ClassWriter cw, String targetSignature) {
|
||||
$FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
|
||||
typedField.visitEnd();
|
||||
}
|
||||
|
||||
private void createWriteMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
|
||||
String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
|
||||
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;";
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
|
||||
new String[] { FIELD_EXCEPTION_CLASS });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 3);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
$Label return$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
Class<?> inputType = Primitives.wrap(outputType);
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
String inputPath = inputType.getName().replace('.', '/');
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Only write to public non-final fields
|
||||
if (isPublic(field) && isNonFinal(field)) {
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 3);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
|
||||
if (!outputType.isPrimitive())
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
|
||||
else
|
||||
boxingHelper.unbox($Type.getType(outputType));
|
||||
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
} else {
|
||||
// Use reflection. We don't have a choice, unfortunately.
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
|
||||
}
|
||||
|
||||
mv.visitJumpInsn($Opcodes.GOTO, return$Label);
|
||||
}
|
||||
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(return$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
mv.visitMaxs(5, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createReadMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
|
||||
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 2);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Note that byte code cannot access non-public fields
|
||||
if (isPublic(field)) {
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.box($Type.getType(outputType));
|
||||
} else {
|
||||
// We have to use reflection for private and protected fields.
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
}
|
||||
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
mv.visitMaxs(5, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "<init>",
|
||||
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
|
||||
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
|
||||
String fullClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, targetName);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
|
||||
mv.visitInsn($Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.fuzzy;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
/**
|
||||
* Represents a matcher for fields, methods, constructors and classes.
|
||||
* <p>
|
||||
* This class should ideally never expose mutable state. Its round number must be immutable.
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class AbstractFuzzyMatcher<T> implements Comparable<AbstractFuzzyMatcher<T>> {
|
||||
private Integer roundNumber;
|
||||
|
||||
/**
|
||||
* Determine if the given value is a match.
|
||||
* @param value - the value to match.
|
||||
* @param parent - the parent container, or NULL if this value is the root.
|
||||
* @return TRUE if it is a match, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isMatch(T value, Object parent);
|
||||
|
||||
/**
|
||||
* Calculate the round number indicating when this matcher should be applied.
|
||||
* <p>
|
||||
* Matchers with a lower round number are applied before matchers with a higher round number.
|
||||
* <p>
|
||||
* By convention, this round number should be negative, except for zero in the case of a matcher
|
||||
* that accepts any value. A good implementation should return the inverted tree depth (class hierachy)
|
||||
* of the least specified type used in the matching. Thus {@link Integer} will have a lower round number than
|
||||
* {@link Number}.
|
||||
*
|
||||
* @return A number (positive or negative) that is used to order matchers.
|
||||
*/
|
||||
protected abstract int calculateRoundNumber();
|
||||
|
||||
/**
|
||||
* Retrieve the cached round number. This should never change once calculated.
|
||||
* <p>
|
||||
* Matchers with a lower round number are applied before matchers with a higher round number.
|
||||
* @return The round number.
|
||||
* @see #calculateRoundNumber()
|
||||
*/
|
||||
public final int getRoundNumber() {
|
||||
if (roundNumber == null) {
|
||||
return roundNumber = calculateRoundNumber();
|
||||
} else {
|
||||
return roundNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two round numbers by taking the highest non-zero number, or return zero.
|
||||
* @param roundA - the first round number.
|
||||
* @param roundB - the second round number.
|
||||
* @return The combined round number.
|
||||
*/
|
||||
protected final int combineRounds(int roundA, int roundB) {
|
||||
if (roundA == 0)
|
||||
return roundB;
|
||||
else if (roundB == 0)
|
||||
return roundA;
|
||||
else
|
||||
return Math.max(roundA, roundB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine n round numbers by taking the highest non-zero number, or return zero.
|
||||
* @param rounds - the round numbers.
|
||||
* @return The combined round number.
|
||||
*/
|
||||
protected final int combineRounds(Integer... rounds) {
|
||||
if (rounds.length < 2)
|
||||
throw new IllegalArgumentException("Must supply at least two arguments.");
|
||||
|
||||
// Get the seed
|
||||
int reduced = combineRounds(rounds[0], rounds[1]);
|
||||
|
||||
// Aggregate it all
|
||||
for (int i = 2; i < rounds.length; i++) {
|
||||
reduced = combineRounds(reduced, rounds[i]);
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AbstractFuzzyMatcher<T> obj) {
|
||||
return Ints.compare(getRoundNumber(), obj.getRoundNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fuzzy matcher that returns the opposite result of the current matcher.
|
||||
* @return An inverted fuzzy matcher.
|
||||
*/
|
||||
public AbstractFuzzyMatcher<T> inverted() {
|
||||
return new AbstractFuzzyMatcher<T>() {
|
||||
@Override
|
||||
public boolean isMatch(T value, Object parent) {
|
||||
return !AbstractFuzzyMatcher.this.isMatch(value, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return -2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that this and the given matcher be TRUE.
|
||||
* @param other - the other fuzzy matcher.
|
||||
* @return A combined fuzzy matcher.
|
||||
*/
|
||||
public AbstractFuzzyMatcher<T> and(final AbstractFuzzyMatcher<T> other) {
|
||||
return new AbstractFuzzyMatcher<T>() {
|
||||
@Override
|
||||
public boolean isMatch(T value, Object parent) {
|
||||
// They both have to be true
|
||||
return AbstractFuzzyMatcher.this.isMatch(value, parent) &&
|
||||
other.isMatch(value, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that either this or the other given matcher be TRUE.
|
||||
* @param other - the other fuzzy matcher.
|
||||
* @return A combined fuzzy matcher.
|
||||
*/
|
||||
public AbstractFuzzyMatcher<T> or(final AbstractFuzzyMatcher<T> other) {
|
||||
return new AbstractFuzzyMatcher<T>() {
|
||||
@Override
|
||||
public boolean isMatch(T value, Object parent) {
|
||||
// Either can be true
|
||||
return AbstractFuzzyMatcher.this.isMatch(value, parent) ||
|
||||
other.isMatch(value, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.fuzzy;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Used to check class equality.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ClassExactMatcher extends AbstractFuzzyMatcher<Class<?>> {
|
||||
/**
|
||||
* Different matching rules.
|
||||
*/
|
||||
enum Options {
|
||||
/**
|
||||
* Match classes exactly.
|
||||
*/
|
||||
MATCH_EXACT,
|
||||
|
||||
/**
|
||||
* A match if the input class is a superclass of the matcher class, or the same class.
|
||||
*/
|
||||
MATCH_SUPER,
|
||||
|
||||
/**
|
||||
* A match if the input class is a derived class of the matcher class, or the same class.
|
||||
*/
|
||||
MATCH_DERIVED
|
||||
}
|
||||
|
||||
/**
|
||||
* Match any class.
|
||||
*/
|
||||
public static final ClassExactMatcher MATCH_ALL = new ClassExactMatcher(null, Options.MATCH_SUPER);
|
||||
|
||||
private final Class<?> matcher;
|
||||
private final Options option;
|
||||
|
||||
/**
|
||||
* Constructs a new class matcher.
|
||||
* @param matcher - the matching class, or NULL to represent anything.
|
||||
* @param option - options specifying the matching rules.
|
||||
*/
|
||||
ClassExactMatcher(Class<?> matcher, Options option) {
|
||||
this.matcher = matcher;
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given class is equivalent.
|
||||
* <p>
|
||||
* If the matcher is NULL, the result will only be TRUE if we're not matching exactly.
|
||||
* @param input - the input class defined in the source file.
|
||||
* @param parent - the container that holds a reference to this class.
|
||||
* @return TRUE if input matches according to the rules in {@link #getOptions()}, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatch(Class<?> input, Object parent) {
|
||||
if (input == null)
|
||||
throw new IllegalArgumentException("Input class cannot be NULL.");
|
||||
|
||||
// Do our checking
|
||||
if (matcher == null)
|
||||
return option != Options.MATCH_EXACT;
|
||||
else if (option == Options.MATCH_SUPER)
|
||||
return input.isAssignableFrom(matcher); // matcher instanceof input
|
||||
else if (option == Options.MATCH_DERIVED)
|
||||
return matcher.isAssignableFrom(input); // input instanceof matcher
|
||||
else
|
||||
return input.equals(matcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return -getClassNumber(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of superclasses of the specific class.
|
||||
* <p>
|
||||
* Object is represented as one. All interfaces are one, unless they're derived.
|
||||
* @param clazz - the class to test.
|
||||
* @return The number of superclasses.
|
||||
*/
|
||||
public static int getClassNumber(Class<?> clazz) {
|
||||
int count = 0;
|
||||
|
||||
// Move up the hierachy
|
||||
while (clazz != null) {
|
||||
count++;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class we're comparing against.
|
||||
* @return Class to compare against.
|
||||
*/
|
||||
public Class<?> getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* The matching rules for this class matcher.
|
||||
* @return The current matching option.
|
||||
*/
|
||||
public Options getOptions() {
|
||||
return option;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (option == Options.MATCH_SUPER)
|
||||
return matcher + " instanceof input";
|
||||
else if (option == Options.MATCH_DERIVED)
|
||||
return "input instanceof " + matcher;
|
||||
else
|
||||
return "Exact " + matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(matcher, option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (obj instanceof ClassExactMatcher) {
|
||||
ClassExactMatcher other = (ClassExactMatcher) obj;
|
||||
|
||||
return Objects.equal(matcher, other.matcher) &&
|
||||
Objects.equal(option, other.option);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.fuzzy;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Determine if a class matches based on its name using a regular expression.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ClassRegexMatcher extends AbstractFuzzyMatcher<Class<?>> {
|
||||
private final Pattern regex;
|
||||
private final int priority;
|
||||
|
||||
public ClassRegexMatcher(Pattern regex, int priority) {
|
||||
if (regex == null)
|
||||
throw new IllegalArgumentException("Regular expression pattern cannot be NULL.");
|
||||
this.regex = regex;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Class<?> value, Object parent) {
|
||||
if (value != null)
|
||||
return regex.matcher(value.getCanonicalName()).matches();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return -priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "class name of " + regex.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(regex, priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (obj instanceof ClassRegexMatcher) {
|
||||
ClassRegexMatcher other = (ClassRegexMatcher) obj;
|
||||
|
||||
return priority == other.priority &&
|
||||
FuzzyMatchers.checkPattern(regex, other.regex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.fuzzy;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents a class matcher that checks for equality using a given set of classes.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ClassSetMatcher extends AbstractFuzzyMatcher<Class<?>> {
|
||||
private final Set<Class<?>> classes;
|
||||
|
||||
public ClassSetMatcher(Set<Class<?>> classes) {
|
||||
if (classes == null)
|
||||
throw new IllegalArgumentException("Set of classes cannot be NULL.");
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Class<?> value, Object parent) {
|
||||
return classes.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
int roundNumber = 0;
|
||||
|
||||
// The highest round number (except zero).
|
||||
for (Class<?> clazz : classes) {
|
||||
roundNumber = combineRounds(roundNumber, -ClassExactMatcher.getClassNumber(clazz));
|
||||
}
|
||||
return roundNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "match any: " + classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return classes.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (obj instanceof ClassSetMatcher) {
|
||||
// See if the sets are equal
|
||||
return Objects.equal(classes, ((ClassSetMatcher) obj).classes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package com.comphenix.protocol.timing;
|
||||
|
||||
/**
|
||||
* Represents an online algortihm for computing the mean and standard deviation without storing every value.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StatisticsStream extends OnlineComputation {
|
||||
// This algorithm is due to Donald Knuth, as described in:
|
||||
// Donald E. Knuth (1998). The Art of Computer Programming, volume 2:
|
||||
// Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.
|
||||
|
||||
private int count = 0;
|
||||
private double mean = 0;
|
||||
private double m2 = 0;
|
||||
|
||||
// Also keep track of minimum and maximum observation
|
||||
private double minimum = Double.MAX_VALUE;
|
||||
private double maximum = 0;
|
||||
|
||||
/**
|
||||
* Construct a new stream with no observations.
|
||||
*/
|
||||
public StatisticsStream() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of the given stream.
|
||||
* @param other - copy of the stream.
|
||||
*/
|
||||
public StatisticsStream(StatisticsStream other) {
|
||||
this.count = other.count;
|
||||
this.mean = other.mean;
|
||||
this.m2 = other.m2;
|
||||
this.minimum = other.minimum;
|
||||
this.maximum = other.maximum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatisticsStream copy() {
|
||||
return new StatisticsStream(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe a value.
|
||||
* @param value - the observed value.
|
||||
*/
|
||||
@Override
|
||||
public void observe(double value) {
|
||||
double delta = value - mean;
|
||||
|
||||
// As per Knuth
|
||||
count++;
|
||||
mean += delta / count;
|
||||
m2 += delta * (value - mean);
|
||||
|
||||
// Update extremes
|
||||
if (value < minimum)
|
||||
minimum = value;
|
||||
if (value > maximum)
|
||||
maximum = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the average of all the observations.
|
||||
* @return The average.
|
||||
*/
|
||||
public double getMean() {
|
||||
checkCount();
|
||||
return mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the variance of all the observations.
|
||||
* @return The variance.
|
||||
*/
|
||||
public double getVariance() {
|
||||
checkCount();
|
||||
return m2 / (count - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the standard deviation of all the observations.
|
||||
* @return The STDV.
|
||||
*/
|
||||
public double getStandardDeviation() {
|
||||
return Math.sqrt(getVariance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the minimum observation yet observed.
|
||||
* @return The minimum observation.
|
||||
*/
|
||||
public double getMinimum() {
|
||||
checkCount();
|
||||
return minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the maximum observation yet observed.
|
||||
* @return The maximum observation.
|
||||
*/
|
||||
public double getMaximum() {
|
||||
checkCount();
|
||||
return maximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the two statistics.
|
||||
* @param other - the other statistics.
|
||||
* @return Combined statistics
|
||||
*/
|
||||
public StatisticsStream add(StatisticsStream other) {
|
||||
// Special cases
|
||||
if (count == 0)
|
||||
return other;
|
||||
else if (other.count == 0)
|
||||
return this;
|
||||
|
||||
StatisticsStream stream = new StatisticsStream();
|
||||
double delta = other.mean - mean;
|
||||
double n = count + other.count;
|
||||
|
||||
stream.count = (int) n;
|
||||
stream.mean = mean + delta * (other.count / n);
|
||||
stream.m2 = m2 + other.m2 + ((delta * delta) * (count * other.count) / n);
|
||||
stream.minimum = Math.min(minimum, other.minimum);
|
||||
stream.maximum = Math.max(maximum, other.maximum);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of observations.
|
||||
* @return Number of observations.
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
private void checkCount() {
|
||||
if (count == 0) {
|
||||
throw new IllegalStateException("No observations in stream.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (count == 0)
|
||||
return "StatisticsStream [Nothing recorded]";
|
||||
|
||||
return String.format("StatisticsStream [Average: %.3f, SD: %.3f, Min: %.3f, Max: %.3f, Count: %s]",
|
||||
getMean(), getStandardDeviation(),
|
||||
getMinimum(), getMaximum(), getCount());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents an input stream that delegates to a byte buffer.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ByteBufferInputStream extends InputStream {
|
||||
private ByteBuffer buf;
|
||||
|
||||
public ByteBufferInputStream(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!buf.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
return buf.get() & 0xFF;
|
||||
}
|
||||
|
||||
public int read(byte[] bytes, int off, int len)
|
||||
throws IOException {
|
||||
if (!buf.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = Math.min(len, buf.remaining());
|
||||
buf.get(bytes, off, len);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents an output stream that is backed by a ByteBuffer.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ByteBufferOutputStream extends OutputStream {
|
||||
ByteBuffer buf;
|
||||
|
||||
public ByteBufferOutputStream(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
buf.put((byte) b);
|
||||
}
|
||||
|
||||
public void write(byte[] bytes, int off, int len)
|
||||
throws IOException {
|
||||
buf.put(bytes, off, len);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +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.utility;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents a dynamic package and an arbitrary number of cached classes.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CachedPackage {
|
||||
private final Map<String, Optional<Class<?>>> cache;
|
||||
private final String packageName;
|
||||
private final ClassSource source;
|
||||
|
||||
/**
|
||||
* Construct a new cached package.
|
||||
* @param packageName - the name of the current package.
|
||||
* @param source - the class source.
|
||||
*/
|
||||
public CachedPackage(String packageName, ClassSource source) {
|
||||
this.packageName = packageName;
|
||||
this.cache = Maps.newConcurrentMap();
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a given class with a class name.
|
||||
* @param className - class name.
|
||||
* @param clazz - type of class.
|
||||
*/
|
||||
public void setPackageClass(String className, Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
cache.put(className, Optional.of(clazz));
|
||||
} else {
|
||||
cache.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class object of a specific class in the current package.
|
||||
* @param className - the specific class.
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find the given class.
|
||||
*/
|
||||
public Optional<Class<?>> getPackageClass(final String className) {
|
||||
Preconditions.checkNotNull(className, "className cannot be null!");
|
||||
|
||||
return cache.computeIfAbsent(className, x -> {
|
||||
try {
|
||||
return Optional.ofNullable(source.loadClass(combine(packageName, className)));
|
||||
} catch (ClassNotFoundException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Correctly combine a package name and the child class we're looking for.
|
||||
* @param packageName - name of the package, or an empty string for the default package.
|
||||
* @param className - the class name.
|
||||
* @return We full class path.
|
||||
*/
|
||||
public static String combine(String packageName, String className) {
|
||||
if (Strings.isNullOrEmpty(packageName))
|
||||
return className;
|
||||
if (Strings.isNullOrEmpty(className))
|
||||
return packageName;
|
||||
return packageName + "." + className;
|
||||
}
|
||||
}
|
||||
@@ -1,229 +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.utility;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Utility methods for sending chat messages.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ChatExtensions {
|
||||
// Used to sent chat messages
|
||||
private ProtocolManager manager;
|
||||
|
||||
// The chat packet constructor
|
||||
private static volatile PacketConstructor chatConstructor;
|
||||
|
||||
// Whether or not we have to use the post-1.6.1 chat format
|
||||
private static volatile Constructor<?> jsonConstructor = getJsonFormatConstructor();
|
||||
private static volatile MethodAccessor messageFactory;
|
||||
|
||||
public ChatExtensions(ProtocolManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message without invoking the packet listeners.
|
||||
* @param receiver - the receiver.
|
||||
* @param message - the message to send.
|
||||
* @throws InvocationTargetException If we were unable to send the message.
|
||||
*/
|
||||
public void sendMessageSilently(CommandSender receiver, String message) throws InvocationTargetException {
|
||||
if (receiver == null)
|
||||
throw new IllegalArgumentException("receiver cannot be NULL.");
|
||||
if (message == null)
|
||||
throw new IllegalArgumentException("message cannot be NULL.");
|
||||
|
||||
// Handle the player case by manually sending packets
|
||||
if (receiver instanceof Player) {
|
||||
sendMessageSilently((Player) receiver, message);
|
||||
} else {
|
||||
receiver.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message without invoking the packet listeners.
|
||||
* @param player - the player to send it to.
|
||||
* @param message - the message to send.
|
||||
* @throws InvocationTargetException If we were unable to send the message.
|
||||
*/
|
||||
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
||||
try {
|
||||
for (PacketContainer packet : createChatPackets(message)) {
|
||||
manager.sendServerPacket(player, packet, false);
|
||||
}
|
||||
} catch (FieldAccessException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct chat packet to send in order to display a given message.
|
||||
* @param message - the message to send.
|
||||
* @return The packets.
|
||||
*/
|
||||
public static PacketContainer[] createChatPackets(String message) {
|
||||
if (jsonConstructor != null) {
|
||||
if (chatConstructor == null) {
|
||||
Class<?> messageClass = jsonConstructor.getParameterTypes()[0];
|
||||
chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { messageClass });
|
||||
|
||||
// Try one of the string constructors
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
messageFactory = Accessors.getMethodAccessor(
|
||||
MinecraftReflection.getCraftMessageClass(), "fromString", String.class);
|
||||
} else {
|
||||
messageFactory = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(messageClass).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
requireModifier(Modifier.STATIC).
|
||||
parameterCount(1).
|
||||
parameterExactType(String.class).
|
||||
returnTypeMatches(FuzzyMatchers.matchParent()).
|
||||
build())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Minecraft 1.7.2 and later
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
Object[] components = (Object[]) messageFactory.invoke(null, message);
|
||||
PacketContainer[] packets = new PacketContainer[components.length];
|
||||
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
packets[i] = chatConstructor.createPacket(components[i]);
|
||||
}
|
||||
return packets;
|
||||
|
||||
// Minecraft 1.6.1 - 1.6.4
|
||||
} else {
|
||||
return new PacketContainer[] { chatConstructor.createPacket(messageFactory.invoke(null, message)) };
|
||||
}
|
||||
|
||||
} else {
|
||||
if (chatConstructor == null) {
|
||||
chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { message });
|
||||
}
|
||||
// Minecraft 1.6.0 and earlier
|
||||
return new PacketContainer[] { chatConstructor.createPacket(message) };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a message without invoking any packet listeners.
|
||||
* @param message - message to send.
|
||||
* @param permission - permission required to receieve the message. NULL to target everyone.
|
||||
* @throws InvocationTargetException If we were unable to send the message.
|
||||
*/
|
||||
public void broadcastMessageSilently(String message, String permission) throws InvocationTargetException {
|
||||
if (message == null)
|
||||
throw new IllegalArgumentException("message cannot be NULL.");
|
||||
|
||||
// Send this message to every online player
|
||||
for (Player player : Util.getOnlinePlayers()) {
|
||||
if (permission == null || player.hasPermission(permission)) {
|
||||
sendMessageSilently(player, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a flower box around a given message.
|
||||
* @param message - the message to print.
|
||||
* @param marginChar - the character to use as margin.
|
||||
* @param marginWidth - the width (in characters) of the left and right margin.
|
||||
* @param marginHeight - the height (in characters) of the top and buttom margin.
|
||||
* @return Flowerboxed message
|
||||
*/
|
||||
public static String[] toFlowerBox(String[] message, String marginChar, int marginWidth, int marginHeight) {
|
||||
String[] output = new String[message.length + marginHeight * 2];
|
||||
int width = getMaximumLength(message);
|
||||
|
||||
// Margins
|
||||
String topButtomMargin = Strings.repeat(marginChar, width + marginWidth * 2);
|
||||
String leftRightMargin = Strings.repeat(marginChar, marginWidth);
|
||||
|
||||
// Add left and right margin
|
||||
for (int i = 0; i < message.length; i++) {
|
||||
output[i + marginHeight] = leftRightMargin + Strings.padEnd(message[i], width, ' ') + leftRightMargin;
|
||||
}
|
||||
|
||||
// Insert top and bottom margin
|
||||
for (int i = 0; i < marginHeight; i++) {
|
||||
output[i] = topButtomMargin;
|
||||
output[output.length - i - 1] = topButtomMargin;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the longest line lenght in a list of strings.
|
||||
* @param lines - the lines.
|
||||
* @return Longest line lenght.
|
||||
*/
|
||||
private static int getMaximumLength(String[] lines) {
|
||||
int current = 0;
|
||||
|
||||
// Find the longest line
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (current < lines[i].length())
|
||||
current = lines[i].length();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a constructor for post-1.6.1 chat packets.
|
||||
* @return A constructor for JSON-based packets.
|
||||
*/
|
||||
static Constructor<?> getJsonFormatConstructor() {
|
||||
Class<?> chatPacket = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.CHAT, true);
|
||||
List<Constructor<?>> list = FuzzyReflection.fromClass(chatPacket).getConstructorList(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(1).
|
||||
parameterMatches(MinecraftReflection.getMinecraftObjectMatcher()).
|
||||
build()
|
||||
);
|
||||
|
||||
// First element or NULL
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class ClassSource {
|
||||
/**
|
||||
* Construct a class source from the default class loader.
|
||||
* @return A class source.
|
||||
*/
|
||||
public static ClassSource fromClassLoader() {
|
||||
return fromClassLoader(ClassSource.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class source from the default class loader and package.
|
||||
* @param packageName - the package that is prepended to every lookup.
|
||||
* @return A package source.
|
||||
*/
|
||||
public static ClassSource fromPackage(String packageName) {
|
||||
return fromClassLoader().usingPackage(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class source from the given class loader.
|
||||
* @param loader - the class loader.
|
||||
* @return The corresponding class source.
|
||||
*/
|
||||
public static ClassSource fromClassLoader(final ClassLoader loader) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
return loader.loadClass(canonicalName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class source from a mapping of canonical names and the corresponding classes.
|
||||
* If the map is null, it will be interpreted as an empty map. If the map does not contain a Class with the specified name, or that string maps to NULL explicitly, a {@link ClassNotFoundException} will be thrown.
|
||||
* @param map - map of class names and classes.
|
||||
* @return The class source.
|
||||
*/
|
||||
public static ClassSource fromMap(final Map<String, Class<?>> map) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
Class<?> loaded = map == null ? null : map.get(canonicalName);
|
||||
if(loaded == null){
|
||||
// Throw the appropriate exception if we can't load the class
|
||||
throw new ClassNotFoundException("The specified class could not be found by this ClassLoader.");
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A ClassLoader which will never successfully load a class.
|
||||
*/
|
||||
public static ClassSource empty(){
|
||||
return fromMap(Collections.<String, Class<?>>emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class source that will attempt lookups in each of the given sources in the order they are in the array, and return the first value that is found.
|
||||
* If the sources array is null or composed of any null elements, an exception will be thrown.
|
||||
* @param sources - the class sources.
|
||||
* @return A new class source.
|
||||
*/
|
||||
public static ClassSource attemptLoadFrom(final ClassSource... sources) {
|
||||
if(sources.length == 0){ // Throws NPE if sources is null, which is what we want
|
||||
return ClassSource.empty();
|
||||
}
|
||||
|
||||
ClassSource source = null;
|
||||
for(int i = 0; i < sources.length; i++){
|
||||
if(sources[i] == null){
|
||||
throw new IllegalArgumentException("Null values are not permitted as ClassSources.");
|
||||
}
|
||||
|
||||
source = source == null ? sources[i] : source.retry(sources[i]);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class source that will retry failed lookups in the given source.
|
||||
* @param other - the other class source.
|
||||
* @return A new class source.
|
||||
*/
|
||||
public ClassSource retry(final ClassSource other) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
try {
|
||||
return ClassSource.this.loadClass(canonicalName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return other.loadClass(canonicalName);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class source that prepends a specific package name to every lookup.
|
||||
* @param packageName - the package name to prepend.
|
||||
* @return The class source.
|
||||
*/
|
||||
public ClassSource usingPackage(final String packageName) {
|
||||
return new ClassSource() {
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
return ClassSource.this.loadClass(append(packageName, canonicalName));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to canonical names together.
|
||||
* @param a - the name to the left.
|
||||
* @param b - the name to the right.
|
||||
* @return The full canonical name, with a dot seperator.
|
||||
*/
|
||||
protected static String append(String a, String b) {
|
||||
boolean left = a.endsWith(".");
|
||||
boolean right = b.endsWith(".");
|
||||
|
||||
// Only add a dot if necessary
|
||||
if (left && right)
|
||||
return a.substring(0, a.length() - 1) + b;
|
||||
else if (left != right)
|
||||
return a + b;
|
||||
else
|
||||
return a + "." + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a class by name.
|
||||
* @param canonicalName - the full canonical name of the class.
|
||||
* @return The corresponding class. If the class is not found, NULL should <b>not</b> be returned, instead a {@code ClassNotFoundException} exception should be thrown.
|
||||
* @throws ClassNotFoundException If the class could not be found.
|
||||
*/
|
||||
public abstract Class<?> loadClass(String canonicalName) throws ClassNotFoundException;
|
||||
}
|
||||
@@ -1,27 +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.utility;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public final class Constants {
|
||||
public static final String PACKAGE_VERSION = "v1_13_R2";
|
||||
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
|
||||
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
|
||||
/**
|
||||
* Static methods for accessing Minecraft methods.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class MinecraftMethods {
|
||||
// For player connection
|
||||
private volatile static Method sendPacketMethod;
|
||||
|
||||
// For network manager
|
||||
private volatile static Method networkManagerHandle;
|
||||
private volatile static Method networkManagerPacketRead;
|
||||
|
||||
// For packet
|
||||
private volatile static Method packetReadByteBuf;
|
||||
private volatile static Method packetWriteByteBuf;
|
||||
|
||||
/**
|
||||
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
|
||||
* @return The send packet method.
|
||||
*/
|
||||
public static Method getSendPacketMethod() {
|
||||
if (sendPacketMethod == null) {
|
||||
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
|
||||
|
||||
try {
|
||||
sendPacketMethod = FuzzyReflection
|
||||
.fromClass(serverHandlerClass)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.nameRegex("sendPacket.*")
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.build());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// We can't use the method below on Netty
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
sendPacketMethod = FuzzyReflection.fromClass(serverHandlerClass).
|
||||
getMethodByParameters("sendPacket", MinecraftReflection.getPacketClass());
|
||||
return sendPacketMethod;
|
||||
}
|
||||
|
||||
Map<String, Method> netServer = getMethodList(
|
||||
serverHandlerClass, MinecraftReflection.getPacketClass());
|
||||
Map<String, Method> netHandler = getMethodList(
|
||||
MinecraftReflection.getNetHandlerClass(), MinecraftReflection.getPacketClass());
|
||||
|
||||
// Remove every method in net handler from net server
|
||||
for (String methodName : netHandler.keySet()) {
|
||||
netServer.remove(methodName);
|
||||
}
|
||||
|
||||
// The remainder is the send packet method
|
||||
if (netServer.size() == 1) {
|
||||
Method[] methods = netServer.values().toArray(new Method[0]);
|
||||
sendPacketMethod = methods[0];
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unable to find the sendPacket method in NetServerHandler/PlayerConnection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendPacketMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the disconnect method for a given player connection.
|
||||
* @param playerConnection - the player connection.
|
||||
* @return The
|
||||
*/
|
||||
public static Method getDisconnectMethod(Class<? extends Object> playerConnection) {
|
||||
try {
|
||||
return FuzzyReflection.fromClass(playerConnection).getMethodByName("disconnect.*");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just assume it's the first String method
|
||||
return FuzzyReflection.fromObject(playerConnection).getMethodByParameters("disconnect", String.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the handle(Packet, GenericFutureListener[]) method of network manager.
|
||||
* <p>
|
||||
* This only exists in version 1.7.2 and above.
|
||||
* @return The handle method.
|
||||
*/
|
||||
public static Method getNetworkManagerHandleMethod() {
|
||||
if (networkManagerHandle == null) {
|
||||
networkManagerHandle = FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
|
||||
.getMethodByParameters("handle", MinecraftReflection.getPacketClass());
|
||||
networkManagerHandle.setAccessible(true);
|
||||
}
|
||||
|
||||
return networkManagerHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packetRead(ChannelHandlerContext, Packet) method of NetworkMananger.
|
||||
* <p>
|
||||
* This only exists in version 1.7.2 and above.
|
||||
* @return The packetRead method.
|
||||
*/
|
||||
public static Method getNetworkManagerReadPacketMethod() {
|
||||
if (networkManagerPacketRead == null) {
|
||||
networkManagerPacketRead = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
|
||||
getMethodByParameters("packetRead", ChannelHandlerContext.class, MinecraftReflection.getPacketClass());
|
||||
networkManagerPacketRead.setAccessible(true);
|
||||
}
|
||||
return networkManagerPacketRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a method mapped list of every method with the given signature.
|
||||
* @param source - class source.
|
||||
* @param params - parameters.
|
||||
* @return Method mapped list.
|
||||
*/
|
||||
private static Map<String, Method> getMethodList(Class<?> source, Class<?>... params) {
|
||||
FuzzyReflection reflect = FuzzyReflection.fromClass(source, true);
|
||||
|
||||
return reflect.getMappedMethods(
|
||||
reflect.getMethodListByParameters(Void.TYPE, params)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Packet.read(PacketDataSerializer) method.
|
||||
* <p>
|
||||
* This only exists in version 1.7.2 and above.
|
||||
* @return The packet read method.
|
||||
*/
|
||||
public static Method getPacketReadByteBufMethod() {
|
||||
initializePacket();
|
||||
return packetReadByteBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Packet.write(PacketDataSerializer) method.
|
||||
* <p>
|
||||
* This only exists in version 1.7.2 and above.
|
||||
* @return The packet write method.
|
||||
*/
|
||||
public static Method getPacketWriteByteBufMethod() {
|
||||
initializePacket();
|
||||
return packetWriteByteBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the two read() and write() methods.
|
||||
*/
|
||||
private static void initializePacket() {
|
||||
// Initialize the methods
|
||||
if (packetReadByteBuf == null || packetWriteByteBuf == null) {
|
||||
// This object will allow us to detect which methods were called
|
||||
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
|
||||
enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass());
|
||||
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
|
||||
if (method.getName().contains("read")) {
|
||||
throw new ReadMethodException();
|
||||
}
|
||||
|
||||
if (method.getName().contains("write")) {
|
||||
throw new WriteMethodException();
|
||||
}
|
||||
|
||||
return proxy.invokeSuper(obj, args);
|
||||
});
|
||||
|
||||
// Create our proxy object
|
||||
Object javaProxy = enhancer.create(
|
||||
new Class<?>[] { MinecraftReflection.getByteBufClass() },
|
||||
new Object[] { Unpooled.buffer() }
|
||||
);
|
||||
|
||||
Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle();
|
||||
List<Method> candidates = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass())
|
||||
.getMethodListByParameters(Void.TYPE, new Class<?>[] { MinecraftReflection.getPacketDataSerializerClass() });
|
||||
|
||||
// Look through all the methods
|
||||
for (Method method : candidates) {
|
||||
try {
|
||||
method.invoke(lookPacket, javaProxy);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof ReadMethodException) {
|
||||
// Must be the reader
|
||||
packetReadByteBuf = method;
|
||||
} else if (e.getCause() instanceof WriteMethodException) {
|
||||
packetWriteByteBuf = method;
|
||||
} else {
|
||||
// throw new RuntimeException("Inner exception.", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Generic reflection error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (packetReadByteBuf == null)
|
||||
throw new IllegalStateException("Unable to find Packet.read(PacketDataSerializer)");
|
||||
if (packetWriteByteBuf == null)
|
||||
throw new IllegalStateException("Unable to find Packet.write(PacketDataSerializer)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal exception used to detect read methods.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class ReadMethodException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ReadMethodException() {
|
||||
super("A read method was executed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal exception used to detect write methods.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class WriteMethodException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public WriteMethodException() {
|
||||
super("A write method was executed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,155 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
// Thanks to Bergerkiller for his excellent hack. :D
|
||||
|
||||
// Copyright (C) 2013 bergerkiller
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException.Reason;
|
||||
|
||||
class RemappedClassSource extends ClassSource {
|
||||
private Object classRemapper;
|
||||
private Method mapType;
|
||||
private ClassLoader loader;
|
||||
|
||||
/**
|
||||
* Construct a new remapped class source using the default class loader.
|
||||
*/
|
||||
public RemappedClassSource() {
|
||||
this(RemappedClassSource.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new renampped class source with the provided class loader.
|
||||
* @param loader - the class loader.
|
||||
*/
|
||||
public RemappedClassSource(ClassLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load the MCPC remapper.
|
||||
* @return TRUE if we succeeded, FALSE otherwise.
|
||||
* @throws RemapperUnavailableException If the remapper is not present.
|
||||
*/
|
||||
public RemappedClassSource initialize() {
|
||||
try {
|
||||
Server server = Bukkit.getServer();
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("Bukkit not initialized.");
|
||||
}
|
||||
|
||||
String version = server.getVersion();
|
||||
if (!version.contains("MCPC") && !version.contains("Cauldron")) {
|
||||
throw new RemapperUnavailableException(Reason.MCPC_NOT_PRESENT);
|
||||
}
|
||||
|
||||
// Obtain the Class remapper used by MCPC+/Cauldron
|
||||
this.classRemapper = FieldUtils.readField(getClass().getClassLoader(), "remapper", true);
|
||||
|
||||
if (this.classRemapper == null) {
|
||||
throw new RemapperUnavailableException(Reason.REMAPPER_DISABLED);
|
||||
}
|
||||
|
||||
// Initialize some fields and methods used by the Jar Remapper
|
||||
Class<?> renamerClazz = classRemapper.getClass();
|
||||
|
||||
this.mapType = MethodUtils.getAccessibleMethod(renamerClazz, "map",
|
||||
new Class<?>[] { String.class });
|
||||
|
||||
return this;
|
||||
|
||||
} catch (RemapperUnavailableException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Damn it
|
||||
throw new RuntimeException("Cannot access MCPC remapper.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||
final String remapped = getClassName(canonicalName);
|
||||
|
||||
try {
|
||||
return loader.loadClass(remapped);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("Cannot find " + canonicalName + "(Remapped: " + remapped + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the obfuscated class name given an unobfuscated canonical class name.
|
||||
* @param path - the canonical class name.
|
||||
* @return The obfuscated class name.
|
||||
*/
|
||||
public String getClassName(String path) {
|
||||
try {
|
||||
String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/'));
|
||||
return remapped.replace('/', '.');
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot remap class name.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RemapperUnavailableException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public enum Reason {
|
||||
MCPC_NOT_PRESENT("The server is not running MCPC+/Cauldron"),
|
||||
REMAPPER_DISABLED("Running an MCPC+/Cauldron server but the remapper is unavailable. Please turn it on!");
|
||||
|
||||
private final String message;
|
||||
|
||||
private Reason(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a human-readable version of this reason.
|
||||
* @return The human-readable verison.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
private final Reason reason;
|
||||
|
||||
public RemapperUnavailableException(Reason reason) {
|
||||
super(reason.getMessage());
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the reasont he remapper is unavailable.
|
||||
* @return The reason.
|
||||
*/
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.NettyByteBufAdapter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtType;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Utility methods for reading and writing Minecraft objects to streams.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StreamSerializer {
|
||||
private static final StreamSerializer DEFAULT = new StreamSerializer();
|
||||
|
||||
// Cached methods
|
||||
private static MethodAccessor READ_ITEM_METHOD;
|
||||
private static MethodAccessor WRITE_ITEM_METHOD;
|
||||
|
||||
private static MethodAccessor READ_NBT_METHOD;
|
||||
private static MethodAccessor WRITE_NBT_METHOD;
|
||||
|
||||
private static MethodAccessor READ_STRING_METHOD;
|
||||
private static MethodAccessor WRITE_STRING_METHOD;
|
||||
|
||||
/**
|
||||
* Retrieve a default stream serializer.
|
||||
* @return A serializer.
|
||||
*/
|
||||
public static StreamSerializer getDefault() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable integer to an output stream.
|
||||
* @param destination - the destination.
|
||||
* @param value - the value to write.
|
||||
* @throws IOException The destination stream threw an exception.
|
||||
*/
|
||||
public void serializeVarInt(@Nonnull DataOutputStream destination, int value) throws IOException {
|
||||
Preconditions.checkNotNull(destination, "source cannot be NULL");
|
||||
|
||||
while ((value & 0xFFFFFF80) != 0) {
|
||||
destination.writeByte(value & 0x7F | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
destination.writeByte(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a variable integer from an input stream.
|
||||
* @param source - the source.
|
||||
* @return The integer.
|
||||
* @throws IOException The source stream threw an exception.
|
||||
*/
|
||||
public int deserializeVarInt(@Nonnull DataInputStream source) throws IOException {
|
||||
Preconditions.checkNotNull(source, "source cannot be NULL");
|
||||
|
||||
int result = 0;
|
||||
int length = 0;
|
||||
byte currentByte;
|
||||
do {
|
||||
currentByte = source.readByte();
|
||||
result |= (currentByte & 0x7F) << length++ * 7;
|
||||
if (length > 5)
|
||||
throw new RuntimeException("VarInt too big");
|
||||
} while ((currentByte & 0x80) == 0x80);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write or serialize a NBT compound to the given output stream.
|
||||
* <p>
|
||||
* Note: An NBT compound can be written to a stream even if it's NULL.
|
||||
*
|
||||
* @param output - the target output stream.
|
||||
* @param compound - the NBT compound to be serialized, or NULL to represent nothing.
|
||||
* @throws IOException If the operation fails due to reflection problems.
|
||||
*/
|
||||
public void serializeCompound(@Nonnull DataOutputStream output, NbtCompound compound) throws IOException {
|
||||
if (output == null)
|
||||
throw new IllegalArgumentException("Output stream cannot be NULL.");
|
||||
|
||||
// Get the NMS version of the compound
|
||||
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (WRITE_NBT_METHOD == null) {
|
||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("writeNbtCompound", /* a */
|
||||
MinecraftReflection.getNBTCompoundClass())
|
||||
);
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
|
||||
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
|
||||
|
||||
WRITE_NBT_METHOD.invoke(buf, handle);
|
||||
} else {
|
||||
if (WRITE_NBT_METHOD == null) {
|
||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(2).
|
||||
parameterDerivedOf(MinecraftReflection.getNBTBaseClass(), 0).
|
||||
parameterDerivedOf(DataOutput.class, 1).
|
||||
returnTypeVoid().
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
WRITE_NBT_METHOD.invoke(null, handle, output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read or deserialize an NBT compound from a input stream.
|
||||
* @param input - the target input stream.
|
||||
* @return The resulting compound, or NULL.
|
||||
* @throws IOException If the operation failed due to reflection or corrupt data.
|
||||
*/
|
||||
public NbtCompound deserializeCompound(@Nonnull DataInputStream input) throws IOException {
|
||||
if (input == null)
|
||||
throw new IllegalArgumentException("Input stream cannot be NULL.");
|
||||
Object nmsCompound = null;
|
||||
|
||||
// Invoke the correct method
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (READ_NBT_METHOD == null) {
|
||||
READ_NBT_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("readNbtCompound", /* h */
|
||||
MinecraftReflection.getNBTCompoundClass(), new Class<?>[0])
|
||||
);
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetReader(input);
|
||||
nmsCompound = READ_NBT_METHOD.invoke(buf);
|
||||
} else {
|
||||
if (READ_NBT_METHOD == null) {
|
||||
READ_NBT_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(1).
|
||||
parameterDerivedOf(DataInput.class).
|
||||
returnDerivedOf(MinecraftReflection.getNBTBaseClass()).
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
nmsCompound = READ_NBT_METHOD.invoke(null, input);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Cannot read item stack.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to an NBT Compound
|
||||
if (nmsCompound != null)
|
||||
return NbtFactory.fromNMSCompound(nmsCompound);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a string using the standard Minecraft UTF-16 encoding.
|
||||
* <p>
|
||||
* Note that strings cannot exceed 32767 characters, regardless if maximum lenght.
|
||||
* @param output - the output stream.
|
||||
* @param text - the string to serialize.
|
||||
* @throws IOException If the data in the string cannot be written.
|
||||
*/
|
||||
public void serializeString(@Nonnull DataOutputStream output, String text) throws IOException {
|
||||
if (output == null)
|
||||
throw new IllegalArgumentException("output stream cannot be NULL.");
|
||||
if (text == null)
|
||||
throw new IllegalArgumentException("text cannot be NULL.");
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (WRITE_STRING_METHOD == null) {
|
||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("writeString", /* a */
|
||||
String.class)
|
||||
);
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
|
||||
WRITE_STRING_METHOD.invoke(buf, text);
|
||||
} else {
|
||||
if (WRITE_STRING_METHOD == null) {
|
||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(2).
|
||||
parameterExactType(String.class, 0).
|
||||
parameterDerivedOf(DataOutput.class, 1).
|
||||
returnTypeVoid().
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
WRITE_STRING_METHOD.invoke(null, text, output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a string using the standard Minecraft UTF-16 encoding.
|
||||
* <p>
|
||||
* Note that strings cannot exceed 32767 characters, regardless if maximum length.
|
||||
* @param input - the input stream.
|
||||
* @param maximumLength - the maximum length of the string.
|
||||
* @return The deserialized string.
|
||||
* @throws IOException If deserializing fails
|
||||
*/
|
||||
public String deserializeString(@Nonnull DataInputStream input, int maximumLength) throws IOException {
|
||||
if (input == null)
|
||||
throw new IllegalArgumentException("Input stream cannot be NULL.");
|
||||
if (maximumLength > 32767)
|
||||
throw new IllegalArgumentException("Maximum length cannot exceed 32767 characters.");
|
||||
if (maximumLength < 0)
|
||||
throw new IllegalArgumentException("Maximum length cannot be negative.");
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (READ_STRING_METHOD == null) {
|
||||
READ_STRING_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("readString", /* c */
|
||||
String.class, new Class<?>[] { int.class })
|
||||
);
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetReader(input);
|
||||
return (String) READ_STRING_METHOD.invoke(buf, maximumLength);
|
||||
} else {
|
||||
if (READ_STRING_METHOD == null) {
|
||||
READ_STRING_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(2).
|
||||
parameterDerivedOf(DataInput.class, 0).
|
||||
parameterExactType(int.class, 1).
|
||||
returnTypeExact(String.class).
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
return (String) READ_STRING_METHOD.invoke(null, input, maximumLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an item stack as a base-64 encoded string.
|
||||
* <p>
|
||||
* Note: An ItemStack can be written to the serialized text even if it's NULL.
|
||||
*
|
||||
* @param stack - the item stack to serialize, or NULL to represent air/nothing.
|
||||
* @return A base-64 representation of the given item stack.
|
||||
* @throws IOException If the operation fails due to reflection problems.
|
||||
*/
|
||||
public String serializeItemStack(ItemStack stack) throws IOException {
|
||||
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||
byte[] bytes = null;
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
|
||||
|
||||
if (WRITE_ITEM_METHOD == null) {
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("writeStack", // a()
|
||||
MinecraftReflection.getItemStackClass()));
|
||||
}
|
||||
|
||||
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||
|
||||
bytes = buf.array();
|
||||
} else {
|
||||
if (WRITE_ITEM_METHOD == null) {
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(2).
|
||||
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
|
||||
parameterDerivedOf(DataOutput.class, 1).
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataOutput = new DataOutputStream(outputStream);
|
||||
|
||||
WRITE_ITEM_METHOD.invoke(null, nmsItem, dataOutput);
|
||||
|
||||
bytes = outputStream.toByteArray();
|
||||
}
|
||||
|
||||
return Base64Coder.encodeLines(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize an item stack from a base-64 encoded string.
|
||||
* @param input - base-64 encoded string.
|
||||
* @return A deserialized item stack, or NULL if the serialized ItemStack was also NULL.
|
||||
* @throws IOException If the operation failed due to reflection or corrupt data.
|
||||
*/
|
||||
public ItemStack deserializeItemStack(String input) throws IOException {
|
||||
Validate.notNull(input, "input cannot be null!");
|
||||
|
||||
Object nmsItem = null;
|
||||
byte[] bytes = Base64Coder.decodeLines(input);
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
ByteBuf buf = Unpooled.copiedBuffer(bytes);
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
|
||||
|
||||
if (READ_ITEM_METHOD == null) {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("readItemStack", // i(ItemStack)
|
||||
MinecraftReflection.getItemStackClass(), new Class<?>[0]));
|
||||
}
|
||||
|
||||
nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||
} else {
|
||||
if (READ_ITEM_METHOD == null) {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(1).
|
||||
parameterDerivedOf(DataInput.class).
|
||||
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
|
||||
DataInputStream inputStream = new DataInputStream(byteStream);
|
||||
|
||||
nmsItem = READ_ITEM_METHOD.invoke(null, inputStream);
|
||||
}
|
||||
|
||||
return nmsItem != null ? MinecraftReflection.getBukkitItemStack(nmsItem) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write or serialize an item stack to the given output stream.
|
||||
* <p>
|
||||
* To supply a byte array, wrap it in a {@link java.io.ByteArrayOutputStream ByteArrayOutputStream}
|
||||
* and {@link java.io.DataOutputStream DataOutputStream}.
|
||||
* <p>
|
||||
* Note: An ItemStack can be written to a stream even if it's NULL.
|
||||
*
|
||||
* @param output - the target output stream.
|
||||
* @param stack - the item stack that will be written, or NULL to represent air/nothing.
|
||||
* @throws IOException If the operation fails due to reflection problems.
|
||||
*/
|
||||
public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException {
|
||||
Validate.notNull("output cannot be null!");
|
||||
|
||||
// Get the NMS version of the ItemStack
|
||||
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (WRITE_ITEM_METHOD == null) {
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("writeStack", /* a */
|
||||
MinecraftReflection.getItemStackClass())
|
||||
);
|
||||
}
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
|
||||
|
||||
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||
|
||||
output.write(buf.array());
|
||||
} else {
|
||||
if (WRITE_ITEM_METHOD == null)
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(2).
|
||||
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
|
||||
parameterDerivedOf(DataOutput.class, 1).
|
||||
build())
|
||||
);
|
||||
|
||||
WRITE_ITEM_METHOD.invoke(null, nmsItem, output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read or deserialize an item stack from an underlying input stream.
|
||||
* <p>
|
||||
* To supply a byte array, wrap it in a {@link java.io.ByteArrayInputStream ByteArrayInputStream}
|
||||
* and {@link java.io.DataInputStream DataInputStream}.
|
||||
*
|
||||
* @param input - the target input stream.
|
||||
* @return The resulting item stack, or NULL if the serialized item stack was NULL.
|
||||
* @throws IOException If the operation failed due to reflection or corrupt data.
|
||||
* @deprecated This is a pretty hacky solution for backwards compatibility. See {@link #deserializeItemStack(DataInputStream)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ItemStack deserializeItemStack(DataInputStream input) throws IOException {
|
||||
Validate.notNull(input, "input cannot be null!");
|
||||
Object nmsItem = null;
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (READ_ITEM_METHOD == null) {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||
getMethodByParameters("readItemStack", /* i */
|
||||
MinecraftReflection.getItemStackClass(), new Class<?>[0])
|
||||
);
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[8192];
|
||||
input.read(bytes);
|
||||
|
||||
ByteBuf buf = Unpooled.copiedBuffer(bytes);
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
|
||||
|
||||
nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||
} else {
|
||||
if (READ_ITEM_METHOD == null) {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||
FuzzyMethodContract.newBuilder().
|
||||
parameterCount(1).
|
||||
parameterDerivedOf(DataInput.class).
|
||||
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
||||
build())
|
||||
);
|
||||
}
|
||||
|
||||
nmsItem = READ_ITEM_METHOD.invoke(null, input);
|
||||
}
|
||||
|
||||
// Convert back to a Bukkit item stack
|
||||
if (nmsItem != null)
|
||||
return MinecraftReflection.getBukkitItemStack(nmsItem);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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.utility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* General utility class
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
/**
|
||||
* Gets a list of currently online Players.
|
||||
* @return The list
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Player> getOnlinePlayers() {
|
||||
return (List<Player>) Bukkit.getOnlinePlayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a variable argument array into a List.
|
||||
* @param elements Array to convert
|
||||
* @return The list
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <E> List<E> asList(E... elements) {
|
||||
List<E> list = new ArrayList<E>(elements.length);
|
||||
for (E element : elements) {
|
||||
list.add(element);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this server is running Spigot or a Spigot fork. This works by checking
|
||||
* the server version for the Strings "Spigot" or "Paper".
|
||||
* @return True if it is, false if not.
|
||||
*/
|
||||
public static boolean isUsingSpigot() {
|
||||
return Bukkit.getServer().getVersion().contains("Spigot") || Bukkit.getServer().getVersion().contains("Paper");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,234 +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.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Copies a immutable net.minecraft.server.ChunkPosition, which represents a integer 3D vector.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ChunkPosition {
|
||||
|
||||
/**
|
||||
* Represents the null (0, 0, 0) origin.
|
||||
*/
|
||||
public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0);
|
||||
|
||||
private static Constructor<?> chunkPositionConstructor;
|
||||
|
||||
// Use protected members, like Bukkit
|
||||
protected final int x;
|
||||
protected final int y;
|
||||
protected final int z;
|
||||
|
||||
// Used to access a ChunkPosition, in case it's names are changed
|
||||
private static StructureModifier<Integer> intModifier;
|
||||
|
||||
/**
|
||||
* Construct an immutable 3D vector.
|
||||
* @param x - x coordinate
|
||||
* @param y - y coordinate
|
||||
* @param z - z coordinate
|
||||
*/
|
||||
public ChunkPosition(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an immutable integer 3D vector from a mutable Bukkit vector.
|
||||
* @param vector - the mutable real Bukkit vector to copy.
|
||||
*/
|
||||
public ChunkPosition(Vector vector) {
|
||||
if (vector == null)
|
||||
throw new IllegalArgumentException("Vector cannot be NULL.");
|
||||
this.x = vector.getBlockX();
|
||||
this.y = vector.getBlockY();
|
||||
this.z = vector.getBlockZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this instance to an equivalent real 3D vector.
|
||||
* @return Real 3D vector.
|
||||
*/
|
||||
public Vector toVector() {
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the x-coordinate.
|
||||
* @return X coordinate.
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the y-coordinate.
|
||||
* @return Y coordinate.
|
||||
*/
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the z-coordinate.
|
||||
* @return Z coordinate.
|
||||
*/
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current position and a given position together, producing a result position.
|
||||
* @param other - the other position.
|
||||
* @return The new result position.
|
||||
*/
|
||||
public ChunkPosition add(ChunkPosition other) {
|
||||
if (other == null)
|
||||
throw new IllegalArgumentException("other cannot be NULL");
|
||||
return new ChunkPosition(x + other.x, y + other.y, z + other.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current position and a given position together, producing a result position.
|
||||
* @param other - the other position.
|
||||
* @return The new result position.
|
||||
*/
|
||||
public ChunkPosition subtract(ChunkPosition other) {
|
||||
if (other == null)
|
||||
throw new IllegalArgumentException("other cannot be NULL");
|
||||
return new ChunkPosition(x - other.x, y - other.y, z - other.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply each dimension in the current position by the given factor.
|
||||
* @param factor - multiplier.
|
||||
* @return The new result.
|
||||
*/
|
||||
public ChunkPosition multiply(int factor) {
|
||||
return new ChunkPosition(x * factor, y * factor, z * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide each dimension in the current position by the given divisor.
|
||||
* @param divisor - the divisor.
|
||||
* @return The new result.
|
||||
*/
|
||||
public ChunkPosition divide(int divisor) {
|
||||
if (divisor == 0)
|
||||
throw new IllegalArgumentException("Cannot divide by null.");
|
||||
return new ChunkPosition(x / divisor, y / divisor, z / divisor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert between NMS ChunkPosition and the wrapper instance.
|
||||
* @return A new converter.
|
||||
*/
|
||||
public static EquivalentConverter<ChunkPosition> getConverter() {
|
||||
return new EquivalentConverter<ChunkPosition>() {
|
||||
@Override
|
||||
public Object getGeneric(ChunkPosition specific) {
|
||||
if (chunkPositionConstructor == null) {
|
||||
try {
|
||||
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
|
||||
getConstructor(int.class, int.class, int.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find chunk position constructor.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the underlying ChunkPosition
|
||||
try {
|
||||
Object result = chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct ChunkPosition.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkPosition getSpecific(Object generic) {
|
||||
if (MinecraftReflection.isChunkPosition(generic)) {
|
||||
// Use a structure modifier
|
||||
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
|
||||
|
||||
// Damn it all
|
||||
if (intModifier.size() < 3) {
|
||||
throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields.");
|
||||
}
|
||||
|
||||
if (intModifier.size() >= 3) {
|
||||
try {
|
||||
StructureModifier<Integer> instance = intModifier.withTarget(generic);
|
||||
ChunkPosition result = new ChunkPosition(instance.read(0), instance.read(1), instance.read(2));
|
||||
return result;
|
||||
} catch (FieldAccessException e) {
|
||||
// This is an exeptional work-around, so we don't want to burden the caller with the messy details
|
||||
throw new RuntimeException("Field access error.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return NULL
|
||||
return null;
|
||||
}
|
||||
|
||||
// Thanks Java Generics!
|
||||
@Override
|
||||
public Class<ChunkPosition> getSpecificType() {
|
||||
return ChunkPosition.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Fast checks
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
|
||||
// Only compare objects of similar type
|
||||
if (obj instanceof ChunkPosition) {
|
||||
ChunkPosition other = (ChunkPosition) obj;
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrappedChunkPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2017 Dan Mulloy
|
||||
*
|
||||
* 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.wrappers;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
|
||||
/**
|
||||
* Utility class for converters
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class Converters {
|
||||
|
||||
/**
|
||||
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
|
||||
* @param converter Underlying converter
|
||||
* @param <T> Element type
|
||||
* @return An ignore null converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
|
||||
return new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return generic != null ? converter.getSpecific(generic) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return specific != null ? converter.getGeneric(specific) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return converter.getSpecificType();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a converter that passes generic and specific values through without converting.
|
||||
* @param clazz Element class
|
||||
* @param <T> Element type
|
||||
* @return A passthrough converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
|
||||
return ignoreNull(new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return (T) generic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return specific;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return clazz;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
|
||||
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
|
||||
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
|
||||
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
|
||||
* @param <T> Wrapper type
|
||||
* @return A handle converter
|
||||
*/
|
||||
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
|
||||
final Function<Object, T> fromHandle) {
|
||||
return new EquivalentConverter<T>() {
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
return fromHandle.apply(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
return toHandle.apply(specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,189 +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.wrappers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class PlayerInfoData {
|
||||
private static Constructor<?> constructor;
|
||||
|
||||
private final int latency;
|
||||
private final NativeGameMode gameMode;
|
||||
private final WrappedGameProfile profile;
|
||||
private final WrappedChatComponent displayName;
|
||||
|
||||
// This is the same order as the NMS class, minus the packet (which isn't a field)
|
||||
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName) {
|
||||
this.profile = profile;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GameProfile of the player represented by this data.
|
||||
* @return The GameProfile
|
||||
*/
|
||||
public WrappedGameProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replaced by {@link #getLatency()}
|
||||
*/
|
||||
@Deprecated
|
||||
public int getPing() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latency between the client and the server.
|
||||
* @return The latency
|
||||
*/
|
||||
public int getLatency() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GameMode of the player represented by this data.
|
||||
* @return The GameMode
|
||||
*/
|
||||
public NativeGameMode getGameMode() {
|
||||
return gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name of the player represented by this data.
|
||||
* @return The display name
|
||||
*/
|
||||
public WrappedChatComponent getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert between NMS PlayerInfoData and the wrapper instance.
|
||||
* @return A new converter.
|
||||
*/
|
||||
public static EquivalentConverter<PlayerInfoData> getConverter() {
|
||||
return new EquivalentConverter<PlayerInfoData>() {
|
||||
@Override
|
||||
public Object getGeneric(PlayerInfoData specific) {
|
||||
if (constructor == null) {
|
||||
try {
|
||||
// public PlayerInfoData(Packet, GameProfile, int, GameMode, ChatComponent)
|
||||
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(
|
||||
MinecraftReflection.getMinecraftClass("PacketPlayOutPlayerInfo"),
|
||||
MinecraftReflection.getGameProfileClass(),
|
||||
int.class,
|
||||
EnumWrappers.getGameModeClass(),
|
||||
MinecraftReflection.getIChatBaseComponentClass()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to construct the underlying PlayerInfoData
|
||||
|
||||
try {
|
||||
// public PlayerInfoData(null, GameProfile, ping, GameMode, ChatComponent)
|
||||
// The packet isn't a field, so it can be null
|
||||
Object result = constructor.newInstance(
|
||||
null,
|
||||
specific.profile.handle,
|
||||
specific.latency,
|
||||
EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode),
|
||||
specific.displayName != null ? specific.displayName.handle : null
|
||||
);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInfoData getSpecific(Object generic) {
|
||||
if (MinecraftReflection.isPlayerInfoData(generic)) {
|
||||
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false)
|
||||
.withTarget(generic);
|
||||
|
||||
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
|
||||
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
|
||||
WrappedGameProfile gameProfile = gameProfiles.read(0);
|
||||
|
||||
StructureModifier<Integer> ints = modifier.withType(int.class);
|
||||
int latency = ints.read(0);
|
||||
|
||||
StructureModifier<NativeGameMode> gameModes = modifier.withType(
|
||||
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
|
||||
NativeGameMode gameMode = gameModes.read(0);
|
||||
|
||||
StructureModifier<WrappedChatComponent> displayNames = modifier.withType(
|
||||
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
|
||||
WrappedChatComponent displayName = displayNames.read(0);
|
||||
|
||||
return new PlayerInfoData(gameProfile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
// Otherwise, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// Thanks Java Generics!
|
||||
@Override
|
||||
public Class<PlayerInfoData> getSpecificType() {
|
||||
return PlayerInfoData.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Fast checks
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
|
||||
// Only compare objects of similar type
|
||||
if (obj instanceof PlayerInfoData) {
|
||||
PlayerInfoData other = (PlayerInfoData) obj;
|
||||
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
|
||||
&& displayName.equals(other.displayName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(latency, gameMode, profile, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s",
|
||||
latency, gameMode, profile, displayName);
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +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.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Allows access to a chunk coordinate.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class WrappedChunkCoordinate extends AbstractWrapper implements Comparable<WrappedChunkCoordinate> {
|
||||
/**
|
||||
* If TRUE, NULLs should be put before non-null instances of this class.
|
||||
*/
|
||||
private static final boolean LARGER_THAN_NULL = true;
|
||||
|
||||
// Used to access a ChunkCoordinate
|
||||
private static StructureModifier<Integer> SHARED_MODIFIER;
|
||||
|
||||
// The current modifier
|
||||
private StructureModifier<Integer> handleModifier;
|
||||
|
||||
/**
|
||||
* Create a new empty wrapper.
|
||||
*/
|
||||
public WrappedChunkCoordinate() {
|
||||
super(MinecraftReflection.getChunkCoordinatesClass());
|
||||
|
||||
try {
|
||||
setHandle(getHandleType().newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct chunk coordinate.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapper for a specific chunk coordinates.
|
||||
* @param handle - the NMS chunk coordinates.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public WrappedChunkCoordinate(Comparable handle) {
|
||||
super(MinecraftReflection.getChunkCoordinatesClass());
|
||||
setHandle(handle);
|
||||
}
|
||||
|
||||
// Ensure that the structure modifier is initialized
|
||||
private StructureModifier<Integer> getModifier() {
|
||||
if (SHARED_MODIFIER == null)
|
||||
SHARED_MODIFIER = new StructureModifier<Object>(handle.getClass(), null, false).withType(int.class);
|
||||
if (handleModifier == null)
|
||||
handleModifier = SHARED_MODIFIER.withTarget(handle);
|
||||
return handleModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapper with specific values.
|
||||
* @param x - the x coordinate.
|
||||
* @param y - the y coordinate.
|
||||
* @param z - the z coordinate.
|
||||
*/
|
||||
public WrappedChunkCoordinate(int x, int y, int z) {
|
||||
this();
|
||||
setX(x);
|
||||
setY(y);
|
||||
setZ(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a chunk coordinate wrapper from a given position.
|
||||
* @param position - the given position.
|
||||
*/
|
||||
public WrappedChunkCoordinate(ChunkPosition position) {
|
||||
this(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the x coordinate of the underlying coordinate.
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public int getX() {
|
||||
return getModifier().read(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x coordinate of the underlying coordinate.
|
||||
* @param newX - the new x coordinate.
|
||||
*/
|
||||
public void setX(int newX) {
|
||||
getModifier().write(0, newX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the y coordinate of the underlying coordinate.
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public int getY() {
|
||||
return getModifier().read(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the y coordinate of the underlying coordinate.
|
||||
* @param newY - the new y coordinate.
|
||||
*/
|
||||
public void setY(int newY) {
|
||||
getModifier().write(1, newY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the z coordinate of the underlying coordinate.
|
||||
* @return The z coordinate.
|
||||
*/
|
||||
public int getZ() {
|
||||
return getModifier().read(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the z coordinate of the underlying coordiate.
|
||||
* @param newZ - the new z coordinate.
|
||||
*/
|
||||
public void setZ(int newZ) {
|
||||
getModifier().write(2, newZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an immutable chunk position from this coordinate.
|
||||
* @return The new immutable chunk position.
|
||||
*/
|
||||
public ChunkPosition toPosition() {
|
||||
return new ChunkPosition(getX(), getY(), getZ());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public int compareTo(WrappedChunkCoordinate other) {
|
||||
// We'll handle NULL objects too, unlike ChunkCoordinates
|
||||
if (other.handle == null)
|
||||
return LARGER_THAN_NULL ? -1 : 1;
|
||||
else
|
||||
return ((Comparable<Object>) handle).compareTo(other.handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ChunkCoordinate [x: %s, y: %s, z: %s]", getX(), getY(), getZ());
|
||||
}
|
||||
}
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
package com.comphenix.protocol.wrappers.nbt.io;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtList;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtWrapper;
|
||||
|
||||
public class NbtBinarySerializer {
|
||||
private static final Class<?> NBT_BASE_CLASS = MinecraftReflection.getNBTBaseClass();
|
||||
|
||||
private interface LoadMethod {
|
||||
/**
|
||||
* Load an NBT compound from a given stream.
|
||||
* @param input - the input stream.
|
||||
* @return The loaded NBT compound.
|
||||
*/
|
||||
public abstract Object loadNbt(DataInput input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from the NBTBase static method pre-1.7.2.
|
||||
*/
|
||||
private static class LoadMethodNbtClass implements LoadMethod {
|
||||
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class);
|
||||
|
||||
@Override
|
||||
public Object loadNbt(DataInput input) {
|
||||
return accessor.invoke(null, input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.2 - 1.7.5
|
||||
*/
|
||||
private static class LoadMethodWorldUpdate implements LoadMethod {
|
||||
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class);
|
||||
|
||||
@Override
|
||||
public Object loadNbt(DataInput input) {
|
||||
return accessor.invoke(null, input, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.8
|
||||
*/
|
||||
private static class LoadMethodSkinUpdate implements LoadMethod {
|
||||
private Class<?> readLimitClass = MinecraftReflection.getNBTReadLimiterClass();
|
||||
private Object readLimiter = FuzzyReflection.fromClass(readLimitClass).getSingleton();
|
||||
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class, readLimitClass);
|
||||
|
||||
@Override
|
||||
public Object loadNbt(DataInput input) {
|
||||
return accessor.invoke(null, input, 0, readLimiter);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to read and write NBT
|
||||
private static Method methodWrite;
|
||||
|
||||
/**
|
||||
* Method selected for loading NBT compounds.
|
||||
*/
|
||||
private static LoadMethod loadMethod;
|
||||
|
||||
/**
|
||||
* Retrieve a default instance of the NBT binary serializer.
|
||||
*/
|
||||
public static final NbtBinarySerializer DEFAULT = new NbtBinarySerializer();
|
||||
|
||||
/**
|
||||
* Write the content of a wrapped NBT tag to a stream.
|
||||
* @param <TType> Type
|
||||
* @param value - the NBT tag to write.
|
||||
* @param destination - the destination stream.
|
||||
*/
|
||||
public <TType> void serialize(NbtBase<TType> value, DataOutput destination) {
|
||||
if (methodWrite == null) {
|
||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||
|
||||
// Use the base class
|
||||
methodWrite = getUtilityClass().
|
||||
getMethodByParameters("writeNBT", base, DataOutput.class);
|
||||
methodWrite.setAccessible(true);
|
||||
}
|
||||
|
||||
try {
|
||||
methodWrite.invoke(null, NbtFactory.fromBase(value).getHandle(), destination);
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Unable to write NBT " + value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT tag from a stream.
|
||||
* @param <TType> Type
|
||||
* @param source - the input stream.
|
||||
* @return An NBT tag.
|
||||
*/
|
||||
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
|
||||
LoadMethod method = loadMethod;
|
||||
|
||||
if (loadMethod == null) {
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
try {
|
||||
method = new LoadMethodWorldUpdate();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Cannot find that method - must be in 1.7.8
|
||||
method = new LoadMethodSkinUpdate();
|
||||
}
|
||||
} else {
|
||||
method = new LoadMethodNbtClass();
|
||||
}
|
||||
|
||||
// Save the selected method
|
||||
loadMethod = method;
|
||||
}
|
||||
|
||||
try {
|
||||
return NbtFactory.fromNMS(method.loadNbt(source), null);
|
||||
} catch (Exception e) {
|
||||
throw new FieldAccessException("Unable to read NBT from " + source, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodAccessor getNbtLoadMethod(Class<?>... parameters) {
|
||||
return Accessors.getMethodAccessor(getUtilityClass().getMethodByParameters("load", NBT_BASE_CLASS, parameters), true);
|
||||
}
|
||||
|
||||
private static FuzzyReflection getUtilityClass() {
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
return FuzzyReflection.fromClass(MinecraftReflection.getNbtCompressedStreamToolsClass(), true);
|
||||
} else {
|
||||
return FuzzyReflection.fromClass(MinecraftReflection.getNBTBaseClass(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from a stream.
|
||||
* @param source - the input stream.
|
||||
* @return An NBT compound.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public NbtCompound deserializeCompound(DataInput source) {
|
||||
// I always seem to override generics ...
|
||||
return (NbtCompound) (NbtBase) deserialize(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT list from a stream.
|
||||
* @param <T> Type
|
||||
* @param source - the input stream.
|
||||
* @return An NBT list.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> NbtList<T> deserializeList(DataInput source) {
|
||||
return (NbtList<T>) (NbtBase) deserialize(source);
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
-524
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +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 org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
/**
|
||||
* Represents a single delayed task.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class DelayedSingleTask {
|
||||
|
||||
protected int taskID = -1;
|
||||
protected Plugin plugin;
|
||||
protected BukkitScheduler scheduler;
|
||||
protected boolean closed;
|
||||
|
||||
/**
|
||||
* Create a single task scheduler.
|
||||
* @param plugin - owner plugin.
|
||||
*/
|
||||
public DelayedSingleTask(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = plugin.getServer().getScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single task scheduler.
|
||||
* @param plugin - owner plugin.
|
||||
* @param scheduler - specialized scheduler.
|
||||
*/
|
||||
public DelayedSingleTask(Plugin plugin, BukkitScheduler scheduler) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a single task for execution.
|
||||
* <p>
|
||||
* Any previously scheduled task will be automatically cancelled.
|
||||
* <p>
|
||||
* Note that a tick delay of zero will execute the task immediately.
|
||||
*
|
||||
* @param ticksDelay - number of ticks before the task is executed.
|
||||
* @param task - the task to schedule.
|
||||
* @return TRUE if the task was successfully scheduled or executed, FALSE otherwise.
|
||||
*/
|
||||
public boolean schedule(long ticksDelay, Runnable task) {
|
||||
if (ticksDelay < 0)
|
||||
throw new IllegalArgumentException("Tick delay cannot be negative.");
|
||||
if (task == null)
|
||||
throw new IllegalArgumentException("task cannot be NULL");
|
||||
if (closed)
|
||||
return false;
|
||||
|
||||
// Special case
|
||||
if (ticksDelay == 0) {
|
||||
task.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Boilerplate, boilerplate
|
||||
final Runnable dispatch = task;
|
||||
|
||||
// Don't run multiple tasks!
|
||||
cancel();
|
||||
taskID = scheduler.scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dispatch.run();
|
||||
taskID = -1;
|
||||
}
|
||||
}, ticksDelay);
|
||||
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a future task is scheduled to be executed.
|
||||
* @return TRUE if a current task has been scheduled for execution, FALSE otherwise.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return taskID >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a future task from being executed.
|
||||
* @return TRUE if a task was cancelled, FALSE otherwise.
|
||||
*/
|
||||
public boolean cancel() {
|
||||
if (isRunning()) {
|
||||
scheduler.cancelTask(taskID);
|
||||
taskID = -1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the raw task ID.
|
||||
* @return Raw task ID, or negative one if no task has been scheduled.
|
||||
*/
|
||||
public int getTaskID() {
|
||||
return taskID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the plugin this task belongs to.
|
||||
* @return The plugin scheduling the current taks.
|
||||
*/
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the current task and all future tasks scheduled by this instance.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
cancel();
|
||||
plugin = null;
|
||||
scheduler = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
}
|
||||
}
|
||||
-256
@@ -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;
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +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 com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||
|
||||
/**
|
||||
* Represents an object that initiate the packet listeners.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ListenerInvoker {
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public abstract void invokePacketRecieving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public abstract void invokePacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the associated ID of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return The packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract int getPacketID(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the associated type of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
public abstract PacketType getPacketType(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the object responsible for intercepting write packets.
|
||||
* @return Object that intercepts write packets.
|
||||
*/
|
||||
public InterceptWritePacket getInterceptWritePacket();
|
||||
|
||||
/**
|
||||
* Determine if a given packet requires input buffering.
|
||||
* @param packetId - the packet to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean requireInputBuffer(int packetId);
|
||||
|
||||
/**
|
||||
* Associate a given class with the given packet ID. Internal method.
|
||||
* @param clazz - class to associate.
|
||||
*/
|
||||
public abstract void unregisterPacketClass(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Register a given class in the packet registry. Internal method.
|
||||
* @param clazz - class to register.
|
||||
* @param packetID - the the new associated packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void registerPacketClass(Class<?> clazz, int packetID);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketOutputHandler;
|
||||
import com.comphenix.protocol.events.PacketPostListener;
|
||||
import com.comphenix.protocol.events.ScheduledPacket;
|
||||
|
||||
/**
|
||||
* Represents a processor for network markers.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NetworkProcessor {
|
||||
private ErrorReporter reporter;
|
||||
|
||||
/**
|
||||
* Construct a new network processor.
|
||||
* @param reporter - the reporter.
|
||||
*/
|
||||
public NetworkProcessor(ErrorReporter reporter) {
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the serialized packet byte array with the given network marker.
|
||||
* @param event - current packet event.
|
||||
* @param marker - the network marker.
|
||||
* @param input - the input array.
|
||||
* @return The output array.
|
||||
*/
|
||||
public byte[] processOutput(PacketEvent event, NetworkMarker marker, final byte[] input) {
|
||||
// Bit of a hack - but we need the performance
|
||||
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
|
||||
marker.getOutputHandlers();
|
||||
byte[] output = input;
|
||||
|
||||
// Let each handler prepare the actual output
|
||||
while (!handlers.isEmpty()) {
|
||||
PacketOutputHandler handler = handlers.poll();
|
||||
|
||||
try {
|
||||
byte[] changed = handler.handle(event, output);
|
||||
|
||||
// Don't break just because a plugin returned NULL
|
||||
if (changed != null) {
|
||||
output = changed;
|
||||
} else {
|
||||
throw new IllegalStateException("Handler cannot return a NULL array.");
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportMinimal(handler.getPlugin(), "PacketOutputHandler.handle()", e);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the post listeners and packet transmission, if any.
|
||||
* @param event - PacketEvent
|
||||
* @param marker - the network marker, or NULL.
|
||||
*/
|
||||
public void invokePostEvent(PacketEvent event, NetworkMarker marker) {
|
||||
if (marker == null)
|
||||
return;
|
||||
|
||||
if (NetworkMarker.hasPostListeners(marker)) {
|
||||
// Invoke every sent listener
|
||||
for (PacketPostListener listener : marker.getPostListeners()) {
|
||||
try {
|
||||
listener.onPostEvent(event);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
sendScheduledPackets(marker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send any scheduled packets.
|
||||
* @param marker - the network marker.
|
||||
*/
|
||||
private void sendScheduledPackets(NetworkMarker marker) {
|
||||
// Next, invoke post packet transmission
|
||||
List<ScheduledPacket> scheduled = NetworkMarker.readScheduledPackets(marker);
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
if (scheduled != null) {
|
||||
for (ScheduledPacket packet : scheduled) {
|
||||
packet.schedule(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-256
@@ -1,256 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.executors.BukkitFutures;
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.player.InjectedServerConnection;
|
||||
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
||||
public class PacketFilterBuilder {
|
||||
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
|
||||
public static final ReportType REPORT_SPIGOT_IS_DELAYING_INJECTOR = new ReportType("Delaying due to Spigot.");
|
||||
|
||||
private ClassLoader classLoader;
|
||||
private Server server;
|
||||
private Plugin library;
|
||||
private MinecraftVersion mcVersion;
|
||||
private DelayedSingleTask unhookTask;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// Whether or not we need to enable Netty
|
||||
private AsyncFilterManager asyncManager;
|
||||
private boolean nettyEnabled;
|
||||
|
||||
/**
|
||||
* Update the current class loader.
|
||||
* @param classLoader - current class loader.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder classLoader(@Nonnull ClassLoader classLoader) {
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
this.classLoader = classLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current server.
|
||||
* @param server - current server.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder server(@Nonnull Server server) {
|
||||
if (server == null)
|
||||
throw new IllegalArgumentException("server cannot be NULL.");
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a reference to the plugin instance of ProtocolLib.
|
||||
* @param library - plugin instance.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder library(@Nonnull Plugin library) {
|
||||
if (library == null)
|
||||
throw new IllegalArgumentException("library cannot be NULL.");
|
||||
this.library = library;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current Minecraft version.
|
||||
* @param mcVersion - Minecraft version.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder minecraftVersion(@Nonnull MinecraftVersion mcVersion) {
|
||||
if (mcVersion == null)
|
||||
throw new IllegalArgumentException("minecraftVersion cannot be NULL.");
|
||||
this.mcVersion = mcVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the task used to delay unhooking when ProtocolLib is no in use.
|
||||
* @param unhookTask - the unhook task.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder unhookTask(@Nonnull DelayedSingleTask unhookTask) {
|
||||
if (unhookTask == null)
|
||||
throw new IllegalArgumentException("unhookTask cannot be NULL.");
|
||||
this.unhookTask = unhookTask;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error reporter.
|
||||
* @param reporter - new error reporter.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketFilterBuilder reporter(@Nonnull ErrorReporter reporter) {
|
||||
if (reporter == null)
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
this.reporter = reporter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should prepare to hook Netty in Spigot.
|
||||
* <p>
|
||||
* This is calculated in the {@link #build()} method.
|
||||
* @return TRUE if we should, FALSE otherwise.
|
||||
*/
|
||||
public boolean isNettyEnabled() {
|
||||
return nettyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class loader set in this builder.
|
||||
* @return The class loader.
|
||||
*/
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current CraftBukkit server.
|
||||
* @return Current server.
|
||||
*/
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the current ProtocolLib instance.
|
||||
* @return ProtocolLib.
|
||||
*/
|
||||
public Plugin getLibrary() {
|
||||
return library;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current Minecraft version.
|
||||
* @return Current version.
|
||||
*/
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
return mcVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the task that is used to delay unhooking when ProtocolLib is no in use.
|
||||
* @return The unhook task.
|
||||
*/
|
||||
public DelayedSingleTask getUnhookTask() {
|
||||
return unhookTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error reporter.
|
||||
* @return Error reporter.
|
||||
*/
|
||||
public ErrorReporter getReporter() {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the asynchronous manager.
|
||||
* <p>
|
||||
* This is first constructed the {@link #build()} method.
|
||||
* @return The asynchronous manager.
|
||||
*/
|
||||
public AsyncFilterManager getAsyncManager() {
|
||||
return asyncManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet filter manager.
|
||||
* @return A new packet filter manager.
|
||||
*/
|
||||
public InternalManager build() {
|
||||
if (reporter == null)
|
||||
throw new IllegalArgumentException("reporter cannot be NULL.");
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
|
||||
asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
|
||||
nettyEnabled = false;
|
||||
|
||||
// Spigot
|
||||
if (SpigotPacketInjector.canUseSpigotListener()) {
|
||||
// If the server hasn't loaded yet - wait
|
||||
if (InjectedServerConnection.getServerConnection(reporter, server) == null) {
|
||||
// We need to delay this until we know if Netty is enabled
|
||||
final DelayedPacketManager delayed = new DelayedPacketManager(reporter, mcVersion);
|
||||
|
||||
// They must reference each other
|
||||
delayed.setAsynchronousManager(asyncManager);
|
||||
asyncManager.setManager(delayed);
|
||||
|
||||
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class),
|
||||
new FutureCallback<WorldInitEvent>() {
|
||||
@Override
|
||||
public void onSuccess(WorldInitEvent event) {
|
||||
// Nevermind
|
||||
if (delayed.isClosed())
|
||||
return;
|
||||
|
||||
try {
|
||||
registerSpigot(delayed);
|
||||
} catch (Exception e) {
|
||||
onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
reporter.reportWarning(PacketFilterBuilder.this, Report
|
||||
.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
|
||||
}
|
||||
});
|
||||
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_SPIGOT_IS_DELAYING_INJECTOR));
|
||||
|
||||
// Let plugins use this version instead
|
||||
return delayed;
|
||||
} else {
|
||||
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||
InjectedServerConnection.getServerConnection(reporter, server));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise - construct the packet filter manager right away
|
||||
return buildInternal();
|
||||
}
|
||||
|
||||
private void registerSpigot(DelayedPacketManager delayed) {
|
||||
// Use netty if we have a non-standard ServerConnection class
|
||||
nettyEnabled = !MinecraftReflection.isMinecraftObject(
|
||||
InjectedServerConnection.getServerConnection(reporter, server));
|
||||
|
||||
// Switch to the standard manager
|
||||
delayed.setDelegate(buildInternal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new packet filter manager without checking for Netty.
|
||||
* @return A new packet filter manager.
|
||||
*/
|
||||
private PacketFilterManager buildInternal() {
|
||||
PacketFilterManager manager = new PacketFilterManager(this);
|
||||
|
||||
// It's a cyclic reference, but it's too late to fix now
|
||||
asyncManager.setManager(manager);
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
-1175
File diff suppressed because it is too large.
Load diff
-234
@@ -1,234 +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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class BootstrapList implements List<Object> {
|
||||
private List<Object> delegate;
|
||||
private ChannelHandler handler;
|
||||
|
||||
/**
|
||||
* Construct a new bootstrap list.
|
||||
* @param delegate - the delegate.
|
||||
* @param handler - the channel handler to add.
|
||||
*/
|
||||
public BootstrapList(List<Object> delegate, ChannelHandler handler) {
|
||||
this.delegate = delegate;
|
||||
this.handler = handler;
|
||||
|
||||
// Process all existing bootstraps
|
||||
for (Object item : this) {
|
||||
processElement(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean add(Object element) {
|
||||
processElement(element);
|
||||
return delegate.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends Object> collection) {
|
||||
List<Object> copy = Lists.newArrayList(collection);
|
||||
|
||||
// Process the collection before we pass it on
|
||||
for (Object element : copy) {
|
||||
processElement(element);
|
||||
}
|
||||
return delegate.addAll(copy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object set(int index, Object element) {
|
||||
Object old = delegate.set(index, element);
|
||||
|
||||
// Handle the old future, and the newly inserted future
|
||||
if (old != element) {
|
||||
unprocessElement(old);
|
||||
processElement(element);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single element.
|
||||
* @param element - the element.
|
||||
*/
|
||||
protected void processElement(Object element) {
|
||||
if (element instanceof ChannelFuture) {
|
||||
processBootstrap((ChannelFuture) element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unprocess a single element.
|
||||
* @param element - the element to unprocess.
|
||||
*/
|
||||
protected void unprocessElement(Object element) {
|
||||
if (element instanceof ChannelFuture) {
|
||||
unprocessBootstrap((ChannelFuture) element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single channel future.
|
||||
* @param future - the future.
|
||||
*/
|
||||
protected void processBootstrap(ChannelFuture future) {
|
||||
// Important: Must be addFirst()
|
||||
future.channel().pipeline().addFirst(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert any changes we made to the channel future.
|
||||
* @param future - the future.
|
||||
*/
|
||||
protected void unprocessBootstrap(ChannelFuture future) {
|
||||
final Channel channel = future.channel();
|
||||
|
||||
// For thread safety - see ChannelInjector.close()
|
||||
channel.eventLoop().submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
channel.pipeline().remove(handler);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and revert all changes.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
for (Object element : this)
|
||||
unprocessElement(element);
|
||||
}
|
||||
|
||||
// Boiler plate
|
||||
@Override
|
||||
public synchronized int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return delegate.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<Object> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object[] toArray() {
|
||||
return delegate.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized <T> T[] toArray(T[] a) {
|
||||
return delegate.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean remove(Object o) {
|
||||
return delegate.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean containsAll(Collection<?> c) {
|
||||
return delegate.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(int index, Collection<? extends Object> c) {
|
||||
return delegate.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeAll(Collection<?> c) {
|
||||
return delegate.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean retainAll(Collection<?> c) {
|
||||
return delegate.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(int index) {
|
||||
return delegate.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void add(int index, Object element) {
|
||||
delegate.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object remove(int index) {
|
||||
return delegate.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int indexOf(Object o) {
|
||||
return delegate.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int lastIndexOf(Object o) {
|
||||
return delegate.lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator<Object> listIterator() {
|
||||
return delegate.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ListIterator<Object> listIterator(int index) {
|
||||
return delegate.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Object> subList(int fromIndex, int toIndex) {
|
||||
return delegate.subList(fromIndex, toIndex);
|
||||
}
|
||||
// End boiler plate
|
||||
}
|
||||
-910
@@ -1,910 +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.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
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.PacketEvent;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.internal.TypeParameterMatcher;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a channel injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ChannelInjector extends ByteToMessageDecoder implements Injector {
|
||||
private static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
|
||||
private static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
|
||||
private static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
|
||||
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
|
||||
/**
|
||||
* Indicates that a packet has bypassed packet listeners.
|
||||
*/
|
||||
private static final PacketEvent BYPASSED_PACKET = new PacketEvent(ChannelInjector.class);
|
||||
|
||||
// The login packet
|
||||
private static Class<?> PACKET_LOGIN_CLIENT = null;
|
||||
private static FieldAccessor LOGIN_GAME_PROFILE = null;
|
||||
|
||||
// Versioning
|
||||
private static Class<?> PACKET_SET_PROTOCOL = null;
|
||||
|
||||
private static AtomicInteger keyId = new AtomicInteger();
|
||||
private static AttributeKey<Integer> PROTOCOL_KEY;
|
||||
|
||||
static {
|
||||
PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL-" + keyId.getAndIncrement());
|
||||
}
|
||||
|
||||
// Saved accessors
|
||||
private static MethodAccessor DECODE_BUFFER;
|
||||
private static MethodAccessor ENCODE_BUFFER;
|
||||
private static FieldAccessor ENCODER_TYPE_MATCHER;
|
||||
|
||||
// For retrieving the protocol
|
||||
private static FieldAccessor PROTOCOL_ACCESSOR;
|
||||
|
||||
// The factory that created this injector
|
||||
private InjectionFactory factory;
|
||||
|
||||
// The player, or temporary player
|
||||
private Player player;
|
||||
private Player updated;
|
||||
private String playerName;
|
||||
|
||||
// The player connection
|
||||
private Object playerConnection;
|
||||
|
||||
// The current network manager and channel
|
||||
private final Object networkManager;
|
||||
private final Channel originalChannel;
|
||||
private VolatileField channelField;
|
||||
|
||||
// Known network markers
|
||||
private Map<Object, NetworkMarker> packetMarker = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Indicate that this packet has been processed by event listeners.
|
||||
* <p>
|
||||
* This must never be set outside the channel pipeline's thread.
|
||||
*/
|
||||
private PacketEvent currentEvent;
|
||||
|
||||
/**
|
||||
* A packet event that should be processed by the write method.
|
||||
*/
|
||||
private PacketEvent finalEvent;
|
||||
|
||||
/**
|
||||
* A flag set by the main thread to indiciate that a packet should not be processed.
|
||||
*/
|
||||
private final ThreadLocal<Boolean> scheduleProcessPackets = ThreadLocal.withInitial(() -> true);
|
||||
|
||||
// Other handlers
|
||||
private ByteToMessageDecoder vanillaDecoder;
|
||||
private MessageToByteEncoder<Object> vanillaEncoder;
|
||||
|
||||
private Deque<PacketEvent> finishQueue = new ArrayDeque<>();
|
||||
|
||||
// The channel listener
|
||||
private ChannelListener channelListener;
|
||||
|
||||
// Processing network markers
|
||||
private NetworkProcessor processor;
|
||||
|
||||
// Closed
|
||||
private boolean injected;
|
||||
private boolean closed;
|
||||
|
||||
/**
|
||||
* Construct a new channel injector.
|
||||
* @param player - the current player, or temporary player.
|
||||
* @param networkManager - its network manager.
|
||||
* @param channel - its channel.
|
||||
* @param channelListener - a listener.
|
||||
* @param factory - the factory that created this injector
|
||||
*/
|
||||
ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
|
||||
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
|
||||
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
|
||||
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
|
||||
this.channelListener = Preconditions.checkNotNull(channelListener, "channelListener cannot be NULL");
|
||||
this.factory = Preconditions.checkNotNull(factory, "factory cannot be NULL");
|
||||
this.processor = new NetworkProcessor(ProtocolLibrary.getErrorReporter());
|
||||
|
||||
// Get the channel field
|
||||
this.channelField = new VolatileField(FuzzyReflection
|
||||
.fromObject(networkManager, true)
|
||||
.getFieldByType("channel", Channel.class), networkManager, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version of the current protocol.
|
||||
* @return The version.
|
||||
*/
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
Integer value = originalChannel.attr(PROTOCOL_KEY).get();
|
||||
return value != null ? value : MinecraftProtocolVersion.getCurrentVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean inject() {
|
||||
synchronized (networkManager) {
|
||||
if (closed)
|
||||
return false;
|
||||
if (originalChannel instanceof Factory)
|
||||
return false;
|
||||
if (!originalChannel.isActive())
|
||||
return false;
|
||||
|
||||
// Main thread? We should synchronize with the channel thread, otherwise we might see a
|
||||
// pipeline with only some of the handlers removed
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
// Just like in the close() method, we'll avoid blocking the main thread
|
||||
executeInChannelThread(this::inject);
|
||||
return false; // We don't know
|
||||
}
|
||||
|
||||
// Don't inject the same channel twice
|
||||
if (findChannelHandler(originalChannel, ChannelInjector.class) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the vanilla decoder, so we don't have to replicate the work
|
||||
vanillaDecoder = (ByteToMessageDecoder) originalChannel.pipeline().get("decoder");
|
||||
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
|
||||
|
||||
if (vanillaDecoder == null)
|
||||
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline());
|
||||
if (vanillaEncoder == null)
|
||||
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline());
|
||||
patchEncoder(vanillaEncoder);
|
||||
|
||||
if (DECODE_BUFFER == null)
|
||||
DECODE_BUFFER = Accessors.getMethodAccessor(vanillaDecoder.getClass(),
|
||||
"decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
|
||||
if (ENCODE_BUFFER == null)
|
||||
ENCODE_BUFFER = Accessors.getMethodAccessor(vanillaEncoder.getClass(),
|
||||
"encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
||||
|
||||
// Intercept sent packets
|
||||
MessageToByteEncoder<Object> protocolEncoder = new MessageToByteEncoder<Object>() {
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
|
||||
if (packet instanceof WirePacket) {
|
||||
// Special case for wire format
|
||||
ChannelInjector.this.encodeWirePacket((WirePacket) packet, output);
|
||||
} else {
|
||||
ChannelInjector.this.encode(ctx, packet, output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||
super.write(ctx, packet, promise);
|
||||
ChannelInjector.this.finalWrite();
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept recieved packets
|
||||
ChannelInboundHandlerAdapter finishHandler = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
// Execute context first
|
||||
ctx.fireChannelRead(msg);
|
||||
ChannelInjector.this.finishRead();
|
||||
}
|
||||
};
|
||||
|
||||
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
||||
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
||||
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
|
||||
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
||||
|
||||
// Intercept all write methods
|
||||
channelField.setValue(new ChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
|
||||
// Compatibility with Spigot 1.8
|
||||
private final PipelineProxy pipelineProxy = new PipelineProxy(originalChannel.pipeline(), this) {
|
||||
@Override
|
||||
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
|
||||
// Correct the position of the decoder
|
||||
if ("decoder".equals(baseName)) {
|
||||
if (super.get("protocol_lib_decoder") != null && guessCompression(handler)) {
|
||||
super.addBefore("protocol_lib_decoder", name, handler);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return super.addBefore(baseName, name, handler);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ChannelPipeline pipeline() {
|
||||
return pipelineProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Callable<T> onMessageScheduled(final Callable<T> callable, FieldAccessor packetAccessor) {
|
||||
final PacketEvent event = handleScheduled(callable, packetAccessor);
|
||||
|
||||
// Handle cancelled events
|
||||
if (event != null && event.isCancelled())
|
||||
return null;
|
||||
|
||||
return () -> {
|
||||
T result;
|
||||
|
||||
// This field must only be updated in the pipeline thread
|
||||
currentEvent = event;
|
||||
result = callable.call();
|
||||
currentEvent = null;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Runnable onMessageScheduled(final Runnable runnable, FieldAccessor packetAccessor) {
|
||||
final PacketEvent event = handleScheduled(runnable, packetAccessor);
|
||||
|
||||
// Handle cancelled events
|
||||
if (event != null && event.isCancelled())
|
||||
return null;
|
||||
|
||||
return () -> {
|
||||
currentEvent = event;
|
||||
runnable.run();
|
||||
currentEvent = null;
|
||||
};
|
||||
}
|
||||
|
||||
PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
|
||||
// Let the filters handle this packet
|
||||
Object original = accessor.get(instance);
|
||||
|
||||
// See if we've been instructed not to process packets
|
||||
if (!scheduleProcessPackets.get()) {
|
||||
NetworkMarker marker = getMarker(original);
|
||||
|
||||
if (marker != null) {
|
||||
PacketEvent result = new PacketEvent(ChannelInjector.class);
|
||||
result.setNetworkMarker(marker);
|
||||
return result;
|
||||
} else {
|
||||
return BYPASSED_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
PacketEvent event = processSending(original);
|
||||
if (event != null && !event.isCancelled()) {
|
||||
Object changed = event.getPacket().getHandle();
|
||||
|
||||
// Change packet to be scheduled
|
||||
if (original != changed) {
|
||||
accessor.set(instance, changed);
|
||||
}
|
||||
}
|
||||
|
||||
return event != null ? event : BYPASSED_PACKET;
|
||||
}
|
||||
});
|
||||
|
||||
injected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given object is a compressor or decompressor.
|
||||
* @param handler - object to test.
|
||||
* @return TRUE if it is, FALSE if not or unknown.
|
||||
*/
|
||||
private boolean guessCompression(ChannelHandler handler) {
|
||||
String className = handler != null ? handler.getClass().getCanonicalName() : "";
|
||||
return className.contains("Compressor") || className.contains("Decompressor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a given message on the packet listeners.
|
||||
* @param message - the message/packet.
|
||||
* @return The resulting message/packet.
|
||||
*/
|
||||
private PacketEvent processSending(Object message) {
|
||||
return channelListener.onPacketSending(ChannelInjector.this, message, getMarker(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method patches the encoder so that it skips already created packets.
|
||||
* @param encoder - the encoder to patch.
|
||||
*/
|
||||
private void patchEncoder(MessageToByteEncoder<Object> encoder) {
|
||||
if (ENCODER_TYPE_MATCHER == null) {
|
||||
ENCODER_TYPE_MATCHER = Accessors.getFieldAccessor(encoder.getClass(), "matcher", true);
|
||||
}
|
||||
|
||||
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (channelListener.isDebug()) {
|
||||
cause.printStackTrace();
|
||||
}
|
||||
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
private void encodeWirePacket(WirePacket packet, ByteBuf output) {
|
||||
packet.writeId(output);
|
||||
packet.writeBytes(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a packet to a byte buffer, taking over for the standard Minecraft encoder.
|
||||
* @param ctx - the current context.
|
||||
* @param packet - the packet to encode to a byte array.
|
||||
* @param output - the output byte array.
|
||||
*/
|
||||
private void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
|
||||
NetworkMarker marker = null;
|
||||
PacketEvent event = currentEvent;
|
||||
|
||||
try {
|
||||
// Skip every kind of non-filtered packet
|
||||
if (!scheduleProcessPackets.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This packet has not been seen by the main thread
|
||||
if (event == null) {
|
||||
Class<?> clazz = packet.getClass();
|
||||
|
||||
// Schedule the transmission on the main thread instead
|
||||
if (channelListener.hasMainThreadListener(clazz)) {
|
||||
// Delay the packet
|
||||
scheduleMainThread(packet);
|
||||
packet = null;
|
||||
|
||||
} else {
|
||||
event = processSending(packet);
|
||||
|
||||
// Handle the output
|
||||
if (event != null) {
|
||||
packet = !event.isCancelled() ? event.getPacket().getHandle() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event != null) {
|
||||
// Retrieve marker without accidentally constructing it
|
||||
marker = NetworkMarker.getNetworkMarker(event);
|
||||
}
|
||||
|
||||
// Process output handler
|
||||
if (packet != null && event != null && NetworkMarker.hasOutputHandlers(marker)) {
|
||||
ByteBuf packetBuffer = ctx.alloc().buffer();
|
||||
ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, packetBuffer);
|
||||
|
||||
// Let each handler prepare the actual output
|
||||
byte[] data = processor.processOutput(event, marker, getBytes(packetBuffer));
|
||||
|
||||
// Write the result
|
||||
output.writeBytes(data);
|
||||
packet = null;
|
||||
|
||||
// Sent listeners?
|
||||
finalEvent = event;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
channelListener.getReporter().reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_INTERCEPT_SERVER_PACKET).callerParam(packet).error(e).build());
|
||||
} finally {
|
||||
// Attempt to handle the packet nevertheless
|
||||
if (packet != null) {
|
||||
ENCODE_BUFFER.invoke(vanillaEncoder, ctx, packet, output);
|
||||
finalEvent = event;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a packet has been written to the channel
|
||||
*/
|
||||
private void finalWrite() {
|
||||
PacketEvent event = finalEvent;
|
||||
|
||||
if (event != null) {
|
||||
// Necessary to prevent infinite loops
|
||||
finalEvent = null;
|
||||
currentEvent = null;
|
||||
|
||||
processor.invokePostEvent(event, NetworkMarker.getNetworkMarker(event));
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleMainThread(final Object packetCopy) {
|
||||
// Don't use BukkitExecutors for this - it has a bit of overhead
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), () -> {
|
||||
if (!closed) {
|
||||
invokeSendPacket(packetCopy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) {
|
||||
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
|
||||
|
||||
try {
|
||||
// Reset queue
|
||||
finishQueue.clear();
|
||||
|
||||
for (ListIterator<Object> it = packets.listIterator(); it.hasNext(); ) {
|
||||
Object input = it.next();
|
||||
Class<?> packetClass = input.getClass();
|
||||
NetworkMarker marker = null;
|
||||
|
||||
// Special case!
|
||||
handleLogin(packetClass, input);
|
||||
|
||||
if (channelListener.includeBuffer(packetClass)) {
|
||||
byteBuffer.resetReaderIndex();
|
||||
marker = new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
|
||||
}
|
||||
|
||||
PacketEvent output = channelListener.onPacketReceiving(this, input, marker);
|
||||
|
||||
// Handle packet changes
|
||||
if (output != null) {
|
||||
if (output.isCancelled()) {
|
||||
it.remove();
|
||||
} else {
|
||||
if (output.getPacket().getHandle() != input) {
|
||||
it.set(output.getPacket().getHandle());
|
||||
}
|
||||
|
||||
finishQueue.addLast(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
channelListener.getReporter().reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_INTERCEPT_CLIENT_PACKET).callerParam(byteBuffer).error(e).build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after our decoder
|
||||
*/
|
||||
private void finishRead() {
|
||||
// Assume same order
|
||||
PacketEvent event = finishQueue.pollFirst();
|
||||
|
||||
if (event != null) {
|
||||
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
|
||||
|
||||
if (marker != null) {
|
||||
processor.invokePostEvent(event, marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when we may need to handle the login packet.
|
||||
* @param packetClass - the packet class.
|
||||
* @param packet - the packet.
|
||||
*/
|
||||
private void handleLogin(Class<?> packetClass, Object packet) {
|
||||
// Try to find the login packet class
|
||||
if (PACKET_LOGIN_CLIENT == null) {
|
||||
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
|
||||
}
|
||||
|
||||
// If we can't, there's an issue
|
||||
if (PACKET_LOGIN_CLIENT == null) {
|
||||
throw new IllegalStateException("Failed to obtain login start packet. Did you build Spigot with BuildTools?");
|
||||
}
|
||||
|
||||
if (LOGIN_GAME_PROFILE == null) {
|
||||
LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, MinecraftReflection.getGameProfileClass(), true);
|
||||
}
|
||||
|
||||
// See if we are dealing with the login packet
|
||||
if (PACKET_LOGIN_CLIENT.equals(packetClass)) {
|
||||
WrappedGameProfile profile = WrappedGameProfile.fromHandle(LOGIN_GAME_PROFILE.get(packet));
|
||||
|
||||
// Save the channel injector
|
||||
factory.cacheInjector(profile.getName(), this);
|
||||
}
|
||||
|
||||
if (PACKET_SET_PROTOCOL == null) {
|
||||
try {
|
||||
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
|
||||
} catch (Throwable ex) {
|
||||
PACKET_SET_PROTOCOL = getClass(); // If we can't find it don't worry about it
|
||||
}
|
||||
}
|
||||
|
||||
if (PACKET_SET_PROTOCOL.equals(packetClass)) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromObject(packet);
|
||||
try {
|
||||
int protocol = (int) fuzzy.invokeMethod(packet, "getProtocol", int.class);
|
||||
originalChannel.attr(PROTOCOL_KEY).set(protocol);
|
||||
} catch (Throwable ex) {
|
||||
// Oh well
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
|
||||
// See NetworkManager.channelActive(ChannelHandlerContext) for why
|
||||
if (channelField != null) {
|
||||
channelField.refreshValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every byte in the given byte buffer.
|
||||
* @param buffer - the buffer.
|
||||
* @return The bytes.
|
||||
*/
|
||||
private byte[] getBytes(ByteBuf buffer) {
|
||||
byte[] data = new byte[buffer.readableBytes()];
|
||||
|
||||
buffer.readBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the current player.
|
||||
* @param message - the disconnect message, if possible.
|
||||
*/
|
||||
private void disconnect(String message) {
|
||||
// If we're logging in, we can only close the channel
|
||||
if (playerConnection == null || player instanceof Factory) {
|
||||
originalChannel.disconnect();
|
||||
} else {
|
||||
// Call the disconnect method
|
||||
try {
|
||||
MinecraftMethods.getDisconnectMethod(playerConnection.getClass()).
|
||||
invoke(playerConnection, message);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Unable to invoke disconnect method.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
saveMarker(packet, marker);
|
||||
|
||||
try {
|
||||
scheduleProcessPackets.set(filtered);
|
||||
invokeSendPacket(packet);
|
||||
} finally {
|
||||
scheduleProcessPackets.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the sendPacket method in Minecraft.
|
||||
* @param packet - the packet to send.
|
||||
*/
|
||||
private void invokeSendPacket(Object packet) {
|
||||
Validate.isTrue(!closed, "cannot send packets to a closed channel");
|
||||
|
||||
// Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active
|
||||
try {
|
||||
if (player instanceof Factory) {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet);
|
||||
} else {
|
||||
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_PACKET).messageParam(packet, playerName).error(ex).build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(final Object packet) {
|
||||
// TODO: Ensure the packet listeners are executed in the channel thread.
|
||||
|
||||
// Execute this in the channel thread
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
|
||||
} catch (Exception e) {
|
||||
// Inform the user
|
||||
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute in the worker thread
|
||||
if (originalChannel.eventLoop().inEventLoop()) {
|
||||
action.run();
|
||||
} else {
|
||||
originalChannel.eventLoop().execute(action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
if (PROTOCOL_ACCESSOR == null) {
|
||||
PROTOCOL_ACCESSOR = Accessors.getFieldAccessor(
|
||||
networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true);
|
||||
}
|
||||
return Protocol.fromVanilla((Enum<?>) PROTOCOL_ACCESSOR.get(networkManager));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the player connection of the current player.
|
||||
* @return The player connection.
|
||||
*/
|
||||
private Object getPlayerConnection() {
|
||||
if (playerConnection == null) {
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("cannot send packet to offline player" + (playerName != null ? " " + playerName : ""));
|
||||
}
|
||||
|
||||
playerConnection = MinecraftFields.getPlayerConnection(player);
|
||||
}
|
||||
|
||||
return playerConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return packetMarker.get(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
if (marker != null) {
|
||||
packetMarker.put(packet, marker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
if (player == null && playerName != null) {
|
||||
return Bukkit.getPlayerExact(playerName);
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the player instance.
|
||||
* @param player - current instance.
|
||||
*/
|
||||
@Override
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.playerName = player.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the updated player instance.
|
||||
* @param updated - updated instance.
|
||||
*/
|
||||
@Override
|
||||
public void setUpdatedPlayer(Player updated) {
|
||||
this.updated = updated;
|
||||
this.playerName = updated.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInjected() {
|
||||
return injected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this channel has been closed and cleaned up.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
if (injected) {
|
||||
channelField.revertValue();
|
||||
|
||||
// Calling remove() in the main thread will block the main thread, which may lead
|
||||
// to a deadlock:
|
||||
// http://pastebin.com/L3SBVKzp
|
||||
//
|
||||
// ProtocolLib executes this close() method through a PlayerQuitEvent in the main thread,
|
||||
// which has implicitly aquired a lock on SimplePluginManager (see SimplePluginManager.callEvent(Event)).
|
||||
// Unfortunately, the remove() method will schedule the removal on one of the Netty worker threads if
|
||||
// it's called from a different thread, blocking until the removal has been confirmed.
|
||||
//
|
||||
// This is bad enough (Rule #1: Don't block the main thread), but the real trouble starts if the same
|
||||
// worker thread happens to be handling a server ping connection when this removal task is scheduled.
|
||||
// In that case, it may attempt to invoke an asynchronous ServerPingEvent (see PacketStatusListener)
|
||||
// using SimplePluginManager.callEvent(). But, since this has already been locked by the main thread,
|
||||
// we end up with a deadlock. The main thread is waiting for the worker thread to process the task, and
|
||||
// the worker thread is waiting for the main thread to finish executing PlayerQuitEvent.
|
||||
//
|
||||
// TL;DR: Concurrency is hard.
|
||||
executeInChannelThread(() -> {
|
||||
String[] handlers = new String[] {
|
||||
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
|
||||
};
|
||||
|
||||
for (String handler : handlers) {
|
||||
try {
|
||||
originalChannel.pipeline().remove(handler);
|
||||
} catch (NoSuchElementException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clear cache
|
||||
factory.invalidate(player);
|
||||
|
||||
// Clear player instances
|
||||
// Should help fix memory leaks
|
||||
this.player = null;
|
||||
this.updated = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a specific command in the channel thread.
|
||||
* <p>
|
||||
* Exceptions are printed through the standard error reporter mechanism.
|
||||
* @param command - the command to execute.
|
||||
*/
|
||||
private void executeInChannelThread(final Runnable command) {
|
||||
originalChannel.eventLoop().execute(() -> {
|
||||
try {
|
||||
command.run();
|
||||
} catch (Exception e) {
|
||||
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
|
||||
Report.newBuilder(REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD).error(e).build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first channel handler that is assignable to a given type.
|
||||
* @param channel - the channel.
|
||||
* @param clazz - the type.
|
||||
* @return The first handler, or NULL.
|
||||
*/
|
||||
static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a socket injector that foreards to the current channel injector.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class ChannelSocketInjector implements SocketInjector {
|
||||
private final ChannelInjector injector;
|
||||
|
||||
ChannelSocketInjector(ChannelInjector injector) {
|
||||
this.injector = Preconditions.checkNotNull(injector, "injector cannot be NULL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket getSocket() {
|
||||
return SocketAdapter.adapt((SocketChannel) injector.originalChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getAddress() {
|
||||
return injector.originalChannel.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
injector.disconnect(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
injector.sendServerPacket(packet, marker, filtered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return injector.getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getUpdatedPlayer() {
|
||||
return injector.updated != null ? injector.updated : getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferState(SocketInjector delegate) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdatedPlayer(Player updatedPlayer) {
|
||||
injector.setPlayer(updatedPlayer);
|
||||
}
|
||||
|
||||
ChannelInjector getChannelInjector() {
|
||||
return injector;
|
||||
}
|
||||
}
|
||||
|
||||
public Channel getChannel() {
|
||||
return originalChannel;
|
||||
}
|
||||
}
|
||||
-352
@@ -1,352 +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.lang.reflect.Field;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelProgressivePromise;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
public abstract class ChannelProxy implements Channel {
|
||||
// Mark that a certain object does not contain a message field
|
||||
private static final FieldAccessor MARK_NO_MESSAGE = new FieldAccessor() {
|
||||
@Override
|
||||
public void set(Object instance, Object value) { }
|
||||
@Override
|
||||
public Object get(Object instance) { return null; }
|
||||
@Override
|
||||
public Field getField() { return null; };
|
||||
};
|
||||
|
||||
// Looking up packets in inner classes
|
||||
private static Map<Class<?>, FieldAccessor> MESSAGE_LOOKUP = Maps.newConcurrentMap();
|
||||
|
||||
// The underlying channel
|
||||
protected Channel delegate;
|
||||
protected Class<?> messageClass;
|
||||
|
||||
// Event loop proxy
|
||||
private transient EventLoopProxy loopProxy;
|
||||
|
||||
public ChannelProxy(Channel delegate, Class<?> messageClass) {
|
||||
this.delegate = delegate;
|
||||
this.messageClass = messageClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a packet is scheduled for transmission in the event loop.
|
||||
* @param <T> Type
|
||||
* @param callable - callable to schedule for execution.
|
||||
* @param packetAccessor - accessor for modifying the packet in the callable.
|
||||
* @return The callable that will be scheduled, or NULL to cancel.
|
||||
*/
|
||||
protected abstract <T> Callable<T> onMessageScheduled(Callable<T> callable, FieldAccessor packetAccessor);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is scheduled for transmission in the event loop.
|
||||
* @param runnable - the runnable that contains a packet to be scheduled.
|
||||
* @param packetAccessor - accessor for modifying the packet in the runnable.
|
||||
* @return The runnable that will be scheduled, or NULL to cancel.
|
||||
*/
|
||||
protected abstract Runnable onMessageScheduled(Runnable runnable, FieldAccessor packetAccessor);
|
||||
|
||||
@Override
|
||||
public <T> Attribute<T> attr(AttributeKey<T> paramAttributeKey) {
|
||||
return delegate.attr(paramAttributeKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress paramSocketAddress) {
|
||||
return delegate.bind(paramSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline pipeline() {
|
||||
return delegate.pipeline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress) {
|
||||
return delegate.connect(paramSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBufAllocator alloc() {
|
||||
return delegate.alloc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise newPromise() {
|
||||
return delegate.newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoop eventLoop() {
|
||||
if (loopProxy == null) {
|
||||
loopProxy = new EventLoopProxy() {
|
||||
@Override
|
||||
protected EventLoop getDelegate() {
|
||||
return delegate.eventLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Runnable schedulingRunnable(final Runnable runnable) {
|
||||
final FieldAccessor accessor = getMessageAccessor(runnable);
|
||||
|
||||
if (accessor != null) {
|
||||
Runnable result = onMessageScheduled(runnable, accessor);;
|
||||
return result != null ? result : getEmptyRunnable();
|
||||
}
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Callable<T> schedulingCallable(Callable<T> callable) {
|
||||
FieldAccessor accessor = getMessageAccessor(callable);
|
||||
|
||||
if (accessor != null) {
|
||||
Callable<T> result = onMessageScheduled(callable, accessor);;
|
||||
return result != null ? result : EventLoopProxy.<T>getEmptyCallable();
|
||||
}
|
||||
return callable;
|
||||
}
|
||||
};
|
||||
}
|
||||
return loopProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a way to access the packet field of an object.
|
||||
* @param value - the object.
|
||||
* @return The packet field accessor, or NULL if not found.
|
||||
*/
|
||||
private FieldAccessor getMessageAccessor(Object value) {
|
||||
Class<?> clazz = value.getClass();
|
||||
FieldAccessor accessor = MESSAGE_LOOKUP.get(clazz);
|
||||
|
||||
if (accessor == null) {
|
||||
try {
|
||||
accessor = Accessors.getFieldAccessor(clazz, messageClass, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
accessor = MARK_NO_MESSAGE;
|
||||
}
|
||||
// Save the result
|
||||
MESSAGE_LOOKUP.put(clazz, accessor);
|
||||
}
|
||||
return accessor != MARK_NO_MESSAGE ? accessor : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress1,
|
||||
SocketAddress paramSocketAddress2) {
|
||||
return delegate.connect(paramSocketAddress1, paramSocketAddress2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelProgressivePromise newProgressivePromise() {
|
||||
return delegate.newProgressivePromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel parent() {
|
||||
return delegate.parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfig config() {
|
||||
return delegate.config();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newSucceededFuture() {
|
||||
return delegate.newSucceededFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return delegate.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect() {
|
||||
return delegate.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered() {
|
||||
return delegate.isRegistered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture newFailedFuture(Throwable paramThrowable) {
|
||||
return delegate.newFailedFuture(paramThrowable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
return delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return delegate.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ChannelFuture deregister() {
|
||||
return delegate.deregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise voidPromise() {
|
||||
return delegate.voidPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return delegate.metadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress paramSocketAddress,
|
||||
ChannelPromise paramChannelPromise) {
|
||||
return delegate.bind(paramSocketAddress, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress localAddress() {
|
||||
return delegate.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress remoteAddress() {
|
||||
return delegate.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress,
|
||||
ChannelPromise paramChannelPromise) {
|
||||
return delegate.connect(paramSocketAddress, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture closeFuture() {
|
||||
return delegate.closeFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return delegate.isWritable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel flush() {
|
||||
return delegate.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress paramSocketAddress1,
|
||||
SocketAddress paramSocketAddress2, ChannelPromise paramChannelPromise) {
|
||||
return delegate.connect(paramSocketAddress1, paramSocketAddress2, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel read() {
|
||||
return delegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe() {
|
||||
return delegate.unsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect(ChannelPromise paramChannelPromise) {
|
||||
return delegate.disconnect(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close(ChannelPromise paramChannelPromise) {
|
||||
return delegate.close(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ChannelFuture deregister(ChannelPromise paramChannelPromise) {
|
||||
return delegate.deregister(paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object paramObject) {
|
||||
return delegate.write(paramObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object paramObject, ChannelPromise paramChannelPromise) {
|
||||
return delegate.write(paramObject, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object paramObject, ChannelPromise paramChannelPromise) {
|
||||
return delegate.writeAndFlush(paramObject, paramChannelPromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object paramObject) {
|
||||
return delegate.writeAndFlush(paramObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Channel o) {
|
||||
return delegate.compareTo(o);
|
||||
}
|
||||
|
||||
/* Added in Netty 4.1, seem to be unused
|
||||
public long bytesBeforeUnwritable() {
|
||||
return delegate.bytesBeforeUnwritable();
|
||||
}
|
||||
|
||||
public long bytesBeforeWritable() {
|
||||
return delegate.bytesBeforeWritable();
|
||||
}
|
||||
|
||||
public ChannelId id() {
|
||||
return delegate.id();
|
||||
}
|
||||
|
||||
public <T> boolean hasAttr(AttributeKey<T> key) {
|
||||
return delegate.hasAttr(key);
|
||||
}
|
||||
*/
|
||||
}
|
||||
-265
@@ -1,265 +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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ProgressivePromise;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* An event loop proxy.
|
||||
* @author Kristian.
|
||||
*/
|
||||
abstract class EventLoopProxy implements EventLoop {
|
||||
private static final Runnable EMPTY_RUNNABLE = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
private static final Callable<?> EMPTY_CALLABLE = new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the underlying event loop.
|
||||
* @return The event loop.
|
||||
*/
|
||||
protected abstract EventLoop getDelegate();
|
||||
|
||||
/**
|
||||
* Retrieve a callable that does nothing but return NULL.
|
||||
* @return The empty callable.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Callable<T> getEmptyCallable() {
|
||||
return (Callable<T>) EMPTY_CALLABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a runnable that does nothing.
|
||||
* @return A NO-OP runnable.
|
||||
*/
|
||||
public static Runnable getEmptyRunnable() {
|
||||
return EMPTY_RUNNABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a runnable is being scheduled.
|
||||
* @param runnable - the runnable that is scheduling.
|
||||
* @return The runnable to schedule instead. Cannot be NULL.
|
||||
*/
|
||||
protected abstract Runnable schedulingRunnable(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Invoked when a callable is being scheduled.
|
||||
* @param runnable - the callable that is scheduling.
|
||||
* @return The callable to schedule instead. Cannot be NULL.
|
||||
*/
|
||||
protected abstract <T> Callable<T> schedulingCallable(Callable<T> callable);
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
getDelegate().execute(schedulingRunnable(command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> action) {
|
||||
return getDelegate().submit(schedulingCallable(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Runnable action, T arg1) {
|
||||
return getDelegate().submit(schedulingRunnable(action), arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable action) {
|
||||
return getDelegate().submit(schedulingRunnable(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> action, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().schedule(schedulingCallable(action), arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable action, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().schedule(schedulingRunnable(action), arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable action, long arg1, long arg2, TimeUnit arg3) {
|
||||
return getDelegate().scheduleAtFixedRate(schedulingRunnable(action), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable action, long arg1, long arg2, TimeUnit arg3) {
|
||||
return getDelegate().scheduleWithFixedDelay(schedulingRunnable(action), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
// Boiler plate:
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return getDelegate().awaitTermination(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inEventLoop() {
|
||||
return getDelegate().inEventLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inEventLoop(Thread arg0) {
|
||||
return getDelegate().inEventLoop(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return getDelegate().isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return getDelegate().isTerminated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||
throws InterruptedException {
|
||||
return getDelegate().invokeAll(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
|
||||
TimeUnit unit) throws InterruptedException {
|
||||
return getDelegate().invokeAll(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException,
|
||||
ExecutionException {
|
||||
return getDelegate().invokeAny(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return getDelegate().invokeAny(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShuttingDown() {
|
||||
return getDelegate().isShuttingDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<EventExecutor> iterator() {
|
||||
return getDelegate().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> newFailedFuture(Throwable arg0) {
|
||||
return getDelegate().newFailedFuture(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoop next() {
|
||||
return ((EventLoopGroup) getDelegate()).next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ProgressivePromise<V> newProgressivePromise() {
|
||||
return getDelegate().newProgressivePromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Promise<V> newPromise() {
|
||||
return getDelegate().newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> newSucceededFuture(V arg0) {
|
||||
return getDelegate().newSucceededFuture(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventLoopGroup parent() {
|
||||
return getDelegate().parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture register(Channel arg0, ChannelPromise arg1) {
|
||||
return getDelegate().register(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture register(Channel arg0) {
|
||||
return getDelegate().register(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> shutdownGracefully() {
|
||||
return getDelegate().shutdownGracefully();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> shutdownGracefully(long arg0, long arg1, TimeUnit arg2) {
|
||||
return getDelegate().shutdownGracefully(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> terminationFuture() {
|
||||
return getDelegate().terminationFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void shutdown() {
|
||||
getDelegate().shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public List<Runnable> shutdownNow() {
|
||||
return getDelegate().shutdownNow();
|
||||
}
|
||||
|
||||
/*
|
||||
public ChannelFuture register(ChannelPromise promise) {
|
||||
return getDelegate().register(promise);
|
||||
}
|
||||
*/
|
||||
}
|
||||
-236
@@ -1,236 +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 io.netty.channel.Channel;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.ChannelInjector.ChannelSocketInjector;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
/**
|
||||
* Represents an injector factory.
|
||||
* <p>
|
||||
* Note that the factory will return {@link ClosedInjector} when the factory is closed.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class InjectionFactory {
|
||||
// This should work as long as the injectors are, uh, injected
|
||||
private final ConcurrentMap<Player, Injector> playerLookup = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
private final ConcurrentMap<String, Injector> nameLookup = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// Whether or not the factory is closed
|
||||
private volatile boolean closed;
|
||||
|
||||
// The current plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
public InjectionFactory(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the main plugin associated with this injection factory.
|
||||
* @return The main plugin.
|
||||
*/
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param listener - the listener.
|
||||
* @return A new injector, an existing injector associated with this player, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromPlayer(Player player, ChannelListener listener) {
|
||||
if (closed)
|
||||
return new ClosedInjector(player);
|
||||
Injector injector = playerLookup.get(player);
|
||||
|
||||
// Find a temporary injector as well
|
||||
if (injector == null)
|
||||
injector = getTemporaryInjector(player);
|
||||
if (injector != null && !injector.isClosed())
|
||||
return injector;
|
||||
|
||||
Object networkManager = MinecraftFields.getNetworkManager(player);
|
||||
|
||||
// Must be a temporary Bukkit player
|
||||
if (networkManager == null) {
|
||||
return fromName(player.getName(), player);
|
||||
}
|
||||
Channel channel = FuzzyReflection.getFieldValue(networkManager, Channel.class, true);
|
||||
|
||||
// See if a channel has already been created
|
||||
injector = (ChannelInjector) ChannelInjector.findChannelHandler(channel, ChannelInjector.class);
|
||||
|
||||
if (injector != null) {
|
||||
// Update the player instance
|
||||
playerLookup.remove(injector.getPlayer());
|
||||
injector.setPlayer(player);
|
||||
} else {
|
||||
injector = new ChannelInjector(player, networkManager, channel, listener, this);
|
||||
}
|
||||
|
||||
// Cache injector and return
|
||||
cacheInjector(player, injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached injector from a name.
|
||||
* <p>
|
||||
* The injector may be NULL if the plugin has been reloaded during a player login.
|
||||
* @param name - the name.
|
||||
* @param player - the player.
|
||||
* @return The cached injector, or a closed injector if it could not be found.
|
||||
*/
|
||||
public Injector fromName(String name, Player player) {
|
||||
if (!closed) {
|
||||
Injector injector = nameLookup.get(name);
|
||||
|
||||
// We can only retrieve cached injectors
|
||||
if (injector != null) {
|
||||
// Update instance
|
||||
injector.setUpdatedPlayer(player);
|
||||
return injector;
|
||||
}
|
||||
}
|
||||
return new ClosedInjector(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new channel injector for the given channel.
|
||||
* @param channel - the channel.
|
||||
* @param listener - the listener.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @return The channel injector, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
if (closed)
|
||||
return new ClosedInjector(null);
|
||||
|
||||
Object networkManager = findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(Bukkit.getServer());
|
||||
ChannelInjector injector = new ChannelInjector(temporaryPlayer, networkManager, channel, listener, this);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, new ChannelSocketInjector(injector));
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a cached injector.
|
||||
* @param player - the associated player.
|
||||
* @return The cached injector, or NULL if nothing was cached.
|
||||
*/
|
||||
public Injector invalidate(Player player) {
|
||||
Injector injector = playerLookup.remove(player);
|
||||
|
||||
nameLookup.remove(player.getName());
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by player.
|
||||
* @param player - the player.
|
||||
* @param injector - the injector to cache.
|
||||
* @return The previously cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(Player player, Injector injector) {
|
||||
nameLookup.put(player.getName(), injector);
|
||||
return playerLookup.put(player, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an injector by name alone.
|
||||
* @param name - the name to lookup.
|
||||
* @param injector - the injector.
|
||||
* @return The cached injector.
|
||||
*/
|
||||
public Injector cacheInjector(String name, Injector injector) {
|
||||
return nameLookup.put(name, injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated channel injector.
|
||||
* @param player - the temporary player, or normal Bukkit player.
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private ChannelInjector getTemporaryInjector(Player player) {
|
||||
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
if (injector != null) {
|
||||
return ((ChannelSocketInjector) injector).getChannelInjector();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the network manager in a channel's pipeline.
|
||||
* @param channel - the channel.
|
||||
* @return The network manager.
|
||||
*/
|
||||
private Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = ChannelInjector.findChannelHandler(channel, MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
if (networkManager != null)
|
||||
return networkManager;
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the factory is closed.
|
||||
* <p>
|
||||
* If it is, all new injectors will be closed by default.
|
||||
* @return TRUE if it is closed, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all injectors created by this factory, and cease the creation of new injections.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
|
||||
// Close everything
|
||||
for (Injector injector : playerLookup.values())
|
||||
injector.close();
|
||||
for (Injector injector : nameLookup.values())
|
||||
injector.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
|
||||
public class NettyNetworkMarker extends NetworkMarker {
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
// Skip the variable int containing the packet ID
|
||||
getSerializer().deserializeVarInt(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
// We don't have to add anything - it's already there
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
// As above
|
||||
return input;
|
||||
}
|
||||
}
|
||||
-393
@@ -1,393 +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.net.SocketAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.util.concurrent.EventExecutorGroup;
|
||||
|
||||
/**
|
||||
* A pipeline proxy.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PipelineProxy implements ChannelPipeline {
|
||||
protected final ChannelPipeline pipeline;
|
||||
protected final Channel channel;
|
||||
|
||||
public PipelineProxy(ChannelPipeline pipeline, Channel channel) {
|
||||
this.pipeline = pipeline;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addAfter(EventExecutorGroup arg0, String arg1, String arg2, ChannelHandler arg3) {
|
||||
pipeline.addAfter(arg0, arg1, arg2, arg3);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addAfter(String arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addAfter(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addBefore(EventExecutorGroup arg0, String arg1, String arg2, ChannelHandler arg3) {
|
||||
pipeline.addBefore(arg0, arg1, arg2, arg3);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addBefore(String arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addBefore(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(ChannelHandler... arg0) {
|
||||
pipeline.addFirst(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(EventExecutorGroup arg0, ChannelHandler... arg1) {
|
||||
pipeline.addFirst(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(EventExecutorGroup arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addFirst(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addFirst(String arg0, ChannelHandler arg1) {
|
||||
pipeline.addFirst(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(ChannelHandler... arg0) {
|
||||
pipeline.addLast(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(EventExecutorGroup arg0, ChannelHandler... arg1) {
|
||||
pipeline.addLast(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(EventExecutorGroup arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.addLast(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline addLast(String arg0, ChannelHandler arg1) {
|
||||
pipeline.addLast(arg0, arg1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress arg0, ChannelPromise arg1) {
|
||||
return pipeline.bind(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(SocketAddress arg0) {
|
||||
return pipeline.bind(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
return pipeline.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close(ChannelPromise arg0) {
|
||||
return pipeline.close(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, ChannelPromise arg1) {
|
||||
return pipeline.connect(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, SocketAddress arg1, ChannelPromise arg2) {
|
||||
return pipeline.connect(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0, SocketAddress arg1) {
|
||||
return pipeline.connect(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture connect(SocketAddress arg0) {
|
||||
return pipeline.connect(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(ChannelHandler arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(Class<? extends ChannelHandler> arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext context(String arg0) {
|
||||
return pipeline.context(arg0);
|
||||
}
|
||||
|
||||
// We have to call the depreciated methods to properly implement the proxy
|
||||
@Override
|
||||
public ChannelFuture deregister() {
|
||||
return pipeline.deregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture deregister(ChannelPromise arg0) {
|
||||
return pipeline.deregister(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelUnregistered() {
|
||||
pipeline.fireChannelUnregistered();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect() {
|
||||
return pipeline.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture disconnect(ChannelPromise arg0) {
|
||||
return pipeline.disconnect(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelActive() {
|
||||
pipeline.fireChannelActive();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelInactive() {
|
||||
pipeline.fireChannelInactive();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelRead(Object arg0) {
|
||||
pipeline.fireChannelRead(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelReadComplete() {
|
||||
pipeline.fireChannelReadComplete();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelRegistered() {
|
||||
pipeline.fireChannelRegistered();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireChannelWritabilityChanged() {
|
||||
pipeline.fireChannelWritabilityChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireExceptionCaught(Throwable arg0) {
|
||||
pipeline.fireExceptionCaught(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline fireUserEventTriggered(Object arg0) {
|
||||
pipeline.fireUserEventTriggered(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler first() {
|
||||
return pipeline.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext firstContext() {
|
||||
return pipeline.firstContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline flush() {
|
||||
pipeline.flush();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T get(Class<T> arg0) {
|
||||
return pipeline.get(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler get(String arg0) {
|
||||
return pipeline.get(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String, ChannelHandler>> iterator() {
|
||||
return pipeline.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler last() {
|
||||
return pipeline.last();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandlerContext lastContext() {
|
||||
return pipeline.lastContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> names() {
|
||||
return pipeline.names();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline read() {
|
||||
pipeline.read();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline remove(ChannelHandler arg0) {
|
||||
pipeline.remove(arg0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T remove(Class<T> arg0) {
|
||||
return pipeline.remove(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler remove(String arg0) {
|
||||
return pipeline.remove(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler removeFirst() {
|
||||
return pipeline.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler removeLast() {
|
||||
return pipeline.removeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline replace(ChannelHandler arg0, String arg1, ChannelHandler arg2) {
|
||||
pipeline.replace(arg0, arg1, arg2);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ChannelHandler> T replace(Class<T> arg0, String arg1, ChannelHandler arg2) {
|
||||
return pipeline.replace(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler replace(String arg0, String arg1, ChannelHandler arg2) {
|
||||
return pipeline.replace(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ChannelHandler> toMap() {
|
||||
return pipeline.toMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object arg0, ChannelPromise arg1) {
|
||||
return pipeline.write(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture write(Object arg0) {
|
||||
return pipeline.write(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object arg0, ChannelPromise arg1) {
|
||||
return pipeline.writeAndFlush(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture writeAndFlush(Object arg0) {
|
||||
return pipeline.writeAndFlush(arg0);
|
||||
}
|
||||
|
||||
/* Added in Netty 4.1, seem to be unused
|
||||
public ChannelFuture newFailedFuture(Throwable ex) {
|
||||
return pipeline.newFailedFuture(ex);
|
||||
}
|
||||
|
||||
public ChannelProgressivePromise newProgressivePromise() {
|
||||
return pipeline.newProgressivePromise();
|
||||
}
|
||||
|
||||
public ChannelPromise newPromise() {
|
||||
return pipeline.newPromise();
|
||||
}
|
||||
|
||||
public ChannelFuture newSucceededFuture() {
|
||||
return pipeline.newSucceededFuture();
|
||||
}
|
||||
|
||||
public ChannelPromise voidPromise() {
|
||||
return pipeline.voidPromise();
|
||||
}
|
||||
*/
|
||||
}
|
||||
Loaded 100 of 459 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user