Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
456430f055 | ||
|
|
71af32aa68 | ||
|
|
24204e7515 | ||
|
|
098b3957c0 | ||
|
|
99f480765c | ||
|
|
327a17ba50 | ||
|
|
3991ad0e8d | ||
|
|
1ca55a3c83 | ||
|
|
76991b85c3 | ||
|
|
b6f74a8adb | ||
|
|
952aabe7b0 | ||
|
|
01d77b7d0e | ||
|
|
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 | ||
|
|
4713d320f8 | ||
|
|
083c8e1481 | ||
|
|
6ff6976e85 | ||
|
|
7e87c90b8d | ||
|
|
6fdf0f8255 | ||
|
|
7e12f57eda | ||
|
|
776ec56a2d | ||
|
|
8ec31d83db | ||
|
|
1373911ca2 | ||
|
|
cf5d9dfc7d | ||
|
|
9df0dd48e4 | ||
|
|
d112e9b1dc | ||
|
|
206dc30e17 | ||
|
|
4c3a0afd7e | ||
|
|
8d9bf7d91f | ||
|
|
3ef537242f | ||
|
|
976e4b4217 | ||
|
|
e827a3dd1f | ||
|
|
778031c105 | ||
|
|
6c3654ef40 | ||
|
|
5048f6f34b | ||
|
|
c9dd6e2ba7 | ||
|
|
fc76af9d56 | ||
|
|
aed2285bcb | ||
|
|
1d11908af1 | ||
|
|
3ccc56fef9 | ||
|
|
39e0c974ee | ||
|
|
e52c69ca22 | ||
|
|
d7d53bd905 | ||
|
|
fb2d5016e5 | ||
|
|
015099b92e | ||
|
|
7e8356f0bb | ||
|
|
72b8c4ced8 | ||
|
|
2d22df404a | ||
|
|
0f13a32925 | ||
|
|
1ddb7a0014 | ||
|
|
f9376432c4 | ||
|
|
d195fb6990 | ||
|
|
e4be102a77 | ||
|
|
5f14ccdc34 | ||
|
|
7573459a48 | ||
|
|
01b68cf8e5 | ||
|
|
f3b1a07189 | ||
|
|
c70eb27653 | ||
|
|
4483ce0d79 | ||
|
|
1b23e9ec22 | ||
|
|
1f5692a0c7 | ||
|
|
0ac68dc179 | ||
|
|
d891a057c0 | ||
|
|
9e5bdf4124 | ||
|
|
e1ea295600 | ||
|
|
59171c5443 | ||
|
|
501244cb3c | ||
|
|
390c21f6d5 | ||
|
|
20d78832b0 | ||
|
|
adb3c5392c | ||
|
|
cd2f56d7c7 | ||
|
|
7c9bbc3f6c | ||
|
|
52e396b25d | ||
|
|
aeb2cfb65e | ||
|
|
7f5ea6e493 | ||
|
|
1be94aad78 | ||
|
|
6bb169ac01 | ||
|
|
ea7900d64f | ||
|
|
123abdff30 | ||
|
|
11247f1c01 | ||
|
|
f093c9196d | ||
|
|
81daa62071 | ||
|
|
f99b977888 | ||
|
|
b00ae2ff15 | ||
|
|
b4f9c501b3 | ||
|
|
1738c6f243 | ||
|
|
abea7441a4 | ||
|
|
3887ba27d2 | ||
|
|
76a3af84a1 | ||
|
|
be5d42ee19 | ||
|
|
790711c16e | ||
|
|
8c3127dee1 | ||
|
|
5ec87c9d76 | ||
|
|
2205ce639e | ||
|
|
2844d75607 | ||
|
|
a6810ea874 | ||
|
|
aaf1af8e41 | ||
|
|
b8dd43aeed | ||
|
|
e959efc5af | ||
|
|
73ce01bbde | ||
|
|
b30628f6db | ||
|
|
9289825d87 | ||
|
|
57cfbc4f81 | ||
|
|
3ab2026d62 | ||
|
|
e439c778a6 | ||
|
|
98413c917e | ||
|
|
540a5e53c4 | ||
|
|
27047f83a5 | ||
|
|
222b535c15 | ||
|
|
3f31af6b70 | ||
|
|
b6a054b766 | ||
|
|
3b7b692e80 | ||
|
|
c6788addeb | ||
|
|
5b38bc7f38 | ||
|
|
33bb12713d | ||
|
|
d175a7655e | ||
|
|
4afb38aae1 | ||
|
|
36b5670b3d | ||
|
|
7b61796506 | ||
|
|
684b687e42 | ||
|
|
7a9b9afe2e | ||
|
|
7d382279ad | ||
|
|
b4f5f18283 | ||
|
|
17e150393a | ||
|
|
b5ecfb9c5f | ||
|
|
31a0c9b10c | ||
|
|
e4d95e09cf | ||
|
|
d173be0db5 | ||
|
|
051989a396 | ||
|
|
795c77e943 | ||
|
|
1de6bbc9dc | ||
|
|
474c74b1f9 | ||
|
|
caaa57c915 | ||
|
|
588f736348 | ||
|
|
828302150a | ||
|
|
144723af18 | ||
|
|
087913ab3a | ||
|
|
64942cbb5e | ||
|
|
97430a8e42 | ||
|
|
1bdd200794 | ||
|
|
679035c3bf | ||
|
|
bd4352f041 | ||
|
|
05ffeb8e7f | ||
|
|
6c982a83f0 | ||
|
|
9860fbaec6 | ||
|
|
26c29d9c85 | ||
|
|
242b25eb1e | ||
|
|
c13b8d4fc6 | ||
|
|
12e9c08a1a | ||
|
|
98a0528806 | ||
|
|
015a6067fd | ||
|
|
e34105e754 | ||
|
|
a995866d5b | ||
|
|
a88347dfe6 | ||
|
|
4f404a41c6 | ||
|
|
88444dcf74 | ||
|
|
4f5ab9eb8c | ||
|
|
f51427b63c | ||
|
|
cdcb6a6007 |
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,9 +2,11 @@ _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`
|
||||
|
||||
Debug paste link:
|
||||
|
||||
Description and relevant errors:
|
||||
Description and relevant errors:
|
||||
@@ -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
|
||||
+7
-2
@@ -8,7 +8,7 @@
|
||||
.settings/
|
||||
bin/
|
||||
.idea/
|
||||
.iml
|
||||
*.iml
|
||||
.ipr
|
||||
.iws
|
||||
|
||||
@@ -175,4 +175,9 @@ pip-log.txt
|
||||
.DS_Store
|
||||
|
||||
# Log4J files
|
||||
logs/
|
||||
logs/
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
logs/
|
||||
.java-version
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk7
|
||||
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,35 @@
|
||||
# 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/).
|
||||
Currently maintained by dmulloy2 on behalf of [Spigot](https://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 +38,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,20 +47,32 @@ You can also add ProtocolLib as a Maven dependency:
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
...
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>3.6.5</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
````
|
||||
|
||||
Or use the maven dependency with gradle:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url "https://repo.dmulloy2.net/repository/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0-SNAPSHOT'
|
||||
}
|
||||
```
|
||||
|
||||
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
|
||||
|
||||
````java
|
||||
@@ -72,16 +87,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);
|
||||
}
|
||||
});
|
||||
````
|
||||
@@ -91,20 +104,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!");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -115,49 +127,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.
|
||||
|
||||
|
||||
### Incompatibility
|
||||
|
||||
The following plugins (to be expanded) are not compatible with ProtocolLib:
|
||||
* TagAPI
|
||||
@@ -14,8 +14,8 @@
|
||||
<parent>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-Parent</artifactId>
|
||||
<version>v3-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
<version>v4-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<repositories>
|
||||
@@ -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>
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -77,20 +77,6 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.8.8-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.8.8-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
File renamed without changes.
+3
-3
@@ -178,7 +178,7 @@ public final class Reflection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||
*
|
||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
@@ -191,7 +191,7 @@ public final class Reflection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||
*
|
||||
* @param clazz - a class to start with.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
@@ -204,7 +204,7 @@ public final class Reflection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||
*
|
||||
* @param clazz - a class to start with.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
+11
-13
@@ -72,7 +72,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;
|
||||
@@ -105,8 +105,7 @@ public abstract class TinyProtocol {
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Damn you, late bind
|
||||
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
|
||||
|
||||
// Damn you, late bind
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -129,7 +128,7 @@ public abstract class TinyProtocol {
|
||||
synchronized (networkManagers) {
|
||||
// Stop injecting channels
|
||||
if (!closed) {
|
||||
injectChannelInternal(channel);
|
||||
channel.eventLoop().submit(() -> injectChannelInternal(channel));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -256,13 +255,12 @@ public abstract class TinyProtocol {
|
||||
* <p>
|
||||
* Note that this is not executed on the main thread.
|
||||
*
|
||||
* @param reciever - the receiving player, NULL for early login/status packets.
|
||||
* @param receiver - the receiving player, NULL for early login/status packets.
|
||||
* @param channel - the channel that received the packet. Never NULL.
|
||||
* @param remoteAddress - remote address of the sending client. Never NULL.
|
||||
* @param packet - the packet being sent.
|
||||
* @return The packet to send instead, or NULL to cancel the transmission.
|
||||
*/
|
||||
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
|
||||
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
@@ -283,7 +281,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Send a packet to a particular player.
|
||||
* <p>
|
||||
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
|
||||
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* @param player - the destination player.
|
||||
* @param packet - the packet to send.
|
||||
@@ -295,7 +293,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Send a packet to a particular client.
|
||||
* <p>
|
||||
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
|
||||
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* @param channel - client identified by a channel.
|
||||
* @param packet - the packet to send.
|
||||
@@ -307,7 +305,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Pretend that a given packet has been received from a player.
|
||||
* <p>
|
||||
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
|
||||
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* @param player - the player that sent the packet.
|
||||
* @param packet - the packet that will be received by the server.
|
||||
@@ -319,7 +317,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Pretend that a given packet has been received from a given client.
|
||||
* <p>
|
||||
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
|
||||
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||
*
|
||||
* @param channel - client identified by a channel.
|
||||
* @param packet - the packet that will be received by the server.
|
||||
@@ -353,7 +351,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Add a custom channel handler to the given channel.
|
||||
*
|
||||
* @param player - the channel to inject.
|
||||
* @param channel - the channel to inject.
|
||||
* @return The intercepted channel, or NULL if it has already been injected.
|
||||
*/
|
||||
public void injectChannel(Channel channel) {
|
||||
@@ -363,7 +361,7 @@ public abstract class TinyProtocol {
|
||||
/**
|
||||
* Add a custom channel handler to the given channel.
|
||||
*
|
||||
* @param player - the channel to inject.
|
||||
* @param channel - the channel to inject.
|
||||
* @return The packet interceptor.
|
||||
*/
|
||||
private PacketInterceptor injectChannelInternal(Channel channel) {
|
||||
File renamed without changes.
+112
@@ -0,0 +1,112 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
}
|
||||
|
||||
group = 'com.comphenix.protocol'
|
||||
version = '5.0.0-SNAPSHOT'
|
||||
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'
|
||||
}
|
||||
|
||||
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,245 +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.0-SNAPSHOT</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>v4-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.number></project.build.number>
|
||||
</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>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>com.comphenix.protocol.Application</Main-Class>
|
||||
<Implementation-Title>ProtocolLib</Implementation-Title>
|
||||
<Implementation-Version>${project.version}${project.build.number}</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.10</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>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<!-- Included with Minecraft
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.23.Final</version>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.executors</groupId>
|
||||
<artifactId>BukkitExecutors</artifactId>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</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;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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.9";
|
||||
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.9.4";
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.9.4) was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2016-05-10";
|
||||
|
||||
/**
|
||||
* 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,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.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
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,175 +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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
-111
@@ -1,111 +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() {
|
||||
ProtocolLogger.debug("Initializing the Netty protocol registry"); // Debug for issue #202
|
||||
|
||||
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 (int i = 0; i < protocols.length; i++) {
|
||||
Object protocol = protocols[i];
|
||||
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 (IllegalArgumentException ex) {
|
||||
// Sometimes this happens with fake packets, just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-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,55 +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.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class WirePacket {
|
||||
private final int id;
|
||||
private final byte[] bytes;
|
||||
|
||||
public WirePacket(int id, byte[] bytes) {
|
||||
this.id = id;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void writeId(ByteBuf output) {
|
||||
int i = id;
|
||||
while ((i & -128) != 0) {
|
||||
output.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
}
|
||||
|
||||
output.writeByte(i);
|
||||
}
|
||||
|
||||
public void writeBytes(ByteBuf output) {
|
||||
output.writeBytes(bytes);
|
||||
}
|
||||
}
|
||||
@@ -1,322 +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.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.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 {
|
||||
initialize();
|
||||
|
||||
if (LEGACY_SERVER_PACKETS == null) {
|
||||
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
|
||||
}
|
||||
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 {
|
||||
initialize();
|
||||
|
||||
if (LEGACY_CLIENT_PACKETS == null) {
|
||||
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
|
||||
}
|
||||
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();
|
||||
return NETTY.getPacketTypeLookup().get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,138 +0,0 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
|
||||
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
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.
|
||||
*/
|
||||
public 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 EmptyClassVisitor() {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// Check method
|
||||
if (methodName.equals(name) && methodDescription.equals(desc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
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,685 +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.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(getFieldType(fieldIndex), 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) {
|
||||
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 != null && 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);
|
||||
|
||||
if (fieldType != null) {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -1,120 +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.cloning;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Represents an object that can clone a specific list of Bukkit- and Minecraft-related objects.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class BukkitCloner implements Cloner {
|
||||
// List of classes we support
|
||||
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
|
||||
|
||||
public BukkitCloner() {
|
||||
addClass(0, MinecraftReflection.getItemStackClass());
|
||||
addClass(1, MinecraftReflection.getDataWatcherClass());
|
||||
|
||||
// Try to add position classes
|
||||
try {
|
||||
addClass(2, MinecraftReflection.getBlockPositionClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
|
||||
try {
|
||||
addClass(3, MinecraftReflection.getChunkPositionClass());
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
addClass(4, MinecraftReflection.getServerPingClass());
|
||||
}
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
|
||||
addClass(6, MinecraftReflection.getMinecraftKeyClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void addClass(int id, Class<?> clazz) {
|
||||
if (clazz != null)
|
||||
clonableClasses.put(id, clazz);
|
||||
}
|
||||
|
||||
private int findMatchingClass(Class<?> type) {
|
||||
// See if is a subclass of any of our supported superclasses
|
||||
for (Entry<Integer, Class<?>> entry : clonableClasses.entrySet()) {
|
||||
if (entry.getValue().isAssignableFrom(type)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClone(Object source) {
|
||||
if (source == null)
|
||||
return false;
|
||||
|
||||
return findMatchingClass(source.getClass()) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone(Object source) {
|
||||
if (source == null)
|
||||
throw new IllegalArgumentException("source cannot be NULL.");
|
||||
|
||||
// Convert to a wrapper
|
||||
switch (findMatchingClass(source.getClass())) {
|
||||
case 0:
|
||||
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
|
||||
case 1:
|
||||
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
|
||||
return dataConverter.getGeneric(clonableClasses.get(1), dataConverter.getSpecific(source).deepClone());
|
||||
case 2:
|
||||
EquivalentConverter<BlockPosition> blockConverter = BlockPosition.getConverter();
|
||||
return blockConverter.getGeneric(clonableClasses.get(2), blockConverter.getSpecific(source));
|
||||
case 3:
|
||||
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
|
||||
return chunkConverter.getGeneric(clonableClasses.get(3), chunkConverter.getSpecific(source));
|
||||
case 4:
|
||||
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
|
||||
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
|
||||
case 5:
|
||||
return source;
|
||||
case 6:
|
||||
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
|
||||
return keyConverter.getGeneric(clonableClasses.get(5), keyConverter.getSpecific(source));
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
-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(getFieldType(index), 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);
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.ClassVisitor;
|
||||
import net.sf.cglib.asm.FieldVisitor;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public abstract class EmptyClassVisitor implements ClassVisitor {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.Label;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public class EmptyMethodVisitor implements MethodVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCode() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object cst) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start,
|
||||
Label end, int index) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
-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 Method}.
|
||||
*
|
||||
* @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 Method}.
|
||||
*
|
||||
* @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 Method} 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 Method} 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 Method} 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 Method} 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();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
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;
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
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 errorLabel = new Label();
|
||||
Label returnLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, 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, returnLabel);
|
||||
}
|
||||
|
||||
mv.visitLabel(errorLabel);
|
||||
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(returnLabel);
|
||||
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 errorLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, 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(errorLabel);
|
||||
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,95 +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 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, 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) {
|
||||
cache.put(className, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Class<?> getPackageClass(String className) {
|
||||
try {
|
||||
Class<?> result = cache.get(Preconditions.checkNotNull(className, "className cannot be NULL"));
|
||||
|
||||
// Concurrency is not a problem - we don't care if we look up a class twice
|
||||
if (result == null) {
|
||||
// Look up the class dynamically
|
||||
result = source.loadClass(combine(packageName, className));
|
||||
if (result == null)
|
||||
throw new IllegalArgumentException("Source " + source + " returned NULL for " + className);
|
||||
|
||||
cache.put(className, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Cannot find class " + combine(packageName, className), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_9_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,235 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
|
||||
/**
|
||||
* 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).getMethodByName("sendPacket.*");
|
||||
} 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(), GenericFutureListener[].class);
|
||||
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(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* A lookup of the associated protocol version of a given Minecraft server.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class MinecraftProtocolVersion {
|
||||
private static final NavigableMap<MinecraftVersion, Integer> lookup = createLookup();
|
||||
|
||||
private static NavigableMap<MinecraftVersion, Integer> createLookup() {
|
||||
TreeMap<MinecraftVersion, Integer> map = Maps.newTreeMap();
|
||||
|
||||
// Source: http://wiki.vg/Protocol_version_numbers
|
||||
// Doesn't include pre-releases
|
||||
map.put(new MinecraftVersion(1, 0, 0), 22);
|
||||
map.put(new MinecraftVersion(1, 1, 0), 23);
|
||||
map.put(new MinecraftVersion(1, 2, 2), 28);
|
||||
map.put(new MinecraftVersion(1, 2, 4), 29);
|
||||
map.put(new MinecraftVersion(1, 3, 1), 39);
|
||||
map.put(new MinecraftVersion(1, 4, 2), 47);
|
||||
map.put(new MinecraftVersion(1, 4, 3), 48);
|
||||
map.put(new MinecraftVersion(1, 4, 4), 49);
|
||||
map.put(new MinecraftVersion(1, 4, 6), 51);
|
||||
map.put(new MinecraftVersion(1, 5, 0), 60);
|
||||
map.put(new MinecraftVersion(1, 5, 2), 61);
|
||||
map.put(new MinecraftVersion(1, 6, 0), 72);
|
||||
map.put(new MinecraftVersion(1, 6, 1), 73);
|
||||
map.put(new MinecraftVersion(1, 6, 2), 74);
|
||||
map.put(new MinecraftVersion(1, 6, 4), 78);
|
||||
|
||||
// After Netty
|
||||
map.put(new MinecraftVersion(1, 7, 1), 4);
|
||||
map.put(new MinecraftVersion(1, 7, 6), 5);
|
||||
map.put(new MinecraftVersion(1, 8, 0), 47);
|
||||
map.put(new MinecraftVersion(1, 9, 0), 107);
|
||||
map.put(new MinecraftVersion(1, 9, 2), 109);
|
||||
map.put(new MinecraftVersion(1, 9, 4), 110);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version of the Minecraft protocol for the current version of Minecraft.
|
||||
* @return The version number.
|
||||
*/
|
||||
public static int getCurrentVersion() {
|
||||
return getVersion(ProtocolLibrary.getProtocolManager().getMinecraftVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version of the Minecraft protocol for this version of Minecraft.
|
||||
* @param version - the version.
|
||||
* @return The version number.
|
||||
*/
|
||||
public static int getVersion(MinecraftVersion version) {
|
||||
Entry<MinecraftVersion, Integer> result = lookup.floorEntry(version);
|
||||
return result != null ? result.getValue() : Integer.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
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.RemapperUnavaibleException.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 RemapperUnavaibleException 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 RemapperUnavaibleException(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 RemapperUnavaibleException(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 (RemapperUnavaibleException 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 RemapperUnavaibleException 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 RemapperUnavaibleException(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. This works by checking
|
||||
* the server version for the String "Spigot"
|
||||
* @return True if it is, false if not.
|
||||
*/
|
||||
public static boolean isUsingSpigot() {
|
||||
return Bukkit.getServer().getVersion().contains("Spigot");
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
/**
|
||||
* Allows us to stay backwards compatible with older versions of Bukkit.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class WrappedScheduler {
|
||||
/**
|
||||
* Represents a backwards compatible Bukkit task.
|
||||
*/
|
||||
public static interface TaskWrapper {
|
||||
/**
|
||||
* Cancel the current task.
|
||||
*/
|
||||
public void cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a given task for a single asynchronous execution.
|
||||
* @param plugin - the owner plugin.
|
||||
* @param runnable - the task to run.
|
||||
* @param firstDelay - the amount of time to wait until executing the task.
|
||||
* @return A cancel token.
|
||||
*/
|
||||
public static TaskWrapper runAsynchronouslyOnce(final Plugin plugin, Runnable runnable, long firstDelay) {
|
||||
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a given task for multiple asynchronous executions.
|
||||
* @param plugin - the owner plugin.
|
||||
* @param runnable - the task to run.
|
||||
* @param firstDelay - the amount of time to wait until executing the task for the first time.
|
||||
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
|
||||
* @return A cancel token.
|
||||
*/
|
||||
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, Runnable runnable, long firstDelay, long repeatDelay) {
|
||||
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, repeatDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a given task for asynchronous execution.
|
||||
* @param plugin - the owner plugin.
|
||||
* @param scheduler - the current Bukkit scheduler.
|
||||
* @param runnable - the task to run.
|
||||
* @param firstDelay - the amount of time to wait until executing the task for the first time.
|
||||
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
|
||||
* @return A cancel token.
|
||||
*/
|
||||
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
|
||||
try {
|
||||
@SuppressWarnings("deprecation")
|
||||
final int taskID = scheduler.scheduleAsyncRepeatingTask(plugin, runnable, firstDelay, repeatDelay);
|
||||
|
||||
// Return the cancellable object
|
||||
return new TaskWrapper() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
scheduler.cancelTask(taskID);
|
||||
}
|
||||
};
|
||||
|
||||
} catch (NoSuchMethodError e) {
|
||||
return tryUpdatedVersion(plugin, scheduler, runnable, firstDelay, repeatDelay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to do the same with the updated scheduling method.
|
||||
* @param plugin - the owner plugin.
|
||||
* @param scheduler - the current Bukkit scheduler.
|
||||
* @param runnable - the task to run.
|
||||
* @param firstDelay - the amount of time to wait until executing the task for the first time.
|
||||
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
|
||||
* @return A cancel token.
|
||||
*/
|
||||
private static TaskWrapper tryUpdatedVersion(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
|
||||
final BukkitTask task = scheduler.runTaskTimerAsynchronously(plugin, runnable, firstDelay, repeatDelay);
|
||||
|
||||
return new TaskWrapper() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
task.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
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(Class<?> genericType, 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<Object>(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,166 +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.Modifier;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
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.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Represents a wrapper around IBlockData.
|
||||
*
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class WrappedBlockData extends AbstractWrapper {
|
||||
private static final Class<?> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
|
||||
private static final Class<?> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
|
||||
private static final Class<?> BLOCK = MinecraftReflection.getBlockClass();
|
||||
|
||||
private static MethodAccessor FROM_LEGACY_DATA = null;
|
||||
private static MethodAccessor TO_LEGACY_DATA = null;
|
||||
private static MethodAccessor GET_NMS_BLOCK = null;
|
||||
private static MethodAccessor GET_BLOCK = null;
|
||||
|
||||
static {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(IBLOCK_DATA)
|
||||
.build();
|
||||
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
|
||||
|
||||
contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.parameterExactArray(IBLOCK_DATA)
|
||||
.returnTypeExact(int.class)
|
||||
.build();
|
||||
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
|
||||
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[] { Material.class }));
|
||||
|
||||
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
|
||||
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
|
||||
new Class<?>[0]));
|
||||
}
|
||||
|
||||
public WrappedBlockData(Object handle) {
|
||||
super(IBLOCK_DATA);
|
||||
setHandle(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this BlockData.
|
||||
* @return The type of this BlockData.
|
||||
*/
|
||||
public Material getType() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return BukkitConverters.getBlockConverter().getSpecific(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type id of this BlockData.
|
||||
* @return The type id of this BlockData.
|
||||
* @deprecated ID's are deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public int getTypeId() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return BukkitConverters.getBlockIDConverter().getSpecific(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data of this BlockData.
|
||||
* @return The data of this BlockData.
|
||||
*/
|
||||
public int getData() {
|
||||
Object block = GET_BLOCK.invoke(handle);
|
||||
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of this BlockData.
|
||||
* @param type New type
|
||||
*/
|
||||
public void setType(Material type) {
|
||||
setTypeAndData(type, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data of this BlockData.
|
||||
* @param data New data
|
||||
*/
|
||||
public void setData(int data) {
|
||||
setTypeAndData(getType(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type and data of this BlockData.
|
||||
* @param type New type
|
||||
* @param data New data
|
||||
*/
|
||||
public void setTypeAndData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
setHandle(blockData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BlockData instance with the given type and no data.
|
||||
* @param type Block type
|
||||
* @return New BlockData
|
||||
*/
|
||||
public static WrappedBlockData createData(Material type) {
|
||||
return createData(type, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BlockData instance with the given type and data.
|
||||
* @param type Block type
|
||||
* @param data Block data
|
||||
* @return New BlockData
|
||||
*/
|
||||
public static WrappedBlockData createData(Material type, int data) {
|
||||
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
|
||||
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
|
||||
return new WrappedBlockData(blockData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrappedBlockData[handle=" + handle + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof WrappedBlockData) {
|
||||
WrappedBlockData that = (WrappedBlockData) o;
|
||||
return this.getType() == that.getType();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
-186
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* 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 boolean equals(Object other) {
|
||||
if (other instanceof WrappedChunkCoordinate) {
|
||||
WrappedChunkCoordinate wrapper = (WrappedChunkCoordinate) other;
|
||||
return Objects.equal(handle, wrapper.handle);
|
||||
}
|
||||
|
||||
// It's tempting to handle the ChunkCoordinate case too, but then
|
||||
// the equals() method won't be commutative, causing a.equals(b) to
|
||||
// be different to b.equals(a).
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return handle.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ChunkCoordinate [x: %s, y: %s, z: %s]", getX(), getY(), getZ());
|
||||
}
|
||||
}
|
||||
-232
@@ -1,232 +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.wrappers;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
|
||||
/**
|
||||
* Represents a DataWatcher Item in 1.9.
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class WrappedWatchableObject extends AbstractWrapper {
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass();
|
||||
private static ConstructorAccessor constructor;
|
||||
|
||||
private final StructureModifier<Object> modifier;
|
||||
|
||||
/**
|
||||
* Constructs a wrapped watchable object around an existing NMS data watcher item.
|
||||
* @param handle Data watcher item
|
||||
*/
|
||||
public WrappedWatchableObject(Object handle) {
|
||||
super(HANDLE_TYPE);
|
||||
setHandle(handle);
|
||||
this.modifier = new StructureModifier<Object>(handleType).withTarget(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a wrapped watchable object with a given watcher object and initial value.
|
||||
* @param watcherObject Watcher object
|
||||
* @param value Initial value
|
||||
*/
|
||||
public WrappedWatchableObject(WrappedDataWatcherObject watcherObject, Object value) {
|
||||
this(newHandle(watcherObject, value));
|
||||
}
|
||||
|
||||
private static Object newHandle(WrappedDataWatcherObject watcherObject, Object value) {
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
|
||||
}
|
||||
|
||||
return constructor.invoke(watcherObject.getHandle(), value);
|
||||
}
|
||||
|
||||
// ---- Getter methods
|
||||
|
||||
/**
|
||||
* Gets this Item's watcher object, which contains the index and serializer.
|
||||
* @return The watcher object
|
||||
*/
|
||||
public WrappedDataWatcherObject getWatcherObject() {
|
||||
return new WrappedDataWatcherObject(modifier.read(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this Item's index from the watcher object
|
||||
* @return The index
|
||||
*/
|
||||
public int getIndex() {
|
||||
return getWatcherObject().getIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the wrapped value of this data watcher item.
|
||||
* @return The wrapped value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return getWrapped(getRawValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw value of this data watcher item.
|
||||
* @return Raw value
|
||||
*/
|
||||
public Object getRawValue() {
|
||||
return modifier.readSafely(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the wrapped object value, if needed.
|
||||
*
|
||||
* @param value - the raw NMS object to wrap.
|
||||
* @return The wrapped object.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
static Object getWrapped(Object value) {
|
||||
// Handle the special cases
|
||||
if (MinecraftReflection.isItemStack(value)) {
|
||||
return BukkitConverters.getItemStackConverter().getSpecific(value);
|
||||
} else if (MinecraftReflection.isChunkCoordinates(value)) {
|
||||
return new WrappedChunkCoordinate((Comparable) value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the wrapped type, if needed.
|
||||
*
|
||||
* @param wrapped - the wrapped class type.
|
||||
* @return The wrapped class type.
|
||||
*/
|
||||
static Class<?> getWrappedType(Class<?> unwrapped) {
|
||||
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
|
||||
return ChunkPosition.class;
|
||||
else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass()))
|
||||
return BlockPosition.class;
|
||||
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
|
||||
return WrappedChunkCoordinate.class;
|
||||
else if (unwrapped.equals(MinecraftReflection.getItemStackClass()))
|
||||
return ItemStack.class;
|
||||
else
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this item.
|
||||
* @param value New value
|
||||
* @param updateClient Whether or not to update the client
|
||||
*/
|
||||
public void setValue(Object value, boolean updateClient) {
|
||||
modifier.write(1, getUnwrapped(value));
|
||||
|
||||
if (updateClient) {
|
||||
setDirtyState(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this item.
|
||||
* @param value New value
|
||||
*/
|
||||
public void setValue(Object value) {
|
||||
setValue(value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the raw NMS value.
|
||||
*
|
||||
* @param wrapped - the wrapped position.
|
||||
* @return The raw NMS object.
|
||||
*/
|
||||
static Object getUnwrapped(Object wrapped) {
|
||||
// Convert special cases
|
||||
if (wrapped instanceof ChunkPosition)
|
||||
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
|
||||
else if (wrapped instanceof BlockPosition)
|
||||
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
|
||||
else if (wrapped instanceof WrappedChunkCoordinate)
|
||||
return ((WrappedChunkCoordinate) wrapped).getHandle();
|
||||
else if (wrapped instanceof ItemStack)
|
||||
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
|
||||
else
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the unwrapped type, if needed.
|
||||
*
|
||||
* @param wrapped - the unwrapped class type.
|
||||
* @return The unwrapped class type.
|
||||
*/
|
||||
static Class<?> getUnwrappedType(Class<?> wrapped) {
|
||||
if (wrapped.equals(ChunkPosition.class))
|
||||
return MinecraftReflection.getChunkPositionClass();
|
||||
else if (wrapped.equals(BlockPosition.class))
|
||||
return MinecraftReflection.getBlockPositionClass();
|
||||
else if (wrapped.equals(WrappedChunkCoordinate.class))
|
||||
return MinecraftReflection.getChunkCoordinatesClass();
|
||||
else if (ItemStack.class.isAssignableFrom(wrapped))
|
||||
return MinecraftReflection.getItemStackClass();
|
||||
else
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the value must be synchronized with the client.
|
||||
* @return True if it must, false if not
|
||||
*/
|
||||
public boolean getDirtyState() {
|
||||
return (boolean) modifier.read(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this item's dirty state
|
||||
* @param dirty New state
|
||||
*/
|
||||
public void setDirtyState(boolean dirty) {
|
||||
modifier.write(2, dirty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null) return false;
|
||||
|
||||
if (obj instanceof WrappedWatchableObject) {
|
||||
// watcher object, value, dirty state
|
||||
WrappedWatchableObject other = (WrappedWatchableObject) obj;
|
||||
return getWatcherObject().equals(other.getWatcherObject())
|
||||
&& getRawValue().equals(other.getRawValue())
|
||||
&& getDirtyState() == other.getDirtyState();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
|
||||
}
|
||||
}
|
||||
-250
@@ -1,250 +0,0 @@
|
||||
package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
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.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Manipulate tile entities.
|
||||
* @author Kristian
|
||||
*/
|
||||
class TileEntityAccessor<T extends BlockState> {
|
||||
/**
|
||||
* Token indicating that the given block state doesn't contany any tile entities.
|
||||
*/
|
||||
private static final TileEntityAccessor<BlockState> EMPTY_ACCESSOR = new TileEntityAccessor<BlockState>();
|
||||
|
||||
/**
|
||||
* Cached field accessors - {@link #EMPTY_ACCESSOR} represents no valid tile entity.
|
||||
*/
|
||||
private static final ConcurrentMap<Class<?>, TileEntityAccessor<?>> cachedAccessors = Maps.newConcurrentMap();
|
||||
|
||||
private FieldAccessor tileEntityField;
|
||||
private MethodAccessor readCompound;
|
||||
private MethodAccessor writeCompound;
|
||||
|
||||
// For CGLib detection
|
||||
private boolean writeDetected;
|
||||
private boolean readDetected;
|
||||
|
||||
private TileEntityAccessor() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new tile entity accessor.
|
||||
* @param tileEntityField - the tile entity field.
|
||||
* @param state - the block state.
|
||||
*/
|
||||
private TileEntityAccessor(FieldAccessor tileEntityField, T state) {
|
||||
if (tileEntityField != null) {
|
||||
this.tileEntityField = tileEntityField;
|
||||
Class<?> type = tileEntityField.getField().getType();
|
||||
|
||||
// Possible read/write methods
|
||||
try {
|
||||
findMethodsUsingASM();
|
||||
} catch (IOException ex1) {
|
||||
try {
|
||||
// Much slower though
|
||||
findMethodUsingCGLib(state);
|
||||
} catch (Exception ex2) {
|
||||
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we found them
|
||||
if (readCompound == null)
|
||||
throw new RuntimeException("Unable to find read method in " + type);
|
||||
if (writeCompound == null)
|
||||
throw new RuntimeException("Unable to find write method in " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the read/write methods in TileEntity.
|
||||
* @throws IOException If we cannot find these methods.
|
||||
*/
|
||||
private void findMethodsUsingASM() throws IOException {
|
||||
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
|
||||
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
|
||||
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
|
||||
|
||||
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
|
||||
final String expectedDesc = "(L" + tagCompoundName + ";)V";
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
final String methodName = name;
|
||||
|
||||
// Detect read/write calls to NBTTagCompound
|
||||
if (expectedDesc.equals(desc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
private int readMethods;
|
||||
private int writeMethods;
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
// This must be a virtual call on NBTTagCompound that accepts a String
|
||||
if (opcode == Opcodes.INVOKEVIRTUAL &&
|
||||
tagCompoundName.equals(owner) &&
|
||||
desc.startsWith("(Ljava/lang/String")) {
|
||||
|
||||
// Is this a write call?
|
||||
if (desc.endsWith(")V")) {
|
||||
writeMethods++;
|
||||
} else {
|
||||
readMethods++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (readMethods > writeMethods) {
|
||||
readCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
|
||||
} else if (writeMethods > readMethods) {
|
||||
writeCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
|
||||
}
|
||||
super.visitEnd();
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the read/write methods in TileEntity.
|
||||
* @param blockState - the block state.
|
||||
* @throws IOException If we cannot find these methods.
|
||||
*/
|
||||
private void findMethodUsingCGLib(T blockState) throws IOException {
|
||||
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
|
||||
|
||||
// This is a much slower method, but it is necessary in MCPC
|
||||
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
|
||||
enhancer.setSuperclass(nbtCompoundClass);
|
||||
enhancer.setCallback(new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
if (method.getReturnType().equals(Void.TYPE)) {
|
||||
// Write method
|
||||
writeDetected = true;
|
||||
} else {
|
||||
// Read method
|
||||
readDetected = true;
|
||||
}
|
||||
throw new RuntimeException("Stop execution.");
|
||||
}
|
||||
});
|
||||
Object compound = enhancer.create();
|
||||
Object tileEntity = tileEntityField.get(blockState);
|
||||
|
||||
// Look in every read/write like method
|
||||
for (Method method : FuzzyReflection.fromObject(tileEntity, true).
|
||||
getMethodListByParameters(Void.TYPE, new Class<?>[] { nbtCompoundClass })) {
|
||||
|
||||
try {
|
||||
readDetected = false;
|
||||
writeDetected = false;
|
||||
method.invoke(tileEntity, compound);
|
||||
} catch (Exception e) {
|
||||
// Okay - see if we detected a write or read
|
||||
if (readDetected)
|
||||
readCompound = Accessors.getMethodAccessor(method, true);
|
||||
if (writeDetected)
|
||||
writeCompound = Accessors.getMethodAccessor(method, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the JAR name (slash instead of dots) of the given clas.
|
||||
* @param clazz - the class.
|
||||
* @return The JAR name.
|
||||
*/
|
||||
private static String getJarName(Class<?> clazz) {
|
||||
return clazz.getCanonicalName().replace('.', '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the NBT compound that represents a given tile entity.
|
||||
* @param state - tile entity represented by a block state.
|
||||
* @return The compound.
|
||||
*/
|
||||
public NbtCompound readBlockState(T state) {
|
||||
NbtCompound output = NbtFactory.ofCompound("");
|
||||
Object tileEntity = tileEntityField.get(state);
|
||||
|
||||
// Write the block state to the output compound
|
||||
writeCompound.invoke(tileEntity, NbtFactory.fromBase(output).getHandle());
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the NBT compound as a tile entity.
|
||||
* @param state - target block state.
|
||||
* @param compound - the compound.
|
||||
*/
|
||||
public void writeBlockState(T state, NbtCompound compound) {
|
||||
Object tileEntity = tileEntityField.get(state);
|
||||
|
||||
// Ensure the block state is set to the compound
|
||||
readCompound.invoke(tileEntity, NbtFactory.fromBase(compound).getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an accessor for the tile entity at a specific location.
|
||||
* @param state - the block state.
|
||||
* @return The accessor, or NULL if this block state doesn't contain any tile entities.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BlockState> TileEntityAccessor<T> getAccessor(T state) {
|
||||
Class<?> craftBlockState = state.getClass();
|
||||
TileEntityAccessor<?> accessor = cachedAccessors.get(craftBlockState);
|
||||
|
||||
// Attempt to construct the accessor
|
||||
if (accessor == null ) {
|
||||
TileEntityAccessor<?> created = null;
|
||||
FieldAccessor field = null;
|
||||
|
||||
try {
|
||||
field = Accessors.getFieldAccessor(craftBlockState, MinecraftReflection.getTileEntityClass(), true);
|
||||
} catch (Exception e) {
|
||||
created = EMPTY_ACCESSOR;
|
||||
}
|
||||
if (field != null) {
|
||||
created = new TileEntityAccessor<T>(field, state);
|
||||
}
|
||||
accessor = cachedAccessors.putIfAbsent(craftBlockState, created);
|
||||
|
||||
// We won the race
|
||||
if (accessor == null) {
|
||||
accessor = created;
|
||||
}
|
||||
}
|
||||
return (TileEntityAccessor<T>) (accessor != EMPTY_ACCESSOR ? accessor : null);
|
||||
}
|
||||
}
|
||||
-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,283 +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.0.1</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>v4-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.number></project.build.number>
|
||||
</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>2.4.3</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>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>com.comphenix.protocol.Application</Main-Class>
|
||||
<Implementation-Title>ProtocolLib</Implementation-Title>
|
||||
<Implementation-Version>${project.version}${project.build.number}</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.10</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>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<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>
|
||||
|
||||
<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>4.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- Included with Minecraft
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.23.Final</version>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.executors</groupId>
|
||||
<artifactId>BukkitExecutors</artifactId>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
/**
|
||||
* Ignore this class.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class Application {
|
||||
private static Thread mainThread;
|
||||
private static boolean primaryMethod = true;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// For now, though we might consider making a proper application
|
||||
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are running on the main thread.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isPrimaryThread() {
|
||||
if (primaryMethod) {
|
||||
try {
|
||||
return Bukkit.isPrimaryThread();
|
||||
} catch (LinkageError e) {
|
||||
primaryMethod = false;
|
||||
}
|
||||
}
|
||||
// Fallback method
|
||||
return Thread.currentThread().equals(mainThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the calling thread as the primary thread.
|
||||
*/
|
||||
static void registerPrimaryThread() {
|
||||
mainThread = Thread.currentThread();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
-275
@@ -1,275 +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.Constructor;
|
||||
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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* While this function may look pretty bad, it's essentially just a reflection-warped
|
||||
* version of the following:
|
||||
*
|
||||
* @SuppressWarnings("unchecked")
|
||||
* public static void updateEntity2(Entity entity, List<Player> observers) {
|
||||
*
|
||||
* World world = entity.getWorld();
|
||||
* WorldServer worldServer = ((CraftWorld) world).getHandle();
|
||||
*
|
||||
* EntityTracker tracker = worldServer.tracker;
|
||||
* EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
|
||||
*
|
||||
* List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
|
||||
*
|
||||
* entry.trackedPlayers.removeAll(nmsPlayers);
|
||||
* entry.scanPlayers(nmsPlayers);
|
||||
* }
|
||||
*
|
||||
* private static List<EntityPlayer> getNmsPlayers(List<Player> players) {
|
||||
* List<EntityPlayer> nsmPlayers = new ArrayList<EntityPlayer>();
|
||||
*
|
||||
* for (Player bukkitPlayer : players) {
|
||||
* CraftPlayer craftPlayer = (CraftPlayer) bukkitPlayer;
|
||||
* nsmPlayers.add(craftPlayer.getHandle());
|
||||
* }
|
||||
*
|
||||
* return nsmPlayers;
|
||||
* }
|
||||
*
|
||||
*/
|
||||
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
|
||||
try {
|
||||
//EntityTrackerEntry trackEntity = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
|
||||
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
|
||||
|
||||
if (trackedPlayersField == null) {
|
||||
// This one is fairly easy
|
||||
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
|
||||
}
|
||||
|
||||
// Phew, finally there.
|
||||
Collection<?> trackedPlayers = (Collection<?>) FieldUtils.readField(trackedPlayersField, trackerEntry, false);
|
||||
List<Object> nmsPlayers = unwrapBukkit(observers);
|
||||
|
||||
// trackEntity.trackedPlayers.clear();
|
||||
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.9
|
||||
if (scanPlayersMethod == null) {
|
||||
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
|
||||
}
|
||||
|
||||
//trackEntity.scanPlayers(server.players);
|
||||
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) {
|
||||
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 + (entity.isDead() ? " - entity is dead." : "."));
|
||||
}
|
||||
|
||||
if (trackedPlayersField == null) {
|
||||
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
|
||||
}
|
||||
|
||||
Collection<?> trackedPlayers = null;
|
||||
Object value = FieldUtils.readField(trackedPlayersField, trackerEntry, false);
|
||||
|
||||
if (value instanceof Collection) {
|
||||
trackedPlayers = (Collection<?>) value;
|
||||
} else if (value instanceof Map) { // PaperSpigot
|
||||
trackedPlayers = ((Map<?, ?>) value).keySet();
|
||||
} else { // Please, no more changes
|
||||
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Set or Map, but got " + value.getClass());
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entity tracker entry given a ID.
|
||||
* @param world - world server.
|
||||
* @param entityID - entity ID.
|
||||
* @return The entity tracker entry.
|
||||
* @throws FieldAccessException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
if (trackedEntitiesField == null) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Set<Class> ignoredTypes = new HashSet<Class>();
|
||||
|
||||
// Well, this is more difficult. But we're looking for a Minecraft object that is not
|
||||
// created by the constructor(s).
|
||||
for (Constructor<?> constructor : tracker.getClass().getConstructors()) {
|
||||
for (Class<?> type : constructor.getParameterTypes()) {
|
||||
ignoredTypes.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
// The Minecraft field that's NOT filled in by the constructor
|
||||
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
|
||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex(), ignoredTypes);
|
||||
}
|
||||
|
||||
// Read the entity hashmap
|
||||
Object trackedEntities = null;
|
||||
|
||||
try {
|
||||
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
|
||||
} 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 asssociated 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;
|
||||
}
|
||||
}
|
||||
-1309
File diff suppressed because it is too large.
Load diff
Loaded 100 of 463 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user