Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8ac54e237 | ||
|
|
089e07fe49 | ||
|
|
1d3b8f802c | ||
|
|
27283e48a2 | ||
|
|
d22584afbc | ||
|
|
249ab01425 | ||
|
|
851097de44 | ||
|
|
c095a51f7c | ||
|
|
5e62b7001e | ||
|
|
8cc4ba168a | ||
|
|
264a21d092 | ||
|
|
e2585db350 | ||
|
|
a2f7a52d31 | ||
|
|
d04121202d | ||
|
|
95517ade2c | ||
|
|
c9441de7e4 | ||
|
|
dc946cb845 | ||
|
|
5fa30d9adb | ||
|
|
a7043424ec | ||
|
|
e1b3d21732 | ||
|
|
8ffc056a7e | ||
|
|
7c274fb03d | ||
|
|
3b533b5138 | ||
|
|
5665510f7e | ||
|
|
47faa21ef4 | ||
|
|
575f457830 | ||
|
|
d5bf7909e8 | ||
|
|
fdf90b7931 | ||
|
|
e3305e4eae | ||
|
|
15fe8d394e | ||
|
|
082db38c40 | ||
|
|
be54dc41d1 | ||
|
|
487e79c043 | ||
|
|
ef6390a0b8 | ||
|
|
9d9fc65633 | ||
|
|
6eac61c912 | ||
|
|
d89362a271 | ||
|
|
0a407069fa | ||
|
|
661fdb1127 | ||
|
|
9e6c0f72a8 | ||
|
|
70ad2613f1 | ||
|
|
9052946c45 | ||
|
|
3ddc43bf7b | ||
|
|
cdb757eae5 | ||
|
|
50a3e03649 | ||
|
|
957284930e | ||
|
|
198a24371c | ||
|
|
bcc994b0dd | ||
|
|
f870bd5a32 | ||
|
|
a14abd6f4f | ||
|
|
2456c3cf17 | ||
|
|
9828e4ae1d | ||
|
|
030aa887c7 | ||
|
|
ba624a2b48 | ||
|
|
e7582cb1ec | ||
|
|
82bf11fcbc | ||
|
|
ac6e8b1709 | ||
|
|
00cc545795 | ||
|
|
c7ee816aba | ||
|
|
e7f85e67f6 | ||
|
|
d239d2f967 | ||
|
|
63970c6d82 | ||
|
|
4cffdd4f64 | ||
|
|
cb3f0291b7 | ||
|
|
edaffad045 | ||
|
|
172f89940a | ||
|
|
fb4bc397c6 | ||
|
|
e595344941 | ||
|
|
05dbc53fd0 | ||
|
|
0374da573b | ||
|
|
8e64fb8c21 | ||
|
|
0f55402171 | ||
|
|
acb7372eb7 | ||
|
|
057ab050ea | ||
|
|
a01997fe53 | ||
|
|
e4adbf4a1a | ||
|
|
261d68c58e | ||
|
|
38617d1f61 | ||
|
|
cc5bcb8a64 | ||
|
|
b6794f5597 | ||
|
|
18f77308f5 | ||
|
|
6bb473ea8d | ||
|
|
7984f6400c | ||
|
|
3498d093b0 | ||
|
|
a3db32758c | ||
|
|
45952d3759 | ||
|
|
c552b12696 | ||
|
|
0ae9d9dbbf | ||
|
|
8852e3b3a3 | ||
|
|
a496b2d3a8 | ||
|
|
6198dbfe70 | ||
|
|
a81d52a54f | ||
|
|
cf20e8ac5f | ||
|
|
4f8a248c6b | ||
|
|
57d58db35e | ||
|
|
c214ca02e8 | ||
|
|
6b8b0e1771 | ||
|
|
e6671513f2 | ||
|
|
4c7b50d6fb | ||
|
|
1c9229a2fc | ||
|
|
d11c18c024 | ||
|
|
dc4b5b4d35 | ||
|
|
517299d673 | ||
|
|
a8baa7af50 | ||
|
|
a1f847c905 | ||
|
|
f3ea43b1eb | ||
|
|
551b5d0654 | ||
|
|
9f39662df6 | ||
|
|
7ea2156f37 | ||
|
|
5032e7d07e | ||
|
|
c7c6ee9fbc | ||
|
|
4431ca63c2 | ||
|
|
1fa7211cf0 | ||
|
|
de576795dc | ||
|
|
f6286bcc25 | ||
|
|
8276b212e9 | ||
|
|
aabd9247a6 | ||
|
|
997c2ad6a1 | ||
|
|
233a2ba5df | ||
|
|
ed0d5a7cfb | ||
|
|
87f083460c | ||
|
|
2d6dedefb8 | ||
|
|
8adbf36a3a | ||
|
|
8c706b665c | ||
|
|
2e3677a592 | ||
|
|
3602c5087b | ||
|
|
5c84b3d8cc | ||
|
|
7defe7129f | ||
|
|
995dc135b8 | ||
|
|
3f64fb7d5f | ||
|
|
ac8f7a00ae | ||
|
|
6893f34289 | ||
|
|
0a69d62cbe | ||
|
|
5e0096aa2c | ||
|
|
7326205495 | ||
|
|
a8212db7f2 | ||
|
|
2905d77206 | ||
|
|
cb8ec2ce20 | ||
|
|
09f484f956 | ||
|
|
c48922eb3e | ||
|
|
b701a0ef67 | ||
|
|
eb50f4e907 | ||
|
|
6480ea34fc | ||
|
|
511941462f | ||
|
|
34cc783cac | ||
|
|
ecc79b4f49 | ||
|
|
76f10c463f | ||
|
|
41d5144b57 | ||
|
|
de196f2e4f | ||
|
|
0acc2f95fb | ||
|
|
d16eb9b27e | ||
|
|
8760b53a0a | ||
|
|
a40182186f | ||
|
|
ef2b173749 | ||
|
|
75c9284cda | ||
|
|
2abecba7bf | ||
|
|
fb6055cbfe | ||
|
|
639c9ea8c1 | ||
|
|
1872845d2b | ||
|
|
93755c6732 | ||
|
|
68973fd136 | ||
|
|
1a0bd37a01 | ||
|
|
3551779600 | ||
|
|
b8deddcd2c | ||
|
|
c49bf89ead | ||
|
|
4ff4fe2ab2 | ||
|
|
1b99e2cf57 | ||
|
|
55830b0dfb | ||
|
|
e61f56b2e0 | ||
|
|
95ea95be13 | ||
|
|
d72b7fc4df | ||
|
|
0992c74351 | ||
|
|
081c23ad8b | ||
|
|
258f0fc636 | ||
|
|
c4e3864fc0 | ||
|
|
405b6e8047 | ||
|
|
9847912128 | ||
|
|
d89a34cca3 | ||
|
|
6197138380 | ||
|
|
f5f4876182 | ||
|
|
293c1229ff | ||
|
|
ba60f10744 | ||
|
|
c0493de4df | ||
|
|
46b2f69b01 | ||
|
|
f926113c66 | ||
|
|
0e4df51613 | ||
|
|
c5f72dc728 | ||
|
|
cee5b1ee42 | ||
|
|
f1b9cd2e56 | ||
|
|
7807e6d9c4 | ||
|
|
4b354f806d | ||
|
|
41707ca867 | ||
|
|
5bca3a97ec | ||
|
|
3679aa618f | ||
|
|
e9d96f2daf | ||
|
|
f371419396 | ||
|
|
f32d3211ed | ||
|
|
45f99d4c31 | ||
|
|
71a9c90fe9 | ||
|
|
406500b0bd | ||
|
|
9257298e17 | ||
|
|
0376602894 | ||
|
|
95ceabb495 | ||
|
|
6c8ccb1b1e | ||
|
|
f351dd03e7 | ||
|
|
c1e77a49b6 | ||
|
|
9adf6040fa | ||
|
|
3e320618f9 | ||
|
|
6c1de57d32 | ||
|
|
7694d311d1 | ||
|
|
eee1fd941e | ||
|
|
9c5c181389 | ||
|
|
b4b85b1602 | ||
|
|
f6bb1b48f5 | ||
|
|
4ce846a899 | ||
|
|
ef08117f34 | ||
|
|
4b582a7279 | ||
|
|
ad53c1ed7f | ||
|
|
6e46d018a4 | ||
|
|
6beacf9804 | ||
|
|
e15c8d7257 | ||
|
|
3518804cbf | ||
|
|
101f86c8cd | ||
|
|
22c2435656 | ||
|
|
af53275ab9 | ||
|
|
eccbb567e8 | ||
|
|
bae3599388 | ||
|
|
88ab4e53a7 | ||
|
|
25fa2eabbb | ||
|
|
e12d8ec466 | ||
|
|
a7022bddf8 | ||
|
|
523c27237d | ||
|
|
147891f76b | ||
|
|
a5ba52ce05 | ||
|
|
ccb2a4756f | ||
|
|
f5a96b791d | ||
|
|
c628a8a23f | ||
|
|
80f63d79ef | ||
|
|
b225afe6ef | ||
|
|
4e1625b017 | ||
|
|
5c4867365e | ||
|
|
3549a823cb | ||
|
|
23a00d2555 | ||
|
|
57931e7231 | ||
|
|
9e19e0c8af | ||
|
|
74461609c1 | ||
|
|
7d3e6bc7cb | ||
|
|
175606f320 | ||
|
|
b226e94981 | ||
|
|
7d0687aa4a | ||
|
|
d152b8b9e1 | ||
|
|
1fbd561aec | ||
|
|
a8295c9de0 | ||
|
|
e894a3c733 | ||
|
|
a2f8f28010 | ||
|
|
704c876772 | ||
|
|
0233e2ab27 | ||
|
|
5ae8a35740 | ||
|
|
ed766eb656 | ||
|
|
b1b35fe025 | ||
|
|
9b6a9fca3d | ||
|
|
615128eba2 | ||
|
|
8f3b938e39 | ||
|
|
625a1d2191 | ||
|
|
8a35c0235e | ||
|
|
69e8ddcbf6 | ||
|
|
ce8a504750 | ||
|
|
11f3844d63 | ||
|
|
56d5f8eec6 | ||
|
|
7a98cdd28f | ||
|
|
d40c5851e9 | ||
|
|
ccda9f11c8 | ||
|
|
7e133a0be8 | ||
|
|
d0e573613b | ||
|
|
2197b938f8 | ||
|
|
0d3ba67233 | ||
|
|
9d57521b29 | ||
|
|
d98d4ff466 | ||
|
|
71e27b4799 | ||
|
|
f1354edf20 | ||
|
|
4d82112286 | ||
|
|
ba5a44d2d2 | ||
|
|
597ddc7f82 | ||
|
|
6a7bd209a3 | ||
|
|
722f28f086 | ||
|
|
daed15d61f | ||
|
|
c300c47a14 | ||
|
|
89427cb2da | ||
|
|
fce7f1740e | ||
|
|
0357d8e6aa | ||
|
|
55335944e3 | ||
|
|
c0b2985343 | ||
|
|
91ae5990b1 | ||
|
|
33f1a8975c | ||
|
|
7cfba0b602 | ||
|
|
9b28129187 | ||
|
|
78ab7c840a | ||
|
|
669ec3f453 | ||
|
|
dd32b46055 | ||
|
|
7daa43146e | ||
|
|
89d5f2cc4f | ||
|
|
53fb753284 | ||
|
|
81d89d3839 | ||
|
|
469d81df72 | ||
|
|
7d7f5a0bda | ||
|
|
9954195a8f | ||
|
|
62d210de68 | ||
|
|
e19a2920b9 | ||
|
|
8d2189cb8e | ||
|
|
44a044848f | ||
|
|
4f133366e1 | ||
|
|
7c66507960 | ||
|
|
e436988303 | ||
|
|
8b65efc4bd | ||
|
|
7cff20f218 | ||
|
|
131517105e | ||
|
|
ae2592f3d1 | ||
|
|
c009827622 | ||
|
|
afe18379e0 | ||
|
|
5ac61fa71d | ||
|
|
3511f9a7b2 | ||
|
|
e3f7493616 | ||
|
|
a769599448 | ||
|
|
5df8a1e8e7 | ||
|
|
a3d124b552 | ||
|
|
6d95af3963 | ||
|
|
c08a38ff21 | ||
|
|
4a804bae7c | ||
|
|
fd42c7eba6 | ||
|
|
1af86d1354 | ||
|
|
1b77e1c89f | ||
|
|
c42b9351b8 | ||
|
|
ab8c8cd7c8 | ||
|
|
83518b92fe | ||
|
|
1bf1e8981c | ||
|
|
b1f076936d | ||
|
|
319e793cd6 | ||
|
|
c9e1360951 | ||
|
|
c11ad7930f | ||
|
|
7b36a9de51 | ||
|
|
d5e9170109 | ||
|
|
47b447f195 | ||
|
|
e2d27df1a8 | ||
|
|
9a781d940f | ||
|
|
fed36c9487 | ||
|
|
463327c2a7 | ||
|
|
b0050d4a8d | ||
|
|
a743895c3f | ||
|
|
f65666b7a6 | ||
|
|
3a217503fc | ||
|
|
c71b75e13b | ||
|
|
0c890c3a30 | ||
|
|
f38bb0ca22 | ||
|
|
2144fa1837 | ||
|
|
fc4a617da7 | ||
|
|
126ee6e03f | ||
|
|
498867032f | ||
|
|
aef8eca790 | ||
|
|
d8bbb51721 | ||
|
|
6e4fd5a63b | ||
|
|
e7bd6245c2 | ||
|
|
6242cb0b47 | ||
|
|
bf5072e1cf | ||
|
|
c24b673550 | ||
|
|
dfc1e1cf51 | ||
|
|
c600ab6dd1 | ||
|
|
29754c0787 | ||
|
|
9144980474 | ||
|
|
6a449f8db1 | ||
|
|
3780210f76 | ||
|
|
67bad0dec8 | ||
|
|
79291fd7d9 | ||
|
|
ee9d8d6365 | ||
|
|
cc354db464 | ||
|
|
2b29d27b0d | ||
|
|
75db62ee6f | ||
|
|
7cd7b5ba24 | ||
|
|
a3bfcefdca | ||
|
|
098b787355 | ||
|
|
abb9665813 | ||
|
|
012eec47b5 | ||
|
|
2d40afa3a1 | ||
|
|
a92a7d6e02 | ||
|
|
fc7cdb4244 | ||
|
|
d5b375b264 | ||
|
|
99eadbe343 | ||
|
|
38d33dba16 | ||
|
|
b3de4de0cd | ||
|
|
6b5c166a22 | ||
|
|
1aea00036f | ||
|
|
1262847a0e | ||
|
|
21cfb1297f | ||
|
|
0b1cb61986 | ||
|
|
44907b3e8d | ||
|
|
dac616d72c | ||
|
|
528b234d5a | ||
|
|
e4288383f4 | ||
|
|
bb17eb01b8 | ||
|
|
ca6638c740 | ||
|
|
d8ce66ce29 | ||
|
|
8460c157a5 | ||
|
|
0b807569ad | ||
|
|
75d001d09b | ||
|
|
2118379cf7 | ||
|
|
cd2441ee52 | ||
|
|
ffb4fb5c5a | ||
|
|
c404f2206c | ||
|
|
aec5aa48da | ||
|
|
b63051a796 | ||
|
|
ea4650000e | ||
|
|
2d2018b8d0 | ||
|
|
736a853a85 | ||
|
|
dfd4c5f93e | ||
|
|
2ac87acc8a | ||
|
|
3c5a6a3b7b | ||
|
|
6efb5c1b64 | ||
|
|
91de7e5a22 | ||
|
|
1c42ae1ee8 | ||
|
|
c5ac3c9b3d | ||
|
|
0ea32b3318 | ||
|
|
8afc850ed3 | ||
|
|
3c6dcfe5bf | ||
|
|
59d637e0f5 | ||
|
|
923f3088c0 | ||
|
|
4f8aff9c01 | ||
|
|
99ceb9ae79 | ||
|
|
6e0554f4a4 | ||
|
|
36879f8d02 | ||
|
|
fe77df1434 | ||
|
|
713361f973 | ||
|
|
a314b86f57 | ||
|
|
840c90f871 | ||
|
|
2a247595c8 | ||
|
|
4124c3454e | ||
|
|
c4185e3056 | ||
|
|
d6403d495d | ||
|
|
d7f54048ff | ||
|
|
e487eeb395 | ||
|
|
0d0e1c16ea | ||
|
|
f462ff9e8a | ||
|
|
5582556d8a | ||
|
|
ef6214a442 | ||
|
|
369710333c | ||
|
|
30c4fea044 | ||
|
|
eccc5091b5 | ||
|
|
65770d7c5b | ||
|
|
715e873bae | ||
|
|
eced599818 | ||
|
|
6db7755633 | ||
|
|
653c3d3984 | ||
|
|
5ca841837d | ||
|
|
554e037044 | ||
|
|
457d7e4777 | ||
|
|
b3dc465498 | ||
|
|
c56109a64c | ||
|
|
d5e720fe09 | ||
|
|
a42526bb88 | ||
|
|
bd5f44b477 | ||
|
|
d5d8881677 | ||
|
|
ac2178a247 | ||
|
|
24fa8aec35 | ||
|
|
2ba92a0d47 | ||
|
|
ace178834e | ||
|
|
9c88d0a37f | ||
|
|
bafc1b6799 | ||
|
|
c9a575f45d | ||
|
|
c071c895e7 | ||
|
|
a8799bd493 | ||
|
|
20d4a94792 | ||
|
|
89e0948f97 | ||
|
|
6fe3c15f7d | ||
|
|
fb5dace6a6 | ||
|
|
f71e51c85e | ||
|
|
0dd530a593 | ||
|
|
12df86a142 | ||
|
|
963f021226 | ||
|
|
cb4a8b24d4 | ||
|
|
76c908b132 | ||
|
|
89311af76a | ||
|
|
1ecf515006 | ||
|
|
5c18028577 | ||
|
|
678a7de664 | ||
|
|
0aa190353e | ||
|
|
a42b113618 | ||
|
|
2704611977 | ||
|
|
be6245e693 | ||
|
|
d7b7c4916a | ||
|
|
caae4e4cca | ||
|
|
ee58fbedbe | ||
|
|
a84b45f5dd | ||
|
|
c2edf45846 | ||
|
|
88ff4ee9ca | ||
|
|
fd704f6a29 | ||
|
|
c97b443cf2 | ||
|
|
00df80e6fd | ||
|
|
64c2ba243f | ||
|
|
0ad2e6478d | ||
|
|
ab9f786999 | ||
|
|
e826b339f6 | ||
|
|
f06851f57a | ||
|
|
1a90f0eb28 | ||
|
|
fee986d215 | ||
|
|
9108e78491 | ||
|
|
1b75526892 | ||
|
|
5930a83765 | ||
|
|
ef118a3a64 | ||
|
|
bb42cda4a8 | ||
|
|
121bc9de6c | ||
|
|
1e873c123c | ||
|
|
c704b3709a | ||
|
|
618e09b202 | ||
|
|
fd0bd53826 | ||
|
|
e11c8320f0 | ||
|
|
624595e9f6 | ||
|
|
cc813545d8 | ||
|
|
e1ba6e07c2 | ||
|
|
1bf49b1f0b | ||
|
|
5e34fda7e4 | ||
|
|
aa70ab8b78 | ||
|
|
186ba22dc8 | ||
|
|
b10aa30d9b | ||
|
|
615b9f847c | ||
|
|
8f132fed14 | ||
|
|
854e24f8fc | ||
|
|
ff0544f29a | ||
|
|
72ae12bc77 | ||
|
|
29d2334ef0 | ||
|
|
afb96c4878 | ||
|
|
e4346611c9 | ||
|
|
f15e11272a | ||
|
|
8764d24a16 | ||
|
|
86d543d700 | ||
|
|
99fd01c359 | ||
|
|
573dcc10b1 | ||
|
|
3e30d989a0 | ||
|
|
fc00dd2b38 | ||
|
|
2e9987373d | ||
|
|
27c2f4c076 | ||
|
|
e9b61a8e91 | ||
|
|
222dee6fda | ||
|
|
e8cc25baad | ||
|
|
785f0664da | ||
|
|
ea6c648e4f | ||
|
|
4cdfd72700 | ||
|
|
ccd6987f11 | ||
|
|
6c96271925 | ||
|
|
318db73f3c | ||
|
|
b5b0117fa9 | ||
|
|
79e5555d42 | ||
|
|
b6c2a46a86 | ||
|
|
114a7b6bd6 | ||
|
|
8e6d76296d | ||
|
|
153a68bf06 | ||
|
|
6199e1d8ce | ||
|
|
0db43e612e | ||
|
|
06296ead77 | ||
|
|
dac9bd58e5 | ||
|
|
8e0833ca6b | ||
|
|
88086e00d0 | ||
|
|
9368347b56 | ||
|
|
6828425d72 | ||
|
|
f9da275358 | ||
|
|
1445bd37c3 | ||
|
|
2de176e55c | ||
|
|
b785fe0515 | ||
|
|
984176d53b | ||
|
|
bd46c3f5a0 | ||
|
|
0209c51d10 | ||
|
|
3425ed94e0 | ||
|
|
8ceee31299 | ||
|
|
db9cd296ee | ||
|
|
6b3fb6424c | ||
|
|
e43968c142 | ||
|
|
5c0ba593da | ||
|
|
1f51cf8416 | ||
|
|
442d4de74b | ||
|
|
2a67301642 | ||
|
|
46ec24a686 | ||
|
|
6f25090c01 | ||
|
|
dd36686f1c | ||
|
|
2b980a3cfc | ||
|
|
43aaa728b5 | ||
|
|
45dfe7261b | ||
|
|
7831dc0082 | ||
|
|
ba4e6e5d2f | ||
|
|
68bfaf6d23 | ||
|
|
954dfb74e5 | ||
|
|
f95e7f98b7 | ||
|
|
aabd79e6c7 | ||
|
|
db7daaa6ca | ||
|
|
b66ea090c2 | ||
|
|
ad3317f8d8 | ||
|
|
3b480a61bd | ||
|
|
c0296f04a1 | ||
|
|
6f605e8d6c | ||
|
|
a6cfcac675 | ||
|
|
8e717bb023 | ||
|
|
e885a1cec2 | ||
|
|
e2de17e221 | ||
|
|
5738a992e5 | ||
|
|
122a7e05f6 | ||
|
|
546fdf95d4 | ||
|
|
e6a9cc4776 | ||
|
|
c641f02d7e | ||
|
|
bf11edfaa9 | ||
|
|
3cc6bf4c51 | ||
|
|
156f42318c | ||
|
|
1a1ffe4265 | ||
|
|
3fa4b7a7b2 | ||
|
|
59b270beda | ||
|
|
135942c033 | ||
|
|
0930420844 | ||
|
|
16e546803c | ||
|
|
f6b297fea4 | ||
|
|
3f0b5c8a2b | ||
|
|
dff7d98dd5 |
No files matched your search
+115
-2
@@ -1,3 +1,116 @@
|
||||
target/
|
||||
.idea/
|
||||
### Java files ###
|
||||
*.class
|
||||
|
||||
# Package Files
|
||||
#*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
||||
# Ignore project files
|
||||
*.iml
|
||||
|
||||
# Ignore IDEA directory
|
||||
.idea/*
|
||||
|
||||
# Include the project's code style settings file
|
||||
!.idea/codeStyleSettings.xml
|
||||
|
||||
# Include the project's dictionary
|
||||
!.idea/dictionaries/
|
||||
!.idea/dictionaries/*
|
||||
|
||||
# File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
### Plugin-specific files: ###
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
|
||||
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
nbactions.xml
|
||||
nb-configuration.xml
|
||||
.nb-gradle/
|
||||
|
||||
|
||||
|
||||
### Git ###
|
||||
# Don't exclude the .gitignore itself
|
||||
!.gitignore
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
sudo: false
|
||||
|
||||
language: java
|
||||
jdk: oraclejdk7
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- '$HOME/.m2/repository'
|
||||
|
||||
script:
|
||||
- mvn dependency:purge-local-repository -DactTransitively=false -DreResolve=false
|
||||
- mvn clean install -B -U
|
||||
@@ -0,0 +1,12 @@
|
||||
### What version of ViaVersion are you using? Type /ver viaversion
|
||||
|
||||
### What version of Spigot are you using? Type /ver
|
||||
|
||||
### What plugins are you using? Type /plugins
|
||||
|
||||
### Are you using any additional software like BungeeCord? If so, what software and version? (Please list your plugins as well)
|
||||
|
||||
### How does this error happen? login? Using an item?
|
||||
|
||||
### Is there an error in the console? Use pastebin.com. Is there a kick message?
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
License:
|
||||
--------
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,71 @@
|
||||
# ViaVersion 0.9.2
|
||||
[](https://travis-ci.org/MylesIsCool/ViaVersion)
|
||||
[](https://gitter.im/MylesIsCool/ViaVersion)
|
||||
|
||||
## This is an experimental branch for 1.10, we do not recommend you to use this on a production server
|
||||
**Direct download link:** [Latest builds](https://ci.viaversion.com/job/ViaVersion%201.10%20DEV/)
|
||||
|
||||
**Allows the connection of 1.9 clients to 1.8**
|
||||
**1.10 clients on 1.8 / 1.9 servers**
|
||||
|
||||
This took 100s hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
||||
|
||||
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
|
||||
|
||||
|
||||
Sources:
|
||||
--------
|
||||
|
||||
**wiki.vg** (Protocol info, though I did have to find a lot myself)
|
||||
|
||||
**Burger** We use this tool to get protocol and other useful information for new / undocumented Minecraft versions
|
||||
|
||||
**OpenNBT** (used for slot rewriting)
|
||||
|
||||
|
||||
|
||||
Contributors:
|
||||
--------
|
||||
|
||||
**Myself** (harhar)
|
||||
|
||||
**Matsv/StamBoom**
|
||||
|
||||
**HugoDaBosss**
|
||||
|
||||
**SanderGielisse**
|
||||
|
||||
**Paulomart**
|
||||
|
||||
**gigosaurus**
|
||||
|
||||
**fillefilip8**
|
||||
|
||||
**lenis0012**
|
||||
|
||||
|
||||
|
||||
Releases / Dev Builds:
|
||||
--------
|
||||
You can find official releases here:
|
||||
|
||||
https://www.spigotmc.org/resources/viaversion.19254/history
|
||||
|
||||
|
||||
----------
|
||||
|
||||
You can find official dev builds here:
|
||||
|
||||
**Jenkins:** https://ci.viaversion.com
|
||||
|
||||
**JavaDocs:** https://jd.viaversion.com
|
||||
|
||||
**Maven Repo:** https://repo.viaversion.com
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
MIT License, see LICENSE for more details.
|
||||
|
||||
Thanks!
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
machine:
|
||||
java:
|
||||
version: oraclejdk7
|
||||
general:
|
||||
artifacts:
|
||||
- "target/*.jar"
|
||||
test:
|
||||
override:
|
||||
- mvn clean install -B
|
||||
post:
|
||||
- cp ./target/*.jar $CIRCLE_ARTIFACTS
|
||||
@@ -1,33 +1,223 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>ViaVersion</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<artifactId>viaversion</artifactId>
|
||||
<version>0.9.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ViaVersion</name>
|
||||
<description>Allow 1.8 clients to join 1.9 bukkit servers!</description>
|
||||
<inceptionYear>2016</inceptionYear>
|
||||
<url>https://www.spigotmc.org/resources/viaversion.19254/</url>
|
||||
|
||||
<prerequisites>
|
||||
<maven>3.3.3</maven>
|
||||
</prerequisites>
|
||||
|
||||
<properties>
|
||||
<!-- Project Properties -->
|
||||
<projectEncoding>UTF-8</projectEncoding>
|
||||
<project.build.sourceEncoding>${projectEncoding}</project.build.sourceEncoding>
|
||||
<project.build.outputEncoding>${projectEncoding}</project.build.outputEncoding>
|
||||
<jdkVersion>1.7</jdkVersion>
|
||||
<!-- <testJreVersion>1.7</testJreVersion> -->
|
||||
|
||||
<!-- Change Bukkit Version HERE! -->
|
||||
<bukkitVersion>1.8.8-R0.1-SNAPSHOT</bukkitVersion>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<finalName>${project.name}-${project.version}</finalName>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<!-- <testSourceDirectory>src/test/java</testSourceDirectory> -->
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>.</targetPath>
|
||||
<filtering>false</filtering>
|
||||
<directory>.</directory>
|
||||
<includes>
|
||||
<include>LICENSE</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<targetPath>.</targetPath>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources/</directory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<!--
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
-->
|
||||
|
||||
<plugins>
|
||||
<!-- Maven Java Compiler -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>${jdkVersion}</source>
|
||||
<target>${jdkVersion}</target>
|
||||
<!--
|
||||
<testSource>${testJreVersion}</testSource>
|
||||
<testTarget>${testJreVersion}</testTarget>
|
||||
-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Unit Test Plugin
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19.1</version>
|
||||
<configuration>
|
||||
<argLine>-Dfile.encoding=${projectEncoding} ${argLine}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
-->
|
||||
<!-- Libs Shading and Relocation -->
|
||||
<plugin>
|
||||
<!--Relocate all lib we use in order to fix class loading errors if we use different versions
|
||||
than already loaded libs (i.e. by Mojang -> gson)-->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.spacehq.opennbt</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.opennbt</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.gson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.javassist</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.javassist</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<!-- SpigotMC Repo (Bukkit API) -->
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
|
||||
<!-- SpaceHQ Repo-->
|
||||
<repository>
|
||||
<id>spacehq-repo</id>
|
||||
<url>https://repo.spacehq.org/content/repositories/releases/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Bungee repo -->
|
||||
<repository>
|
||||
<id>bungeecord-repo</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Bukkit API, http://www.spigotmc.org/ or http://bukkit.org/ -->
|
||||
<dependency>
|
||||
<!-- Sorry about this path >:( I promise to fix it when I stop using NMS :) -->
|
||||
<groupId>org.spigot</groupId>
|
||||
<artifactId>spigot1.8</artifactId>
|
||||
<version>1.8</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>E:/spigot_server.jar</systemPath>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>${bukkitVersion}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>gson</artifactId>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<groupId>javax.persistence</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Javassist (Bytecode Library) -->
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.20.0-GA</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- NBT Edit Library -->
|
||||
<dependency>
|
||||
<groupId>org.spacehq</groupId>
|
||||
<artifactId>opennbt</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- GSON (JSON Library) -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.6.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Netty (Network Library) -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.20.Final</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.16.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Bungeecord-->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.9-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.PacketUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockStorage {
|
||||
private int bitsPerEntry;
|
||||
|
||||
private List<Integer> states;
|
||||
private FlexibleStorage storage;
|
||||
|
||||
public BlockStorage() {
|
||||
this.bitsPerEntry = 4;
|
||||
|
||||
this.states = new ArrayList<Integer>();
|
||||
this.states.add(0);
|
||||
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, 4096);
|
||||
}
|
||||
|
||||
public BlockStorage(ByteBuf in) throws IOException {
|
||||
this.bitsPerEntry = in.readUnsignedByte();
|
||||
|
||||
this.states = new ArrayList<Integer>();
|
||||
int stateCount = PacketUtil.readVarInt(in);
|
||||
for (int i = 0; i < stateCount; i++) {
|
||||
this.states.add(PacketUtil.readVarInt(in));
|
||||
}
|
||||
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, PacketUtil.readLongs(PacketUtil.readVarInt(in), in));
|
||||
}
|
||||
|
||||
public void write(ByteBuf out) throws IOException {
|
||||
out.writeByte(this.bitsPerEntry);
|
||||
|
||||
PacketUtil.writeVarInt(this.states.size(), out);
|
||||
for (int state : this.states) {
|
||||
PacketUtil.writeVarInt(state, out);
|
||||
}
|
||||
|
||||
long[] data = this.storage.getData();
|
||||
PacketUtil.writeVarInt(data.length, out);
|
||||
PacketUtil.writeLongs(data, out);
|
||||
}
|
||||
|
||||
public int getBitsPerEntry() {
|
||||
return this.bitsPerEntry;
|
||||
}
|
||||
|
||||
public List<Integer> getStates() {
|
||||
return Collections.unmodifiableList(this.states);
|
||||
}
|
||||
|
||||
public FlexibleStorage getStorage() {
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
public int get(int x, int y, int z) {
|
||||
int id = this.storage.get(index(x, y, z));
|
||||
return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.states.size() ? this.states.get(id) : 0) : id;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int state) {
|
||||
set(index(x, y, z), state);
|
||||
}
|
||||
|
||||
public void set(int ind, int state) {
|
||||
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
|
||||
if (id == -1) {
|
||||
this.states.add(state);
|
||||
if (this.states.size() > 1 << this.bitsPerEntry) {
|
||||
this.bitsPerEntry++;
|
||||
|
||||
List<Integer> oldStates = this.states;
|
||||
if (this.bitsPerEntry > 8) {
|
||||
oldStates = new ArrayList<Integer>(this.states);
|
||||
this.states.clear();
|
||||
this.bitsPerEntry = 13;
|
||||
}
|
||||
|
||||
FlexibleStorage oldStorage = this.storage;
|
||||
this.storage = new FlexibleStorage(this.bitsPerEntry, this.storage.getSize());
|
||||
for (int index = 0; index < this.storage.getSize(); index++) {
|
||||
int value = oldStorage.get(index);
|
||||
this.storage.set(index, this.bitsPerEntry <= 8 ? value : oldStates.get(value));
|
||||
}
|
||||
}
|
||||
|
||||
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
|
||||
}
|
||||
|
||||
this.storage.set(ind, id);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int index = 0; index < this.storage.getSize(); index++) {
|
||||
if (this.storage.get(index) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int index(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof BlockStorage && this.bitsPerEntry == ((BlockStorage) o).bitsPerEntry && this.states.equals(((BlockStorage) o).states) && this.storage.equals(((BlockStorage) o).storage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.bitsPerEntry;
|
||||
result = 31 * result + this.states.hashCode();
|
||||
result = 31 * result + this.storage.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
public class Chunk {
|
||||
private BlockStorage blocks;
|
||||
private NibbleArray3d blocklight;
|
||||
private NibbleArray3d skylight;
|
||||
|
||||
public Chunk(boolean skylight) {
|
||||
this(new BlockStorage(), new NibbleArray3d(2048), skylight ? new NibbleArray3d(2048) : null);
|
||||
}
|
||||
|
||||
public Chunk(BlockStorage blocks, NibbleArray3d blocklight, NibbleArray3d skylight) {
|
||||
this.blocks = blocks;
|
||||
this.blocklight = blocklight;
|
||||
this.skylight = skylight;
|
||||
}
|
||||
|
||||
public BlockStorage getBlocks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
public NibbleArray3d getBlockLight() {
|
||||
return this.blocklight;
|
||||
}
|
||||
|
||||
public NibbleArray3d getSkyLight() {
|
||||
return this.skylight;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.blocks.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof Chunk && this.blocks.equals(((Chunk) o).blocks) && this.blocklight.equals(((Chunk) o).blocklight) && ((this.skylight == null && (((Chunk) o).skylight == null)) || (this.skylight != null && this.skylight.equals(((Chunk) o).skylight))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.blocks.hashCode();
|
||||
result = 31 * result + this.blocklight.hashCode();
|
||||
result = 31 * result + (this.skylight != null ? this.skylight.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
public class Column {
|
||||
private int x;
|
||||
private int z;
|
||||
private Chunk chunks[];
|
||||
private byte biomeData[];
|
||||
|
||||
private boolean skylight;
|
||||
|
||||
public Column(int x, int z, Chunk chunks[]) {
|
||||
this(x, z, chunks, null);
|
||||
}
|
||||
|
||||
public Column(int x, int z, Chunk chunks[], byte biomeData[]) {
|
||||
if(chunks.length != 16) {
|
||||
throw new IllegalArgumentException("Chunk array length must be 16.");
|
||||
}
|
||||
|
||||
if(biomeData != null && biomeData.length != 256) {
|
||||
throw new IllegalArgumentException("Biome data array length must be 256.");
|
||||
}
|
||||
|
||||
this.skylight = false;
|
||||
boolean noSkylight = false;
|
||||
for(int index = 0; index < chunks.length; index++) {
|
||||
if(chunks[index] != null) {
|
||||
if(chunks[index].getSkyLight() == null) {
|
||||
noSkylight = true;
|
||||
} else {
|
||||
this.skylight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(noSkylight && this.skylight) {
|
||||
throw new IllegalArgumentException("Either all chunks must have skylight values or none must have them.");
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.chunks = chunks;
|
||||
this.biomeData = biomeData;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public Chunk[] getChunks() {
|
||||
return this.chunks;
|
||||
}
|
||||
|
||||
public boolean hasBiomeData() {
|
||||
return this.biomeData != null;
|
||||
}
|
||||
|
||||
public byte[] getBiomeData() {
|
||||
return this.biomeData;
|
||||
}
|
||||
|
||||
public boolean hasSkylight() {
|
||||
return this.skylight;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FlexibleStorage {
|
||||
private final long[] data;
|
||||
private final int bitsPerEntry;
|
||||
private final int size;
|
||||
private final long maxEntryValue;
|
||||
|
||||
public FlexibleStorage(int bitsPerEntry, int size) {
|
||||
this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]);
|
||||
}
|
||||
|
||||
public FlexibleStorage(int bitsPerEntry, long[] data) {
|
||||
if(bitsPerEntry < 1 || bitsPerEntry > 32) {
|
||||
throw new IllegalArgumentException("BitsPerEntry cannot be outside of accepted range.");
|
||||
}
|
||||
|
||||
this.bitsPerEntry = bitsPerEntry;
|
||||
this.data = data;
|
||||
|
||||
this.size = this.data.length * 64 / this.bitsPerEntry;
|
||||
this.maxEntryValue = (1L << this.bitsPerEntry) - 1;
|
||||
}
|
||||
|
||||
public long[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getBitsPerEntry() {
|
||||
return this.bitsPerEntry;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public int get(int index) {
|
||||
if(index < 0 || index > this.size - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int bitIndex = index * this.bitsPerEntry;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
if(startIndex == endIndex) {
|
||||
return (int) (this.data[startIndex] >>> startBitSubIndex & this.maxEntryValue);
|
||||
} else {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
return (int) ((this.data[startIndex] >>> startBitSubIndex | this.data[endIndex] << endBitSubIndex) & this.maxEntryValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int index, int value) {
|
||||
if(index < 0 || index > this.size - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if(value < 0 || value > this.maxEntryValue) {
|
||||
throw new IllegalArgumentException("Value cannot be outside of accepted range.");
|
||||
}
|
||||
|
||||
int bitIndex = index * this.bitsPerEntry;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
this.data[startIndex] = this.data[startIndex] & ~(this.maxEntryValue << startBitSubIndex) | ((long) value & this.maxEntryValue) << startBitSubIndex;
|
||||
if(startIndex != endIndex) {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
this.data[endIndex] = this.data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & this.maxEntryValue) >> endBitSubIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static int roundToNearest(int value, int roundTo) {
|
||||
if(roundTo == 0) {
|
||||
return 0;
|
||||
} else if(value == 0) {
|
||||
return roundTo;
|
||||
} else {
|
||||
if(value < 0) {
|
||||
roundTo *= -1;
|
||||
}
|
||||
|
||||
int remainder = value % roundTo;
|
||||
return remainder != 0 ? value + roundTo - remainder : value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof FlexibleStorage && Arrays.equals(this.data, ((FlexibleStorage) o).data) && this.bitsPerEntry == ((FlexibleStorage) o).bitsPerEntry && this.size == ((FlexibleStorage) o).size && this.maxEntryValue == ((FlexibleStorage) o).maxEntryValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(this.data);
|
||||
result = 31 * result + this.bitsPerEntry;
|
||||
result = 31 * result + this.size;
|
||||
result = 31 * result + (int) this.maxEntryValue;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.spacehq.mc.protocol.data.game.chunk;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NibbleArray3d {
|
||||
private byte[] data;
|
||||
|
||||
public NibbleArray3d(int size) {
|
||||
this.data = new byte[size];
|
||||
}
|
||||
|
||||
public NibbleArray3d(byte[] array) {
|
||||
this.data = array;
|
||||
}
|
||||
|
||||
public NibbleArray3d(ByteBuf in, int size) throws IOException {
|
||||
this.data = new byte[size];
|
||||
in.readBytes(this.data);
|
||||
}
|
||||
|
||||
public void write(ByteBuf out) throws IOException {
|
||||
out.writeBytes(this.data);
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int get(int x, int y, int z) {
|
||||
int key = y << 8 | z << 4 | x;
|
||||
int index = key >> 1;
|
||||
int part = key & 1;
|
||||
return part == 0 ? this.data[index] & 15 : this.data[index] >> 4 & 15;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z, int val) {
|
||||
int key = y << 8 | z << 4 | x;
|
||||
int index = key >> 1;
|
||||
int part = key & 1;
|
||||
if(part == 0) {
|
||||
this.data[index] = (byte) (this.data[index] & 240 | val & 15);
|
||||
} else {
|
||||
this.data[index] = (byte) (this.data[index] & 15 | (val & 15) << 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void fill(int val) {
|
||||
for(int index = 0; index < this.data.length << 1; index++) {
|
||||
int ind = index >> 1;
|
||||
int part = index & 1;
|
||||
if(part == 0) {
|
||||
this.data[ind] = (byte) (this.data[ind] & 240 | val & 15);
|
||||
} else {
|
||||
this.data[ind] = (byte) (this.data[ind] & 15 | (val & 15) << 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || (o instanceof NibbleArray3d && Arrays.equals(this.data, ((NibbleArray3d) o).data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(this.data);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package org.spacehq.mc.protocol.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Chunk;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.Column;
|
||||
import org.spacehq.mc.protocol.data.game.chunk.NibbleArray3d;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/* From https://github.com/Steveice10/MCProtocolLib/ */
|
||||
/* No credit taken for writing this code, and used accordance to it's license
|
||||
Original by Steveice10, modified to suit this plugin.
|
||||
*/
|
||||
public class NetUtil {
|
||||
public static int writeNewColumn(ByteBuf out, Column column, boolean fullChunk, boolean hasSkylight) throws IOException {
|
||||
int mask = 0;
|
||||
Chunk chunks[] = column.getChunks();
|
||||
for(int index = 0; index < chunks.length; index++) {
|
||||
Chunk chunk = chunks[index];
|
||||
if(chunk != null && (!fullChunk || !chunk.isEmpty())) {
|
||||
mask |= 1 << index;
|
||||
chunk.getBlocks().write(out);
|
||||
chunk.getBlockLight().write(out);
|
||||
if(hasSkylight || column.hasSkylight()) {
|
||||
chunk.getSkyLight().write(out); // TODO: Make a PR to original lib to correct this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fullChunk) {
|
||||
out.writeBytes(column.getBiomeData());
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
|
||||
int pos = 0;
|
||||
int expected = 0;
|
||||
boolean sky = false;
|
||||
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
// 0 = Calculate expected length and determine if the packet has skylight.
|
||||
// 1 = Create chunks from mask and get blocks.
|
||||
// 2 = Get block light.
|
||||
// 3 = Get sky light.
|
||||
Chunk[] chunks = new Chunk[16];
|
||||
|
||||
for(int pass = 0; pass < 4; pass++) {
|
||||
for(int ind = 0; ind < 16; ind++) {
|
||||
if((bitmask & 1 << ind) != 0) {
|
||||
if(pass == 0) {
|
||||
// Block length + Blocklight length
|
||||
expected += (4096 * 2) + 2048;
|
||||
}
|
||||
|
||||
if(pass == 1) {
|
||||
chunks[ind] = new Chunk(sky || hasSkyLight);
|
||||
|
||||
buf.position(pos / 2);
|
||||
short[] tempData = new short[4096];
|
||||
buf.get(tempData, 0, tempData.length);
|
||||
// convert short array to new one
|
||||
int index = 0;
|
||||
for(short ss:tempData){
|
||||
// s is 16 bits, 12 bits id and 4 bits data
|
||||
int data = ss & 0xF;
|
||||
int id = (ss >> 4) << 4 | data;
|
||||
|
||||
int newCombined = id ; // test
|
||||
|
||||
chunks[ind].getBlocks().set(index, newCombined);
|
||||
index++;
|
||||
}
|
||||
pos += tempData.length * 2;
|
||||
|
||||
}
|
||||
|
||||
if(pass == 2) {
|
||||
NibbleArray3d blocklight = chunks[ind].getBlockLight();
|
||||
System.arraycopy(input, pos, blocklight.getData(), 0, blocklight.getData().length);
|
||||
pos += blocklight.getData().length;
|
||||
}
|
||||
|
||||
if(pass == 3) {
|
||||
if(chunks[ind].getSkyLight() != null) {
|
||||
NibbleArray3d skylight = chunks[ind].getSkyLight();
|
||||
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
|
||||
pos += skylight.getData().length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pass == 0) {
|
||||
// If we have more data than blocks and blocklight combined, there must be skylight data as well.
|
||||
if(input.length > expected) {
|
||||
sky = checkForSky;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte biomeData[] = null;
|
||||
if(isFullChunk) {
|
||||
biomeData = new byte[256];
|
||||
System.arraycopy(input, pos, biomeData, 0, biomeData.length);
|
||||
}
|
||||
|
||||
Column column = new Column(x, z, chunks, biomeData);
|
||||
return column;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.RED;
|
||||
|
||||
public class Bungee extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().severe(RED + "===============================================");
|
||||
getLogger().severe(RED + "ViaVersion is NOT a Bungeecord plugin");
|
||||
getLogger().severe(RED + "Install this plugin on all your spigot/bukkit");
|
||||
getLogger().severe(RED + "servers and use the latest Bungeecord version");
|
||||
getLogger().severe(RED + "to make ViaVersion work with BungeeCord.");
|
||||
getLogger().severe(RED + "===============================================");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
public class CancelException extends Exception{
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ConnectionInfo {
|
||||
private int protocol = 0;
|
||||
private State state = State.HANDSHAKE;
|
||||
private int compression = 0;
|
||||
private Object lastPacket;
|
||||
private java.util.UUID UUID;
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(int protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setCompression(int compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public int getCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
public void setLastPacket(Object lastPacket) {
|
||||
this.lastPacket = lastPacket;
|
||||
}
|
||||
|
||||
public Object getLastPacket() {
|
||||
return lastPacket;
|
||||
}
|
||||
|
||||
public void setUUID(UUID UUID) {
|
||||
this.UUID = UUID;
|
||||
}
|
||||
|
||||
public java.util.UUID getUUID() {
|
||||
return UUID;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return UUID == null ? null : Bukkit.getPlayer(UUID);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.minecraft.server.v1_8_R3.Entity;
|
||||
import net.minecraft.server.v1_8_R3.MinecraftServer;
|
||||
import net.minecraft.server.v1_8_R3.ServerConnection;
|
||||
import net.minecraft.server.v1_8_R3.WorldServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Core extends JavaPlugin {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
/* Obvious message here:
|
||||
If loading this plugin nobody will be on 1.9 cause only 1.8 so we're fine, as for reloading ugh.
|
||||
Clients might crash cause of it being a bum maybe? :P
|
||||
*/
|
||||
try {
|
||||
injectPacketHandler();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Unable to inject handlers, this version only supports 1.8.");
|
||||
}
|
||||
}
|
||||
|
||||
public void injectPacketHandler() throws NoSuchFieldException, IllegalAccessException {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
ServerConnection connection = server.getServerConnection();
|
||||
|
||||
List<ChannelFuture> futures = getPrivateField(connection, "g", List.class);
|
||||
|
||||
for (ChannelFuture future : futures) {
|
||||
ChannelPipeline pipeline = future.channel().pipeline();
|
||||
ChannelHandler bootstrapAcceptor = pipeline.first();
|
||||
ChannelInitializer<SocketChannel> oldInit = getPrivateField(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
setPrivateField(bootstrapAcceptor, "childHandler", newInit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Entity getEntity(final UUID player, final int id) {
|
||||
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<Entity>() {
|
||||
@Override
|
||||
public Entity call() throws Exception {
|
||||
Player p = Bukkit.getPlayer(player);
|
||||
if (p == null) return null;
|
||||
WorldServer ws = ((CraftWorld) p.getWorld()).getHandle();
|
||||
for (Entity e : ws.entityList) {
|
||||
if (e.getId() == id) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
System.out.println("Couldn't find in the world!!");
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error fetching entity ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T> T getPrivateField(Object o, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(o);
|
||||
}
|
||||
|
||||
public static void setPrivateField(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field field = o.getClass().getDeclaredField(f);
|
||||
field.setAccessible(true);
|
||||
field.set(o, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable<ItemStack>() {
|
||||
@Override
|
||||
public ItemStack call() throws Exception {
|
||||
if (info.getPlayer() != null) {
|
||||
return info.getPlayer().getItemInHand();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error fetching hand item ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.chunks.MagicBitSet;
|
||||
import us.myles.ViaVersion.chunks.PacketChunk;
|
||||
import us.myles.ViaVersion.chunks.PacketChunkData;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PacketUtil {
|
||||
private static Method DECODE_METHOD;
|
||||
private static Method ENCODE_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
DECODE_METHOD = ByteToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
|
||||
DECODE_METHOD.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Netty issue?");
|
||||
}
|
||||
try {
|
||||
ENCODE_METHOD = MessageToByteEncoder.class.getDeclaredMethod("encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
||||
ENCODE_METHOD.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Netty issue?");
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress");
|
||||
List<Object> output = new ArrayList<Object>();
|
||||
try {
|
||||
PacketUtil.DECODE_METHOD.invoke(x, ctx, msg, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return output.size() == 0 ? null : (ByteBuf) output.get(0);
|
||||
}
|
||||
|
||||
public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress");
|
||||
ByteBuf output = ctx.alloc().buffer();
|
||||
try {
|
||||
PacketUtil.ENCODE_METHOD.invoke(x, ctx, msg, output);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/* I take no credit, these are taken from BungeeCord */
|
||||
// https://github.com/SpigotMC/BungeeCord/blob/master/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java
|
||||
public static void writeString(String s, ByteBuf buf) {
|
||||
Preconditions.checkArgument(s.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", s.length());
|
||||
|
||||
byte[] b = s.getBytes(Charsets.UTF_8);
|
||||
writeVarInt(b.length, buf);
|
||||
buf.writeBytes(b);
|
||||
}
|
||||
|
||||
public static String readString(ByteBuf buf) {
|
||||
int len = readVarInt(buf);
|
||||
Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len);
|
||||
|
||||
byte[] b = new byte[len];
|
||||
buf.readBytes(b);
|
||||
|
||||
return new String(b, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
public static void writeArrayLegacy(byte[] b, ByteBuf buf, boolean allowExtended) {
|
||||
// (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit
|
||||
if (allowExtended) {
|
||||
Preconditions.checkArgument(b.length <= (Integer.MAX_VALUE & 0x1FFF9A), "Cannot send array longer than 2097050 (got %s bytes)", b.length);
|
||||
} else {
|
||||
Preconditions.checkArgument(b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length);
|
||||
}
|
||||
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only)
|
||||
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour.
|
||||
writeVarShort(buf, b.length);
|
||||
buf.writeBytes(b);
|
||||
}
|
||||
|
||||
public static byte[] readArrayLegacy(ByteBuf buf) {
|
||||
// Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only)
|
||||
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour.
|
||||
int len = readVarShort(buf);
|
||||
|
||||
// (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit
|
||||
Preconditions.checkArgument(len <= (Integer.MAX_VALUE & 0x1FFF9A), "Cannot receive array longer than 2097050 (got %s bytes)", len);
|
||||
|
||||
byte[] ret = new byte[len];
|
||||
buf.readBytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void writeArray(byte[] b, ByteBuf buf) {
|
||||
writeVarInt(b.length, buf);
|
||||
buf.writeBytes(b);
|
||||
}
|
||||
|
||||
public static byte[] readArray(ByteBuf buf) {
|
||||
byte[] ret = new byte[readVarInt(buf)];
|
||||
buf.readBytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void writeStringArray(List<String> s, ByteBuf buf) {
|
||||
writeVarInt(s.size(), buf);
|
||||
for (String str : s) {
|
||||
writeString(str, buf);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> readStringArray(ByteBuf buf) {
|
||||
int len = readVarInt(buf);
|
||||
List<String> ret = new ArrayList<String>(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
ret.add(readString(buf));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input) {
|
||||
return readVarInt(input, 5);
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input, int maxBytes) {
|
||||
int out = 0;
|
||||
int bytes = 0;
|
||||
byte in;
|
||||
while (true) {
|
||||
in = input.readByte();
|
||||
|
||||
out |= (in & 0x7F) << (bytes++ * 7);
|
||||
|
||||
if (bytes > maxBytes) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
|
||||
if ((in & 0x80) != 0x80) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void writeVarInt(int value, ByteBuf output) {
|
||||
int part;
|
||||
while (true) {
|
||||
part = value & 0x7F;
|
||||
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
part |= 0x80;
|
||||
}
|
||||
|
||||
output.writeByte(part);
|
||||
|
||||
if (value == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int readVarShort(ByteBuf buf) {
|
||||
int low = buf.readUnsignedShort();
|
||||
int high = 0;
|
||||
if ((low & 0x8000) != 0) {
|
||||
low = low & 0x7FFF;
|
||||
high = buf.readUnsignedByte();
|
||||
}
|
||||
return ((high & 0xFF) << 15) | low;
|
||||
}
|
||||
|
||||
public static void writeVarShort(ByteBuf buf, int toWrite) {
|
||||
int low = toWrite & 0x7FFF;
|
||||
int high = (toWrite & 0x7F8000) >> 15;
|
||||
if (high != 0) {
|
||||
low = low | 0x8000;
|
||||
}
|
||||
buf.writeShort(low);
|
||||
if (high != 0) {
|
||||
buf.writeByte(high);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeUUID(UUID value, ByteBuf output) {
|
||||
output.writeLong(value.getMostSignificantBits());
|
||||
output.writeLong(value.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
public static UUID readUUID(ByteBuf input) {
|
||||
return new UUID(input.readLong(), input.readLong());
|
||||
}
|
||||
|
||||
public static void writeLongs(long[] data, ByteBuf output) {
|
||||
for (int index = 0; index < data.length; index++) {
|
||||
output.writeLong(data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public static long[] readLongs(int amount, ByteBuf output) {
|
||||
long data[] = new long[amount];
|
||||
for (int index = 0; index < amount; index++) {
|
||||
data[index] = output.readLong();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// This method is based on one from here
|
||||
// https://github.com/Steveice10/MCProtocolLib/blob/master/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java
|
||||
public static PacketChunk readChunkData(boolean isFullChunk, int bitmask, byte[] input) {
|
||||
PacketChunkData[] chunks = new PacketChunkData[16];
|
||||
boolean sky = false;
|
||||
int expected = 0;
|
||||
int position = 0;
|
||||
ShortBuffer blockData = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
expected += (4096 * 2) + 2048;
|
||||
}
|
||||
}
|
||||
// If there is more data, then there must be skylights :D
|
||||
if (input.length > expected) {
|
||||
sky = true;
|
||||
}
|
||||
|
||||
// Read block data
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
chunks[i] = new PacketChunkData(sky);
|
||||
blockData.position(position / 2);
|
||||
blockData.get(chunks[i].getBlocks(), 0, 4096);
|
||||
position = position + (4096 * 2);
|
||||
} else {
|
||||
chunks[i] = new PacketChunkData(sky);
|
||||
}
|
||||
}
|
||||
// Read blocklight data
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
System.arraycopy(input, position, chunks[i].getBlockLight(), 0, 2048);
|
||||
position = position + 2048;
|
||||
}
|
||||
}
|
||||
// Read skylight data
|
||||
if (sky) {
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
System.arraycopy(input, position, chunks[i].getSkyLight(), 0, 2048);
|
||||
position = position + 2048;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] biomeData = null;
|
||||
|
||||
if (isFullChunk) {
|
||||
// yay im a full chunk meaning i know my biomes, mommy get the camera!
|
||||
biomeData = new byte[256];
|
||||
System.arraycopy(input, position, biomeData, 0, 256);
|
||||
}
|
||||
return new PacketChunk(chunks, biomeData);
|
||||
}
|
||||
|
||||
public static void writeNewChunk(ByteBuf buffer, PacketChunkData chunk) {
|
||||
// Bits Per Block (We use 0, cause we're not gonna write a palette ;) )
|
||||
buffer.writeByte(0);
|
||||
// No Palette nor length :D
|
||||
|
||||
// Data Array Length
|
||||
byte[] blockData = convertBlockArray(chunk.getBlocks());
|
||||
writeVarInt(blockData.length / 8, buffer); // Notchian is divide by 8
|
||||
|
||||
buffer.writeBytes(blockData);
|
||||
// Block Light
|
||||
buffer.writeBytes(chunk.getBlockLight());
|
||||
// If has skylight, write it
|
||||
if (chunk.getSkyLight() != null) {
|
||||
buffer.writeBytes(chunk.getSkyLight());
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] convertBlockArray(short[] blocks) {
|
||||
// block ID for the first 9 bits, and the block damage value for the last 4 bits
|
||||
byte[] output = new byte[6664]; // (16 * 16 * 16 * 13) / 8 :) (plus some for padding ...)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
for (short block : blocks) {
|
||||
int blockID = block >> 4; // (Needs to be to 9 bits)
|
||||
int data = block & 0xF; // 8 bits
|
||||
|
||||
}
|
||||
return null; // todo: finish
|
||||
}
|
||||
|
||||
private static BitSet append(BitSet base, int index, MagicBitSet toAdd) {
|
||||
int length = index;
|
||||
for (int i = 0; i < toAdd.getTrueLength(); i++) {
|
||||
base.set(length + i, toAdd.get(i));
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private static MagicBitSet getPaddedBitSet(int value, int bitSize) {
|
||||
MagicBitSet output = new MagicBitSet(bitSize);
|
||||
BitSet temp = BitSet.valueOf(new long[]{value});
|
||||
for (int i = 0; i < bitSize; i++) {
|
||||
output.set(i, false);
|
||||
}
|
||||
int toShift = bitSize - temp.length();
|
||||
for (int i = 0; i < temp.length(); i++) {
|
||||
output.set(toShift + i, temp.get(i));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import us.myles.ViaVersion.chunks.ByteWriter;
|
||||
|
||||
public class Testing {
|
||||
public static void main(String[] args) {
|
||||
// Map<NewType, Set<Type>> conversions = new HashMap<>();
|
||||
// for (MetaIndex metaIndex : MetaIndex.values()) {
|
||||
// if (!conversions.containsKey(metaIndex.getNewType())) {
|
||||
// conversions.put(metaIndex.getNewType(), new HashSet<Type>());
|
||||
// }
|
||||
// conversions.get(metaIndex.getNewType()).add(metaIndex.getOldType());
|
||||
// }
|
||||
// for (Map.Entry<NewType, Set<Type>> conv : conversions.entrySet()) {
|
||||
// System.out.println("Type: " + conv.getKey().name());
|
||||
// for (Type t : conv.getValue()) {
|
||||
// System.out.println(" -> " + t.name());
|
||||
// }
|
||||
// }
|
||||
ByteWriter bytes = new ByteWriter(2);
|
||||
int id = 255;
|
||||
int data = 4;
|
||||
bytes.writeByte(0, 1);
|
||||
bytes.writeFullByte(id);
|
||||
bytes.writeByte(data, 4);
|
||||
|
||||
System.out.println("output bytes: ");
|
||||
for(byte b:bytes.getOutput()){
|
||||
System.out.println("B: " + b + " I: " + ((int)b & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static byte writeByte(byte a, byte b, int index){
|
||||
int intA = (int) a & 0xff;
|
||||
int intB = (int) b & 0xff;
|
||||
System.out.println("As int: " + (intA | (intB >> index)));
|
||||
return (byte) (intA | (intB >> index));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import us.myles.ViaVersion.api.ViaVersionConfig;
|
||||
import us.myles.ViaVersion.util.Configuration;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ViaConfig implements ViaVersionConfig {
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public ViaConfig(ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
generateConfig();
|
||||
}
|
||||
|
||||
public void generateConfig() {
|
||||
File file = new File(plugin.getDataFolder(), "config.yml");
|
||||
if (file.exists()) {
|
||||
// Update conf options
|
||||
Configuration oldConfig = new Configuration(file);
|
||||
oldConfig.reload(false); // Load current options from conf
|
||||
file.delete(); // Delete old conf
|
||||
plugin.saveDefaultConfig(); // Generate new conf
|
||||
Configuration newConfig = new Configuration(file);
|
||||
newConfig.reload(true); // Load default options
|
||||
for (String key : oldConfig.getKeys(false)) {
|
||||
// Set option in new conf if exists
|
||||
if (newConfig.contains(key)) {
|
||||
newConfig.set(key, oldConfig.get(key));
|
||||
}
|
||||
}
|
||||
newConfig.save();
|
||||
} else {
|
||||
plugin.saveDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckForUpdates() {
|
||||
return plugin.getConfig().getBoolean("checkforupdates", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreventCollision() {
|
||||
return plugin.getConfig().getBoolean("prevent-collision", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewEffectIndicator() {
|
||||
return plugin.getConfig().getBoolean("use-new-effect-indicator", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowNewDeathMessages() {
|
||||
return plugin.getConfig().getBoolean("use-new-deathmessages", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressMetadataErrors() {
|
||||
return plugin.getConfig().getBoolean("suppress-metadata-errors", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShieldBlocking() {
|
||||
return plugin.getConfig().getBoolean("shield-blocking", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHologramPatch() {
|
||||
return plugin.getConfig().getBoolean("hologram-patch", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBossbarPatch() {
|
||||
return plugin.getConfig().getBoolean("bossbar-patch", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBossbarAntiflicker() {
|
||||
return plugin.getConfig().getBoolean("bossbar-anti-flicker", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnknownEntitiesSuppressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHologramYOffset() {
|
||||
return plugin.getConfig().getDouble("hologram-y", -1D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlockBreakPatch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPPS() {
|
||||
return plugin.getConfig().getInt("max-pps", 140);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaxPPSKickMessage() {
|
||||
return plugin.getConfig().getString("max-pps-kick-msg", "Sending packets too fast? lag?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrackingPeriod() {
|
||||
return plugin.getConfig().getInt("tracking-period", 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWarningPPS() {
|
||||
return plugin.getConfig().getInt("tracking-warning-pps", 120);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxWarnings() {
|
||||
return plugin.getConfig().getInt("tracking-max-warnings", 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaxWarningsKickMessage() {
|
||||
return plugin.getConfig().getString("tracking-max-kick-msg", "You are sending too many packets, :(");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAntiXRay() {
|
||||
return plugin.getConfig().getBoolean("anti-xray-patch", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSendSupportedVersions() {
|
||||
return plugin.getConfig().getBoolean("send-supported-versions", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStimulatePlayerTick() {
|
||||
return plugin.getConfig().getBoolean("simulate-pt", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemCache() {
|
||||
return plugin.getConfig().getBoolean("item-cache", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNMSPlayerTicking() {
|
||||
return plugin.getConfig().getBoolean("nms-player-ticking", true);
|
||||
}
|
||||
|
||||
public boolean isAutoTeam() {
|
||||
// Collision has to be enabled first
|
||||
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.ViaVersionAPI;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.boss.ViaBossBar;
|
||||
import us.myles.ViaVersion.classgenerator.ClassGenerator;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
import us.myles.ViaVersion.update.UpdateListener;
|
||||
import us.myles.ViaVersion.update.UpdateUtil;
|
||||
import us.myles.ViaVersion.util.ListWrapper;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
|
||||
private final Map<UUID, UserConnection> portedPlayers = new ConcurrentHashMap<>();
|
||||
private List<ChannelFuture> injectedFutures = new ArrayList<>();
|
||||
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
|
||||
private ViaCommandHandler commandHandler;
|
||||
private boolean debug = false;
|
||||
private boolean compatSpigotBuild = false;
|
||||
private boolean spigot = true;
|
||||
private boolean lateBind = false;
|
||||
@Getter
|
||||
private ViaConfig conf;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Config magic
|
||||
conf = new ViaConfig(this);
|
||||
ViaVersion.setInstance(this);
|
||||
// Handle reloads
|
||||
if (System.getProperty("ViaVersion") != null) {
|
||||
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
|
||||
getLogger().severe("ViaVersion is already loaded, we're going to kick all the players... because otherwise we'll crash because of ProtocolLib.");
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.kickPlayer("Server reload, please rejoin!");
|
||||
}
|
||||
|
||||
} else {
|
||||
getLogger().severe("ViaVersion is already loaded, this should work fine... Otherwise reboot the server!!!");
|
||||
|
||||
}
|
||||
}
|
||||
// Spigot detector
|
||||
try {
|
||||
Class.forName("org.spigotmc.SpigotConfig");
|
||||
} catch (ClassNotFoundException e) {
|
||||
spigot = false;
|
||||
}
|
||||
// Check if it's a spigot build with a protocol mod
|
||||
try {
|
||||
compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null;
|
||||
} catch (Exception e) {
|
||||
compatSpigotBuild = false;
|
||||
}
|
||||
// Generate classes needed (only works if it's compat)
|
||||
ClassGenerator.generate();
|
||||
lateBind = !isBinded();
|
||||
|
||||
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
|
||||
if (!lateBind)
|
||||
injectPacketHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (lateBind)
|
||||
injectPacketHandler();
|
||||
if (conf.isCheckForUpdates())
|
||||
UpdateUtil.sendUpdateMessage(this);
|
||||
// Gather version :)
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gatherProtocolVersion();
|
||||
// Check if there are any pipes to this version
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
|
||||
getLogger().info("ViaVersion detected protocol version: " + ProtocolRegistry.SERVER_PROTOCOL);
|
||||
if (!ProtocolRegistry.isWorkingPipe()) {
|
||||
getLogger().warning("ViaVersion will not function on the current protocol.");
|
||||
}
|
||||
}
|
||||
ProtocolRegistry.refreshVersions();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new UpdateListener(this), this);
|
||||
|
||||
getCommand("viaversion").setExecutor(commandHandler = new ViaCommandHandler());
|
||||
getCommand("viaversion").setTabCompleter(commandHandler);
|
||||
|
||||
// Register Protocol Listeners
|
||||
ProtocolRegistry.registerListeners();
|
||||
|
||||
// Warn them if they have anti-xray on and they aren't using spigot
|
||||
if (conf.isAntiXRay() && !spigot) {
|
||||
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("ViaVersion is disabling, if this is a reload it may not work.");
|
||||
uninject();
|
||||
}
|
||||
|
||||
public void gatherProtocolVersion() {
|
||||
try {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
|
||||
Object ping = null;
|
||||
// Search for ping method
|
||||
for (Field f : serverClazz.getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType().getSimpleName().equals("ServerPing")) {
|
||||
f.setAccessible(true);
|
||||
ping = f.get(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ping != null) {
|
||||
Object serverData = null;
|
||||
for (Field f : pingClazz.getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType().getSimpleName().endsWith("ServerData")) {
|
||||
f.setAccessible(true);
|
||||
serverData = f.get(ping);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (serverData != null) {
|
||||
int protocolVersion = -1;
|
||||
for (Field f : serverData.getClass().getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType() == int.class) {
|
||||
f.setAccessible(true);
|
||||
protocolVersion = (int) f.get(serverData);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (protocolVersion != -1) {
|
||||
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// We couldn't work it out... We'll just use ping and hope for the best...
|
||||
}
|
||||
}
|
||||
|
||||
public Object getServerConnection() throws Exception {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = null;
|
||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void injectPacketHandler() {
|
||||
try {
|
||||
Object connection = getServerConnection();
|
||||
if (connection == null) {
|
||||
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
|
||||
return;
|
||||
}
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
List wrapper = new ListWrapper((List) value) {
|
||||
@Override
|
||||
public synchronized void handleAdd(Object o) {
|
||||
synchronized (this) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
injectedLists.add(new Pair<>(field, connection));
|
||||
field.set(connection, wrapper);
|
||||
// Iterate through current list
|
||||
synchronized (wrapper) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.setProperty("ViaVersion", getDescription().getVersion());
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBinded() {
|
||||
try {
|
||||
Object connection = getServerConnection();
|
||||
if (connection == null) {
|
||||
return false;
|
||||
}
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
synchronized (value) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
return true;
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void inject(ChannelFuture future) {
|
||||
try {
|
||||
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
|
||||
try {
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||
injectedFutures.add(future);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// let's find who to blame!
|
||||
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
|
||||
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
|
||||
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
|
||||
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
|
||||
} else {
|
||||
throw new Exception("Unable to find childHandler, weird server version? " + bootstrapAcceptor.getClass().getName());
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Have you got late-bind enabled with something else?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void uninject() {
|
||||
// TODO: Uninject from players currently online to prevent protocol lib issues.
|
||||
for (ChannelFuture future : injectedFutures) {
|
||||
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
|
||||
try {
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
if (oldInit instanceof ViaVersionInitializer) {
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to remove injection... reload won't work with connections sorry");
|
||||
}
|
||||
}
|
||||
injectedFutures.clear();
|
||||
|
||||
for (Pair<Field, Object> pair : injectedLists) {
|
||||
try {
|
||||
Object o = pair.getKey().get(pair.getValue());
|
||||
if (o instanceof ListWrapper) {
|
||||
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
System.out.println("Failed to remove injection... reload might not work with connections sorry");
|
||||
}
|
||||
}
|
||||
|
||||
injectedLists.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPorted(Player player) {
|
||||
return isPorted(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerVersion(@NonNull Player player) {
|
||||
if (!isPorted(player))
|
||||
return ProtocolRegistry.SERVER_PROTOCOL;
|
||||
return portedPlayers.get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerVersion(@NonNull UUID uuid) {
|
||||
if (!isPorted(uuid))
|
||||
return ProtocolRegistry.SERVER_PROTOCOL;
|
||||
return portedPlayers.get(uuid).get(ProtocolInfo.class).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPorted(UUID playerUUID) {
|
||||
return portedPlayers.containsKey(playerUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return getDescription().getVersion();
|
||||
}
|
||||
|
||||
public UserConnection getConnection(UUID playerUUID) {
|
||||
return portedPlayers.get(playerUUID);
|
||||
}
|
||||
|
||||
public UserConnection getConnection(Player player) {
|
||||
return portedPlayers.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException {
|
||||
sendRawPacket(player.getUniqueId(), packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException {
|
||||
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!");
|
||||
UserConnection ci = portedPlayers.get(uuid);
|
||||
ci.sendRawPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar createBossBar(String title, BossColor color, BossStyle style) {
|
||||
return new ViaBossBar(title, 1F, color, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar createBossBar(String title, float health, BossColor color, BossStyle style) {
|
||||
return new ViaBossBar(title, health, color, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean value) {
|
||||
this.debug = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaVersionCommand getCommandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatSpigotBuild() {
|
||||
return compatSpigotBuild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Integer> getSupportedVersions() {
|
||||
return ProtocolRegistry.getSupportedVersions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpigot() {
|
||||
return this.spigot;
|
||||
}
|
||||
|
||||
public void addPortedClient(UserConnection info) {
|
||||
portedPlayers.put(info.get(ProtocolInfo.class).getUuid(), info);
|
||||
}
|
||||
|
||||
public void removePortedClient(UUID clientID) {
|
||||
portedPlayers.remove(clientID);
|
||||
}
|
||||
|
||||
public void run(final Runnable runnable, boolean wait) {
|
||||
try {
|
||||
Future f = Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (wait) {
|
||||
f.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to run task: " + e.getClass().getName());
|
||||
if (ViaVersion.getInstance().isDebug())
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<UUID, UserConnection> getPortedPlayers() {
|
||||
return portedPlayers;
|
||||
}
|
||||
|
||||
public boolean handlePPS(UserConnection info) {
|
||||
// Max PPS Checker
|
||||
if (conf.getMaxPPS() > 0) {
|
||||
if (info.getPacketsPerSecond() >= conf.getMaxPPS()) {
|
||||
info.disconnect(conf.getMaxPPSKickMessage());
|
||||
return true; // don't send current packet
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking PPS Checker
|
||||
if (conf.getMaxWarnings() > 0 && conf.getTrackingPeriod() > 0) {
|
||||
if (info.getSecondsObserved() > conf.getTrackingPeriod()) {
|
||||
// Reset
|
||||
info.setWarnings(0);
|
||||
info.setSecondsObserved(1);
|
||||
} else {
|
||||
info.setSecondsObserved(info.getSecondsObserved() + 1);
|
||||
if (info.getPacketsPerSecond() >= conf.getWarningPPS()) {
|
||||
info.setWarnings(info.getWarnings() + 1);
|
||||
}
|
||||
|
||||
if (info.getWarnings() >= conf.getMaxWarnings()) {
|
||||
info.disconnect(conf.getMaxWarningsKickMessage());
|
||||
return true; // don't send current packet
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.remapper.ValueCreator;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.util.PipelineUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PacketWrapper {
|
||||
private final ByteBuf inputBuffer;
|
||||
private final UserConnection userConnection;
|
||||
private boolean send = true;
|
||||
@Setter
|
||||
@Getter
|
||||
private int id = -1;
|
||||
private LinkedList<Pair<Type, Object>> readableObjects = new LinkedList<>();
|
||||
private List<Pair<Type, Object>> packetValues = new ArrayList<>();
|
||||
|
||||
public PacketWrapper(int packetID, ByteBuf inputBuffer, UserConnection userConnection) {
|
||||
this.id = packetID;
|
||||
this.inputBuffer = inputBuffer;
|
||||
this.userConnection = userConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a part from the output
|
||||
*
|
||||
* @param type The type of the part you wish to get.
|
||||
* @param <T> The return type of the type you wish to get.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @return The requested type or throws ArrayIndexOutOfBounds
|
||||
* @throws Exception If it fails to find it, an exception will be thrown.
|
||||
*/
|
||||
public <T> T get(Type<T> type, int index) throws Exception {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
return (T) packetValue.getValue();
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a type is at an index
|
||||
*
|
||||
* @param type The type of the part you wish to get.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @return True if the type is at the index
|
||||
*/
|
||||
public boolean is(Type type, int index) {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
return true;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a currently existing part in the output
|
||||
*
|
||||
* @param type The type of the part you wish to set.
|
||||
* @param <T> The return type of the type you wish to set.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @param value The value of the part you wish to set it to.
|
||||
* @throws Exception If it fails to set it, an exception will be thrown.
|
||||
*/
|
||||
public <T> void set(Type<T> type, int index, T value) throws Exception {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
packetValue.setValue(value);
|
||||
return;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a type from the input.
|
||||
*
|
||||
* @param type The type you wish to read
|
||||
* @param <T> The return type of the type you wish to read.
|
||||
* @return The requested type
|
||||
* @throws Exception If it fails to read
|
||||
*/
|
||||
public <T> T read(Type<T> type) throws Exception {
|
||||
if (type == Type.NOTHING) return null;
|
||||
if (readableObjects.isEmpty()) {
|
||||
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
|
||||
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
|
||||
try {
|
||||
return type.read(inputBuffer);
|
||||
} catch (Exception e) {
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
} else {
|
||||
Pair<Type, Object> read = readableObjects.poll();
|
||||
if (read.getKey().equals(type) || (type.getBaseClass().equals(read.getKey().getBaseClass()) && type.getOutputClass().equals(read.getKey().getOutputClass()))) {
|
||||
return (T) read.getValue();
|
||||
} else {
|
||||
if (type == Type.NOTHING) {
|
||||
return read(type); // retry
|
||||
} else {
|
||||
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a type to the output.
|
||||
*
|
||||
* @param type The type to write.
|
||||
* @param <T> The return type of the type you wish to write.
|
||||
* @param value The value of the type to write.
|
||||
*/
|
||||
public <T> void write(Type<T> type, T value) {
|
||||
if (value != null) {
|
||||
if (!type.getOutputClass().isAssignableFrom(value.getClass())) {
|
||||
// attempt conversion
|
||||
if (type instanceof TypeConverter) {
|
||||
value = (T) ((TypeConverter) type).from(value);
|
||||
} else {
|
||||
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + type.getOutputClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
packetValues.add(new Pair<Type, Object>(type, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a value from the input and write to the output.
|
||||
*
|
||||
* @param type The type to read and write.
|
||||
* @param <T> The return type of the type you wish to pass through.
|
||||
* @return The type which was read/written.
|
||||
* @throws Exception If it failed to read or write
|
||||
*/
|
||||
public <T> T passthrough(Type<T> type) throws Exception {
|
||||
T value = read(type);
|
||||
write(type, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take all the inputs and write them to the output.
|
||||
*
|
||||
* @throws Exception If it failed to read or write
|
||||
*/
|
||||
public void passthroughAll() throws Exception {
|
||||
// Copy previous objects
|
||||
packetValues.addAll(readableObjects);
|
||||
readableObjects.clear();
|
||||
// If the buffer has readable bytes, copy them.
|
||||
if (inputBuffer.readableBytes() > 0) {
|
||||
passthrough(Type.REMAINING_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the current output to a buffer.
|
||||
*
|
||||
* @param buffer The buffer to write to.
|
||||
* @throws Exception Throws an exception if it fails to write a value.
|
||||
*/
|
||||
public void writeToBuffer(ByteBuf buffer) throws Exception {
|
||||
if (id != -1) {
|
||||
Type.VAR_INT.write(buffer, id);
|
||||
}
|
||||
if (readableObjects.size() > 0) {
|
||||
packetValues.addAll(readableObjects);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
try {
|
||||
Object value = packetValue.getValue();
|
||||
if (value != null) {
|
||||
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
|
||||
// attempt conversion
|
||||
if (packetValue.getKey() instanceof TypeConverter) {
|
||||
value = ((TypeConverter) packetValue.getKey()).from(value);
|
||||
} else {
|
||||
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + packetValue.getKey().getOutputClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
packetValue.getKey().write(buffer, value);
|
||||
} catch (Exception e) {
|
||||
throw new InformativeException(e).set("Index", index).set("Type", packetValue.getKey().getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
writeRemaining(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the input buffer / readable objects
|
||||
*/
|
||||
public void clearInputBuffer() {
|
||||
if (inputBuffer != null)
|
||||
inputBuffer.clear();
|
||||
readableObjects.clear(); // :(
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the packet, used if you have to change the packet completely
|
||||
*/
|
||||
public void clearPacket() {
|
||||
clearInputBuffer();
|
||||
packetValues.clear();
|
||||
}
|
||||
|
||||
private void writeRemaining(ByteBuf output) {
|
||||
if (inputBuffer != null) {
|
||||
output.writeBytes(inputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this packet to the associated user.
|
||||
* Be careful not to send packets twice.
|
||||
* (Sends it after current)
|
||||
*
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
public void send() throws Exception {
|
||||
if (!isCancelled()) {
|
||||
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
|
||||
writeToBuffer(output);
|
||||
user().sendRawPacket(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet for the target of this packet.
|
||||
*
|
||||
* @param packetID The ID of the new packet
|
||||
* @return The newly created packet wrapper
|
||||
*/
|
||||
public PacketWrapper create(int packetID) {
|
||||
return new PacketWrapper(packetID, null, user());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet with values.
|
||||
*
|
||||
* @param packetID The ID of the new packet
|
||||
* @param init A ValueCreator to write to the packet.
|
||||
* @return The newly created packet wrapper
|
||||
* @throws Exception If it failed to write the values from the ValueCreator.
|
||||
*/
|
||||
public PacketWrapper create(int packetID, ValueCreator init) throws Exception {
|
||||
PacketWrapper wrapper = create(packetID);
|
||||
init.write(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel this packet from sending
|
||||
*/
|
||||
public void cancel() {
|
||||
this.send = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this packet is cancelled.
|
||||
*
|
||||
* @return True if the packet won't be sent.
|
||||
*/
|
||||
public boolean isCancelled() {
|
||||
return !this.send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user associated with this Packet
|
||||
*
|
||||
* @return The user
|
||||
*/
|
||||
public UserConnection user() {
|
||||
return this.userConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the reader, so that it can be read again.
|
||||
*/
|
||||
public void resetReader() {
|
||||
this.readableObjects.addAll(packetValues);
|
||||
this.packetValues.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current packet to the server.
|
||||
* (Ensure the ID is suitable for the server version)
|
||||
*
|
||||
* @throws Exception If it failed to write
|
||||
*/
|
||||
public void sendToServer() throws Exception {
|
||||
if (!isCancelled()) {
|
||||
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
|
||||
Type.VAR_INT.write(output, ViaDecodeHandler.PASSTHROUGH_ID); // Pass through
|
||||
|
||||
writeToBuffer(output);
|
||||
|
||||
PipelineUtil.getContextBefore("decompress", user().getChannel().pipeline()).fireChannelRead(output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PacketWrapper{" +
|
||||
"packetValues=" + packetValues +
|
||||
", readableObjects=" + readableObjects +
|
||||
", id=" + id +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class Pair<X, Y> {
|
||||
private X key;
|
||||
private Y value;
|
||||
|
||||
public Pair(X key, Y value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pair{" + key + ", " + value + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
|
||||
public class ViaVersion {
|
||||
|
||||
@Getter
|
||||
private static ViaVersionAPI instance;
|
||||
@Getter
|
||||
private static ViaVersionConfig config;
|
||||
|
||||
public static void setInstance(ViaVersionPlugin plugin) {
|
||||
Validate.isTrue(instance == null, "Instance is already set");
|
||||
ViaVersion.instance = plugin;
|
||||
ViaVersion.config = plugin.getConf();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ViaVersionAPI {
|
||||
/**
|
||||
* Is the player connection modified by ViaVersion?
|
||||
*
|
||||
* @param player Bukkit player object
|
||||
* @return True if the client is modified (At the moment it also means version 1.9 and higher)
|
||||
*/
|
||||
boolean isPorted(Player player);
|
||||
|
||||
/**
|
||||
* Get protocol number from a player
|
||||
*
|
||||
* @param player Bukkit player object
|
||||
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
|
||||
*/
|
||||
int getPlayerVersion(Player player);
|
||||
|
||||
/**
|
||||
* Get protocol number from a player
|
||||
*
|
||||
* @param uuid UUID of a player
|
||||
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
|
||||
*/
|
||||
int getPlayerVersion(UUID uuid);
|
||||
|
||||
/**
|
||||
* Is player using 1.9?
|
||||
*
|
||||
* @param playerUUID UUID of a player
|
||||
* @return True if the client is on 1.9
|
||||
*/
|
||||
boolean isPorted(UUID playerUUID);
|
||||
|
||||
/**
|
||||
* Get the version of the plugin
|
||||
*
|
||||
* @return Plugin version
|
||||
*/
|
||||
String getVersion();
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player (Use new IDs)
|
||||
*
|
||||
* @param player The player to send packet
|
||||
* @param packet The packet, you need a VarInt ID then the packet contents.
|
||||
* @throws IllegalArgumentException If not on 1.9 throws IllegalArg
|
||||
*/
|
||||
void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player (Use new IDs)
|
||||
*
|
||||
* @param uuid The uuid from the player to send packet
|
||||
* @param packet The packet, you need a VarInt ID then the packet contents.
|
||||
* @throws IllegalArgumentException If not on 1.9 throws IllegalArg
|
||||
*/
|
||||
void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Create a new bossbar instance
|
||||
*
|
||||
* @param title The title
|
||||
* @param color The color
|
||||
* @param style The style
|
||||
* @return Bossbar instance
|
||||
*/
|
||||
BossBar createBossBar(String title, BossColor color, BossStyle style);
|
||||
|
||||
/**
|
||||
* Create a new bossbar instance
|
||||
*
|
||||
* @param title The title
|
||||
* @param health Number between 0 and 1
|
||||
* @param color The color
|
||||
* @param style The style
|
||||
* @return Bossbar instance
|
||||
*/
|
||||
BossBar createBossBar(String title, float health, BossColor color, BossStyle style);
|
||||
|
||||
/**
|
||||
* Get if global debug is enabled
|
||||
*
|
||||
* @return true if debug is enabled
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Get ViaVersions command handler
|
||||
*
|
||||
* @return command handler
|
||||
*/
|
||||
ViaVersionCommand getCommandHandler();
|
||||
|
||||
/**
|
||||
* Get if this version is a compatibility build for spigot.
|
||||
* Eg. 1.9.1 / 1.9.2 allow certain versions to connect
|
||||
*
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isCompatSpigotBuild();
|
||||
|
||||
/**
|
||||
* Get the supported protocol versions
|
||||
*
|
||||
* @return a list of protocol versions
|
||||
*/
|
||||
SortedSet<Integer> getSupportedVersions();
|
||||
|
||||
/**
|
||||
* Gets if the server uses spigot
|
||||
*
|
||||
* Note: Will only work after ViaVersion load
|
||||
* @return True if spigot
|
||||
*/
|
||||
boolean isSpigot();
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
public interface ViaVersionConfig {
|
||||
|
||||
/**
|
||||
* Get if the plugin should check for updates
|
||||
*
|
||||
* @return true if update checking is enabled
|
||||
*/
|
||||
boolean isCheckForUpdates();
|
||||
|
||||
/**
|
||||
* Get if collision preventing for players is enabled
|
||||
*
|
||||
* @return true if collision preventing is enabled
|
||||
*/
|
||||
boolean isPreventCollision();
|
||||
|
||||
/**
|
||||
* Get if 1.9 & 1.10 clients are shown the new effect indicator in the top-right corner
|
||||
*
|
||||
* @return true if the using of the new effect indicator is enabled
|
||||
*/
|
||||
boolean isNewEffectIndicator();
|
||||
|
||||
/**
|
||||
* Get if 1.9 & 1.10 clients are shown the new death message on the death screen
|
||||
*
|
||||
* @return true if enabled
|
||||
*/
|
||||
boolean isShowNewDeathMessages();
|
||||
|
||||
/**
|
||||
* Get if metadata errors will be suppressed
|
||||
*
|
||||
* @return true if metadata errors suppression is enabled
|
||||
*/
|
||||
boolean isSuppressMetadataErrors();
|
||||
|
||||
/**
|
||||
* Get if blocking in 1.9 & 1.10 appears as a player holding a shield
|
||||
*
|
||||
* @return true if shield blocking is enabled
|
||||
*/
|
||||
boolean isShieldBlocking();
|
||||
|
||||
/**
|
||||
* Get if armor stand positions are fixed so holograms show up at the correct height in 1.9 & 1.10
|
||||
*
|
||||
* @return true if hologram patching is enabled
|
||||
*/
|
||||
boolean isHologramPatch();
|
||||
|
||||
/**
|
||||
* Get if boss bars are fixed for 1.9 & 1.10 clients
|
||||
*
|
||||
* @return true if boss bar patching is enabled
|
||||
*/
|
||||
boolean isBossbarPatch();
|
||||
|
||||
/**
|
||||
* Get if the boss bars for 1.9 & 1.10 clients are being stopped from flickering
|
||||
* This will keep all boss bars on 100% (not recommended)
|
||||
*
|
||||
* @return true if boss bar anti flickering is enabled
|
||||
*/
|
||||
boolean isBossbarAntiflicker();
|
||||
|
||||
/**
|
||||
* Get if unknown entity errors will be suppressed
|
||||
*
|
||||
* @return true if boss bar patching is enabled
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isUnknownEntitiesSuppressed();
|
||||
|
||||
/**
|
||||
* Get the vertical offset armor stands are being moved with when the hologram patch is enabled
|
||||
*
|
||||
* @return the vertical offset holograms will be moved with
|
||||
*/
|
||||
double getHologramYOffset();
|
||||
|
||||
/**
|
||||
* Get if players will be automatically put in the same team when collision preventing is enabled
|
||||
*
|
||||
* @return true if automatic teaming is enabled
|
||||
*/
|
||||
boolean isAutoTeam();
|
||||
|
||||
/**
|
||||
* Get if our block break patch is enabled to prevent weird ghost glitches.
|
||||
*
|
||||
* @return true if it is enabled.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isBlockBreakPatch();
|
||||
|
||||
/**
|
||||
* Get the maximum number of packets a client can send per second.
|
||||
*
|
||||
* @return The number of packets a client can send per second.
|
||||
*/
|
||||
int getMaxPPS();
|
||||
|
||||
/**
|
||||
* Get the kick message sent if the user hits the max packets per second.
|
||||
*
|
||||
* @return Kick message, with colour codes using '&'
|
||||
*/
|
||||
String getMaxPPSKickMessage();
|
||||
|
||||
/**
|
||||
* The time in seconds that should be tracked for warnings
|
||||
*
|
||||
* @return Time in seconds that should be tracked for warnings
|
||||
*/
|
||||
int getTrackingPeriod();
|
||||
|
||||
/**
|
||||
* The number of packets per second to count as a warning
|
||||
*
|
||||
* @return The number of packets per second to count as a warning.
|
||||
*/
|
||||
int getWarningPPS();
|
||||
|
||||
/**
|
||||
* Get the maximum number of warnings the client can have in the interval
|
||||
*
|
||||
* @return The number of packets a client can send per second.
|
||||
*/
|
||||
int getMaxWarnings();
|
||||
|
||||
/**
|
||||
* Get the kick message sent if the user goes over the warnings in the interval
|
||||
*
|
||||
* @return Kick message, with colour codes using '&'
|
||||
*/
|
||||
String getMaxWarningsKickMessage();
|
||||
|
||||
/**
|
||||
* Is anti-xray enabled?
|
||||
*
|
||||
* @return A boolean
|
||||
*/
|
||||
boolean isAntiXRay();
|
||||
|
||||
/**
|
||||
* Send supported versions in the status response packet
|
||||
*
|
||||
* @return If true, enabled
|
||||
*/
|
||||
boolean isSendSupportedVersions();
|
||||
|
||||
/**
|
||||
* Stimulate the player tick
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isStimulatePlayerTick();
|
||||
|
||||
/**
|
||||
* Use the item cache to prevent high resource usage
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isItemCache();
|
||||
|
||||
/**
|
||||
* Use the NMS player ticking
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isNMSPlayerTicking();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BossBar {
|
||||
/**
|
||||
* Get the current title
|
||||
*
|
||||
* @return the title
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Change the title
|
||||
*
|
||||
* @param title Title can be in either JSON or just text
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar setTitle(String title);
|
||||
|
||||
/**
|
||||
* Get the health
|
||||
*
|
||||
* @return float between 0F - 1F
|
||||
*/
|
||||
float getHealth();
|
||||
|
||||
/**
|
||||
* Change the health
|
||||
*
|
||||
* @param health this float has to be between 0F - 1F
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar setHealth(float health);
|
||||
|
||||
/**
|
||||
* Get the bossbar color
|
||||
*
|
||||
* @return The colour
|
||||
*/
|
||||
BossColor getColor();
|
||||
|
||||
/**
|
||||
* Yay colors!
|
||||
*
|
||||
* @param color Whatever color you want!
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar setColor(BossColor color);
|
||||
|
||||
/**
|
||||
* Get the bosbar style
|
||||
*
|
||||
* @return BossStyle
|
||||
*/
|
||||
BossStyle getStyle();
|
||||
|
||||
/**
|
||||
* Change the bosbar style
|
||||
*
|
||||
* @param style BossStyle
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar setStyle(BossStyle style);
|
||||
|
||||
/**
|
||||
* Show the bossbar to a player.
|
||||
*
|
||||
* @param player The player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addPlayer(Player player);
|
||||
|
||||
/**
|
||||
* Show the bossbar to a player (uuid)
|
||||
*
|
||||
* @param player uuid of the player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addPlayer(UUID player);
|
||||
|
||||
/**
|
||||
* add multiple players
|
||||
*
|
||||
* @param players list of players
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addPlayers(Player... players);
|
||||
|
||||
/**
|
||||
* Remove the bossbar from a player
|
||||
*
|
||||
* @param player The player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar removePlayer(Player player);
|
||||
|
||||
/**
|
||||
* Add flags
|
||||
*
|
||||
* @param flag The flag to add
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addFlag(BossFlag flag);
|
||||
|
||||
/**
|
||||
* Remove flags.
|
||||
*
|
||||
* @param flag The flag to remove
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar removeFlag(BossFlag flag);
|
||||
|
||||
/**
|
||||
* @param flag The flag to check against
|
||||
* @return True if it has the flag
|
||||
*/
|
||||
boolean hasFlag(BossFlag flag);
|
||||
|
||||
/**
|
||||
* Get players
|
||||
*
|
||||
* @return UUIDS from players (sorry I lied)
|
||||
*/
|
||||
Set<UUID> getPlayers();
|
||||
|
||||
/**
|
||||
* Show the bossbar to everyone (In the getPlayer set)
|
||||
*
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar show();
|
||||
|
||||
/**
|
||||
* Hide the bossbar from everyone (In the getPlayer set)
|
||||
*
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar hide();
|
||||
|
||||
/**
|
||||
* Is it visible?
|
||||
*
|
||||
* @return visibility changable with show() and hide()
|
||||
*/
|
||||
boolean isVisible();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossColor {
|
||||
PINK(0),
|
||||
BLUE(1),
|
||||
RED(2),
|
||||
GREEN(3),
|
||||
YELLOW(4),
|
||||
PURPLE(5),
|
||||
WHITE(6);
|
||||
|
||||
private final int id;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossFlag {
|
||||
DARKEN_SKY(1),
|
||||
PLAY_BOSS_MUSIC(2);
|
||||
|
||||
private final int id;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package us.myles.ViaVersion.api.boss;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum BossStyle {
|
||||
SOLID(0),
|
||||
SEGMENTED_6(1),
|
||||
SEGMENTED_10(2),
|
||||
SEGMENTED_12(3),
|
||||
SEGMENTED_20(4);
|
||||
|
||||
private final int id;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package us.myles.ViaVersion.api.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ViaSubCommand {
|
||||
/**
|
||||
* Subcommand name
|
||||
*
|
||||
* @return The command name
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* subcommand description, this'll show in /viaversion list
|
||||
*
|
||||
* @return The command description
|
||||
*/
|
||||
public abstract String description();
|
||||
|
||||
/**
|
||||
* Usage example:
|
||||
* "playerversion [name]"
|
||||
*
|
||||
* @return The command usage
|
||||
*/
|
||||
public String usage() {
|
||||
return name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission, null for everyone
|
||||
*
|
||||
* @return The permission required to use the command
|
||||
*/
|
||||
public String permission() {
|
||||
return "viaversion.admin";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets triggered on execution
|
||||
*
|
||||
* @param sender Command sender
|
||||
* @param args Arguments
|
||||
* @return command executed succesfully if false, show usage
|
||||
*/
|
||||
public abstract boolean execute(CommandSender sender, String[] args);
|
||||
|
||||
/**
|
||||
* Yay, possibility to implement tab-completion
|
||||
*
|
||||
* @param sender Command sender
|
||||
* @param args args
|
||||
* @return tab complete possibilities
|
||||
*/
|
||||
public List<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public String color(String s) {
|
||||
return ViaCommandHandler.color(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message formatted / colored
|
||||
*
|
||||
* @param sender command sender
|
||||
* @param message string message
|
||||
* @param args optional objects
|
||||
*/
|
||||
public void sendMessage(CommandSender sender, String message, Object... args) {
|
||||
ViaCommandHandler.sendMessage(sender, message, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.command;
|
||||
|
||||
public interface ViaVersionCommand {
|
||||
/**
|
||||
* Register your own subcommand inside ViaVersion
|
||||
*
|
||||
* @param command Your own SubCommand instance to handle it.
|
||||
* @throws Exception throws an exception when the subcommand already exists or if it's not valid, example: spacee
|
||||
*/
|
||||
void registerSubCommand(ViaSubCommand command) throws Exception;
|
||||
|
||||
/**
|
||||
* Check if a subcommand is registered.
|
||||
*
|
||||
* @param name Subcommand name
|
||||
* @return true if it exists
|
||||
*/
|
||||
boolean hasSubCommand(String name);
|
||||
|
||||
/**
|
||||
* Get subcommand instance by name
|
||||
*
|
||||
* @param name subcommand name
|
||||
* @return ViaSubCommand instance
|
||||
*/
|
||||
ViaSubCommand getSubCommand(String name);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package us.myles.ViaVersion.api.data;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor
|
||||
public class StoredObject {
|
||||
private UserConnection user;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package us.myles.ViaVersion.api.data;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Data
|
||||
public class UserConnection {
|
||||
private final SocketChannel channel;
|
||||
Map<Class, StoredObject> storedObjects = new ConcurrentHashMap<>();
|
||||
private boolean active = true;
|
||||
private boolean pendingDisconnect = false;
|
||||
private Object lastPacket;
|
||||
private long sentPackets = 0L;
|
||||
private long receivedPackets = 0L;
|
||||
// Used for tracking pps
|
||||
private long startTime = 0L;
|
||||
private long intervalPackets = 0L;
|
||||
private long packetsPerSecond = -1L;
|
||||
// Used for handling warnings (over time)
|
||||
private int secondsObserved = 0;
|
||||
private int warnings = 0;
|
||||
|
||||
|
||||
public UserConnection(SocketChannel socketChannel) {
|
||||
this.channel = socketChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the storage
|
||||
*
|
||||
* @param objectClass The class of the object to get
|
||||
* @param <T> The type of the class you want to get.
|
||||
* @return The requested object
|
||||
*/
|
||||
public <T extends StoredObject> T get(Class<T> objectClass) {
|
||||
return (T) storedObjects.get(objectClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the storage has an object
|
||||
*
|
||||
* @param objectClass The object class to check
|
||||
* @return True if the object is in the storage
|
||||
*/
|
||||
public boolean has(Class<? extends StoredObject> objectClass) {
|
||||
return storedObjects.containsKey(objectClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an object into the stored objects based on class
|
||||
*
|
||||
* @param object The object to store.
|
||||
*/
|
||||
public void put(StoredObject object) {
|
||||
storedObjects.put(object.getClass(), object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player
|
||||
*
|
||||
* @param packet The raw packet to send
|
||||
* @param currentThread Should it run in the same thread
|
||||
*/
|
||||
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
if (currentThread) {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
} else {
|
||||
channel.eventLoop().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player (netty thread)
|
||||
*
|
||||
* @param packet The packet to send
|
||||
*/
|
||||
public void sendRawPacket(final ByteBuf packet) {
|
||||
sendRawPacket(packet, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for incrementing the number of packets sent to the client
|
||||
*/
|
||||
public void incrementSent() {
|
||||
this.sentPackets++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for incrementing the number of packets received from the client
|
||||
*
|
||||
* @return True if the interval has reset
|
||||
*/
|
||||
public boolean incrementReceived() {
|
||||
// handle stats
|
||||
Long diff = System.currentTimeMillis() - startTime;
|
||||
if (diff >= 1000) {
|
||||
packetsPerSecond = intervalPackets;
|
||||
startTime = System.currentTimeMillis();
|
||||
intervalPackets = 1;
|
||||
return true;
|
||||
} else {
|
||||
intervalPackets++;
|
||||
}
|
||||
// increase total
|
||||
this.receivedPackets++;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect a connection
|
||||
*
|
||||
* @param reason The reason to use, not used if player is not active.
|
||||
*/
|
||||
public void disconnect(final String reason) {
|
||||
if (!getChannel().isOpen()) return;
|
||||
if (pendingDisconnect) return;
|
||||
pendingDisconnect = true;
|
||||
if (get(ProtocolInfo.class).getUuid() != null) {
|
||||
final UUID uuid = get(ProtocolInfo.class).getUuid();
|
||||
if (Bukkit.getPlayer(uuid) != null) {
|
||||
Bukkit.getScheduler().runTask(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player != null)
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
getChannel().close(); // =)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package us.myles.ViaVersion.api.minecraft;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class Position {
|
||||
private Long x;
|
||||
private Long y;
|
||||
private Long z;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
public class Chunk {
|
||||
private final int x;
|
||||
private final int z;
|
||||
private final boolean groundUp;
|
||||
private final int primaryBitmask;
|
||||
private final ChunkSection[] sections;
|
||||
private final byte[] biomeData;
|
||||
private boolean unloadPacket = false;
|
||||
|
||||
/**
|
||||
* Chunk unload.
|
||||
*
|
||||
* @param x coord
|
||||
* @param z coord
|
||||
*/
|
||||
public Chunk(int x, int z) {
|
||||
this(x, z, true, 0, new ChunkSection[16], null);
|
||||
this.unloadPacket = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this chunk have biome data
|
||||
*
|
||||
* @return True if the chunk has biome data
|
||||
*/
|
||||
public boolean hasBiomeData() {
|
||||
return biomeData != null && groundUp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChunkSection {
|
||||
/**
|
||||
* Size (dimensions) of blocks in a chunk section.
|
||||
*/
|
||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
||||
/**
|
||||
* Length of the sky and block light nibble arrays.
|
||||
*/
|
||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
||||
/**
|
||||
* Length of the block data array.
|
||||
*/
|
||||
|
||||
private final List<Integer> palette = Lists.newArrayList();
|
||||
private final int[] blocks;
|
||||
private final NibbleArray blockLight;
|
||||
private NibbleArray skyLight;
|
||||
|
||||
public ChunkSection() {
|
||||
this.blocks = new int[SIZE];
|
||||
this.blockLight = new NibbleArray(SIZE);
|
||||
palette.add(0); // AIR
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block in the chunk
|
||||
*
|
||||
* @param x Block X
|
||||
* @param y Block Y
|
||||
* @param z Block Z
|
||||
* @param type The type of the block
|
||||
* @param data The data value of the block
|
||||
*/
|
||||
public void setBlock(int x, int y, int z, int type, int data) {
|
||||
setBlock(index(x, y, z), type, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block in the chunk based on the index
|
||||
*
|
||||
* @param idx Index
|
||||
* @param type The type of the block
|
||||
* @param data The data value of the block
|
||||
*/
|
||||
public void setBlock(int idx, int type, int data) {
|
||||
int hash = type << 4 | (data & 0xF);
|
||||
int index = palette.indexOf(hash);
|
||||
if (index == -1) {
|
||||
index = palette.size();
|
||||
palette.add(hash);
|
||||
}
|
||||
|
||||
blocks[idx] = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block light array
|
||||
*
|
||||
* @param data The value to set the block light to
|
||||
*/
|
||||
public void setBlockLight(byte[] data) {
|
||||
blockLight.setHandle(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sky light array
|
||||
*
|
||||
* @param data The value to set the sky light to
|
||||
*/
|
||||
public void setSkyLight(byte[] data) {
|
||||
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
||||
this.skyLight = new NibbleArray(data);
|
||||
}
|
||||
|
||||
private int index(int x, int y, int z) {
|
||||
return z << 8 | y << 4 | x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the blocks to a buffer.
|
||||
*
|
||||
* @param output The buffer to write to.
|
||||
* @throws Exception Throws if it failed to write.
|
||||
*/
|
||||
public void writeBlocks(ByteBuf output) throws Exception {
|
||||
// Write bits per block
|
||||
int bitsPerBlock = 4;
|
||||
while (palette.size() > 1 << bitsPerBlock) {
|
||||
bitsPerBlock += 1;
|
||||
}
|
||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||
output.writeByte(bitsPerBlock);
|
||||
|
||||
// Write pallet (or not)
|
||||
Type.VAR_INT.write(output, palette.size());
|
||||
for (int mappedId : palette) {
|
||||
Type.VAR_INT.write(output, mappedId);
|
||||
}
|
||||
|
||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
||||
Type.VAR_INT.write(output, length);
|
||||
long[] data = new long[length];
|
||||
for (int index = 0; index < blocks.length; index++) {
|
||||
int value = blocks[index];
|
||||
int bitIndex = index * bitsPerBlock;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
||||
if (startIndex != endIndex) {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
||||
}
|
||||
}
|
||||
for (long l : data) {
|
||||
Type.LONG.write(output, l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the block light to a buffer
|
||||
*
|
||||
* @param output The buffer to write to
|
||||
*/
|
||||
public void writeBlockLight(ByteBuf output) {
|
||||
output.writeBytes(blockLight.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the sky light to a buffer
|
||||
*
|
||||
* @param output The buffer to write to
|
||||
*/
|
||||
public void writeSkyLight(ByteBuf output) {
|
||||
output.writeBytes(skyLight.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sky light is present
|
||||
*
|
||||
* @return True if skylight is present
|
||||
*/
|
||||
public boolean hasSkyLight() {
|
||||
return skyLight != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expected size of this chunk section.
|
||||
*
|
||||
* @return Amount of bytes sent by this section
|
||||
* @throws Exception If it failed to calculate bits properly
|
||||
*/
|
||||
public int getExpectedSize() throws Exception {
|
||||
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
|
||||
int bytes = 1; // bits per block
|
||||
bytes += paletteBytes(); // palette
|
||||
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
|
||||
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
|
||||
bytes += LIGHT_LENGTH; // block light
|
||||
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private int paletteBytes() throws Exception {
|
||||
// Count bytes used by pallet
|
||||
int bytes = countBytes(palette.size());
|
||||
for (int mappedId : palette) {
|
||||
bytes += countBytes(mappedId);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private int countBytes(int value) throws Exception {
|
||||
// Count amount of bytes that would be sent if the value were sent as a VarInt
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Type.VAR_INT.write(buf, value);
|
||||
buf.readerIndex(0);
|
||||
int bitCount = buf.readableBytes();
|
||||
buf.release();
|
||||
return bitCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NibbleArray {
|
||||
private final byte[] handle;
|
||||
|
||||
public NibbleArray(int length) {
|
||||
if (length == 0 || length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
this.handle = new byte[length / 2];
|
||||
}
|
||||
|
||||
public NibbleArray(byte[] handle) {
|
||||
if (handle.length == 0 || handle.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value at a desired X, Y, Z
|
||||
*
|
||||
* @param x Block X
|
||||
* @param y Block Y
|
||||
* @param z Block Z
|
||||
* @return The value at the given XYZ
|
||||
*/
|
||||
public byte get(int x, int y, int z) {
|
||||
return get(y << 8 | z << 4 | x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value at an index
|
||||
*
|
||||
* @param index The index to lookup
|
||||
* @return The value at that index.
|
||||
*/
|
||||
public byte get(int index) {
|
||||
byte value = handle[index / 2];
|
||||
if (index % 2 == 0) {
|
||||
return (byte) (value & 0xF);
|
||||
} else {
|
||||
return (byte) ((value >> 4) & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value based on an x, y, z
|
||||
*
|
||||
* @param x Block X
|
||||
* @param y Block Y
|
||||
* @param z Block Z
|
||||
* @param value Desired Value
|
||||
*/
|
||||
public void set(int x, int y, int z, int value) {
|
||||
set(y << 8 | z << 4 | x, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value at an index
|
||||
*
|
||||
* @param index The index to set the value at.
|
||||
* @param value The desired value
|
||||
*/
|
||||
public void set(int index, int value) {
|
||||
index /= 2;
|
||||
if (index % 2 == 0) {
|
||||
handle[index] = (byte) (handle[index] & 0xF0 | value & 0xF);
|
||||
} else {
|
||||
handle[index] = (byte) (handle[index] & 0xF | (value & 0xF) << 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of this nibble
|
||||
*
|
||||
* @return The size as an int of the nibble
|
||||
*/
|
||||
public int size() {
|
||||
return handle.length * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual number of bytes
|
||||
*
|
||||
* @return The number of bytes based on the handle.
|
||||
*/
|
||||
public int actualSize() {
|
||||
return handle.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with a value
|
||||
*
|
||||
* @param value Value to fill with
|
||||
*/
|
||||
public void fill(byte value) {
|
||||
value &= 0xF; // Max nibble size (= 16)
|
||||
Arrays.fill(handle, (byte) ((value << 4) | value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte array behind this nibble
|
||||
*
|
||||
* @return The byte array
|
||||
*/
|
||||
public byte[] getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a byte array into this nibble
|
||||
*
|
||||
* @param handle The byte array to copy in.
|
||||
*/
|
||||
public void setHandle(byte[] handle) {
|
||||
if (handle.length != this.handle.length) {
|
||||
throw new IllegalArgumentException("Length of handle must equal to size of nibble array!");
|
||||
}
|
||||
|
||||
System.arraycopy(handle, 0, this.handle, 0, handle.length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package us.myles.ViaVersion.api.minecraft.item;
|
||||
|
||||
import lombok.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class Item {
|
||||
private short id;
|
||||
private byte amount;
|
||||
private short data;
|
||||
private CompoundTag tag;
|
||||
|
||||
/**
|
||||
* Create an item from a bukkit stack (doesn't save NBT)
|
||||
*
|
||||
* @param stack The stack to convert from
|
||||
* @return The output stack
|
||||
*/
|
||||
public static Item getItem(ItemStack stack) {
|
||||
if (stack == null) return null;
|
||||
return new Item((short) stack.getTypeId(), (byte) stack.getAmount(), stack.getDurability(), null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.minecraft.metadata;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class Metadata {
|
||||
private int id;
|
||||
private int typeID;
|
||||
private Type type;
|
||||
private Object value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Metadata{" +
|
||||
"id=" + id +
|
||||
", typeID=" + typeID +
|
||||
", type=" + type +
|
||||
", value=" + value +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.remapper.PacketRemapper;
|
||||
import us.myles.ViaVersion.exception.CancelException;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class Protocol {
|
||||
private Map<Pair<State, Integer>, ProtocolPacket> incoming = new HashMap<>();
|
||||
private Map<Pair<State, Integer>, ProtocolPacket> outgoing = new HashMap<>();
|
||||
|
||||
public Protocol() {
|
||||
registerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this protocol filter an object packet from this class.
|
||||
* Default: false
|
||||
*
|
||||
* @param packetClass The class of the current input
|
||||
* @return True if it should handle the filtering
|
||||
*/
|
||||
public boolean isFiltered(Class packetClass) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a packet into the output
|
||||
*
|
||||
* @param info The current user connection
|
||||
* @param packet The input packet as an object (NMS)
|
||||
* @param output The list to put the object into.
|
||||
* @throws Exception Throws exception if cancelled / error.
|
||||
*/
|
||||
protected void filterPacket(UserConnection info, Object packet, List output) throws Exception {
|
||||
output.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listeners for this protocol
|
||||
*/
|
||||
protected void registerListeners() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the packets for this protocol
|
||||
*/
|
||||
protected abstract void registerPackets();
|
||||
|
||||
/**
|
||||
* Initialise a user for this protocol setting up objects.
|
||||
*
|
||||
* @param userConnection The user to initialise
|
||||
*/
|
||||
public abstract void init(UserConnection userConnection);
|
||||
|
||||
/**
|
||||
* Register an incoming packet, with simple id transformation.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
*/
|
||||
public void registerIncoming(State state, int oldPacketID, int newPacketID) {
|
||||
registerIncoming(state, oldPacketID, newPacketID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an incoming packet, with id transformation and remapper.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
* @param packetRemapper The remapper to use for the packet
|
||||
*/
|
||||
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||
incoming.put(new Pair<>(state, newPacketID), protocolPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an outgoing packet, with simple id transformation.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
*/
|
||||
public void registerOutgoing(State state, int oldPacketID, int newPacketID) {
|
||||
registerOutgoing(state, oldPacketID, newPacketID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an outgoing packet, with id transformation and remapper.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
* @param packetRemapper The remapper to use for the packet
|
||||
*/
|
||||
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||
outgoing.put(new Pair<>(state, oldPacketID), protocolPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a packet using this protocol
|
||||
*
|
||||
* @param direction The direction the packet is going in
|
||||
* @param state The current protocol state
|
||||
* @param packetWrapper The packet wrapper to transform
|
||||
* @throws Exception Throws exception if it fails to transform
|
||||
*/
|
||||
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
|
||||
Pair<State, Integer> statePacket = new Pair<>(state, packetWrapper.getId());
|
||||
Map<Pair<State, Integer>, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming);
|
||||
ProtocolPacket protocolPacket;
|
||||
if (packetMap.containsKey(statePacket)) {
|
||||
protocolPacket = packetMap.get(statePacket);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// write packet id
|
||||
int newID = direction == Direction.OUTGOING ? protocolPacket.getNewID() : protocolPacket.getOldID();
|
||||
packetWrapper.setId(newID);
|
||||
// remap
|
||||
if (protocolPacket.getRemapper() != null) {
|
||||
protocolPacket.getRemapper().remap(packetWrapper);
|
||||
if (packetWrapper.isCancelled())
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Protocol:" + getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
class ProtocolPacket {
|
||||
State state;
|
||||
int oldID;
|
||||
int newID;
|
||||
PacketRemapper remapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ProtocolPipeline extends Protocol {
|
||||
List<Protocol> protocolList;
|
||||
private UserConnection userConnection;
|
||||
|
||||
public ProtocolPipeline(UserConnection userConnection) {
|
||||
super();
|
||||
init(userConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
protocolList = new CopyOnWriteArrayList<>();
|
||||
// This is a pipeline so we register basic pipes
|
||||
protocolList.add(ProtocolRegistry.BASE_PROTOCOL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(UserConnection userConnection) {
|
||||
this.userConnection = userConnection;
|
||||
|
||||
ProtocolInfo protocolInfo = new ProtocolInfo(userConnection);
|
||||
protocolInfo.setPipeline(this);
|
||||
|
||||
userConnection.put(protocolInfo);
|
||||
|
||||
/* Init through all our pipes */
|
||||
for (Protocol protocol : protocolList) {
|
||||
protocol.init(userConnection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a protocol to the current pipeline
|
||||
* This will call the .init method.
|
||||
*
|
||||
* @param protocol The protocol to add to the end
|
||||
*/
|
||||
public void add(Protocol protocol) {
|
||||
if (protocolList != null) {
|
||||
protocolList.add(protocol);
|
||||
protocol.init(userConnection);
|
||||
} else {
|
||||
throw new NullPointerException("Tried to add protocol to early");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
|
||||
int originalID = packetWrapper.getId();
|
||||
List<Protocol> protocols = new ArrayList<>(protocolList);
|
||||
// Other way if outgoing
|
||||
if (direction == Direction.OUTGOING)
|
||||
Collections.reverse(protocols);
|
||||
|
||||
for (Protocol protocol : protocols) { // Copy to prevent from removal.
|
||||
protocol.transform(direction, state, packetWrapper);
|
||||
// Reset the reader for the packetWrapper (So it can be recycled across packets)
|
||||
packetWrapper.resetReader();
|
||||
}
|
||||
super.transform(direction, state, packetWrapper);
|
||||
|
||||
if (ViaVersion.getInstance().isDebug()) {
|
||||
// Debug packet
|
||||
String packet = "UNKNOWN";
|
||||
|
||||
// For 1.8/1.9 server version, eventually we'll probably get an API for this...
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL >= ProtocolVersion.v1_8.getId() &&
|
||||
ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_9_3.getId()) {
|
||||
|
||||
PacketType type;
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_8.getId()) {
|
||||
if (direction == Direction.INCOMING) {
|
||||
type = PacketType.findNewPacket(state, direction, originalID);
|
||||
} else {
|
||||
type = PacketType.findOldPacket(state, direction, originalID);
|
||||
}
|
||||
} else {
|
||||
type = PacketType.findNewPacket(state, direction, originalID);
|
||||
}
|
||||
|
||||
// Filter :) This would be not hard coded too, sorry :(
|
||||
if (type == PacketType.PLAY_CHUNK_DATA) return;
|
||||
if (type == PacketType.PLAY_TIME_UPDATE) return;
|
||||
if (type == PacketType.PLAY_KEEP_ALIVE) return;
|
||||
if (type == PacketType.PLAY_KEEP_ALIVE_REQUEST) return;
|
||||
if (type == PacketType.PLAY_ENTITY_LOOK_MOVE) return;
|
||||
if (type == PacketType.PLAY_ENTITY_LOOK) return;
|
||||
if (type == PacketType.PLAY_ENTITY_RELATIVE_MOVE) return;
|
||||
if (type == PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST) return;
|
||||
if (type == PacketType.PLAY_PLAYER_LOOK_REQUEST) return;
|
||||
if (type == PacketType.PLAY_PLAYER_POSITION_REQUEST) return;
|
||||
|
||||
packet = type.name();
|
||||
}
|
||||
String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]";
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
String actualUsername = packetWrapper.user().get(ProtocolInfo.class).getUsername();
|
||||
String username = actualUsername != null ? actualUsername + " " : "";
|
||||
|
||||
plugin.getLogger().log(Level.INFO, "{0}{1}: {2} {3} -> {4} [{5}] Value: {6}",
|
||||
new Object[]{
|
||||
username,
|
||||
direction,
|
||||
state,
|
||||
originalID,
|
||||
packetWrapper.getId(),
|
||||
name,
|
||||
packetWrapper
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the pipeline contains a protocol
|
||||
*
|
||||
* @param pipeClass The class to check
|
||||
* @return True if the protocol class is in the pipeline
|
||||
*/
|
||||
public boolean contains(Class<? extends Protocol> pipeClass) {
|
||||
for (Protocol protocol : protocolList) {
|
||||
if (protocol.getClass().equals(pipeClass)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the pipeline to filter a NMS packet
|
||||
*
|
||||
* @param o The NMS packet object
|
||||
* @param list The output list to write to
|
||||
* @return If it should not write the input object to te list.
|
||||
* @throws Exception If it failed to convert / packet cancelld.
|
||||
*/
|
||||
public boolean filter(Object o, List list) throws Exception {
|
||||
for (Protocol protocol : protocolList) {
|
||||
if (protocol.isFiltered(o.getClass())) {
|
||||
protocol.filterPacket(userConnection, o, list);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Protocol> pipes() {
|
||||
return protocolList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.Bukkit;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.protocols.base.BaseProtocol;
|
||||
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2TO1_9_3_4;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_1to1_9.Protocol1_9_1TO1_9;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3TO1_9_1_2;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_9_1.Protocol1_9TO1_9_1;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ProtocolRegistry {
|
||||
public static final Protocol BASE_PROTOCOL = new BaseProtocol();
|
||||
public static int SERVER_PROTOCOL = -1;
|
||||
// Input Version -> Output Version & Protocol (Allows fast lookup)
|
||||
private static Map<Integer, Map<Integer, Protocol>> registryMap = new ConcurrentHashMap<>();
|
||||
private static Map<Pair<Integer, Integer>, List<Pair<Integer, Protocol>>> pathCache = new ConcurrentHashMap<>();
|
||||
private static List<Protocol> registerList = Lists.newCopyOnWriteArrayList();
|
||||
private static Set<Integer> supportedVersions = Sets.newConcurrentHashSet();
|
||||
|
||||
static {
|
||||
// Base Protocol
|
||||
registerProtocol(BASE_PROTOCOL, Arrays.<Integer>asList(), -1);
|
||||
// Register built in protocols
|
||||
registerProtocol(new Protocol1_9TO1_8(), Collections.singletonList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_8.getId());
|
||||
registerProtocol(new Protocol1_9_1TO1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9.getId());
|
||||
registerProtocol(new Protocol1_9_3TO1_9_1_2(), Arrays.asList(ProtocolVersion.v1_9_3.getId()), ProtocolVersion.v1_9_2.getId());
|
||||
// Only supported for 1.9.4 server to 1.9 (nothing else)
|
||||
registerProtocol(new Protocol1_9TO1_9_1(), Arrays.asList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_9_2.getId());
|
||||
registerProtocol(new Protocol1_9_1_2TO1_9_3_4(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9_3.getId());
|
||||
registerProtocol(new Protocol1_10To1_9_3_4(), Collections.singletonList(ProtocolVersion.v1_10.getId()), ProtocolVersion.v1_9_3.getId());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a protocol
|
||||
*
|
||||
* @param protocol The protocol to register.
|
||||
* @param supported Supported client versions.
|
||||
* @param output The output server version it converts to.
|
||||
*/
|
||||
public static void registerProtocol(Protocol protocol, List<Integer> supported, Integer output) {
|
||||
// Clear cache as this may make new routes.
|
||||
if (pathCache.size() > 0)
|
||||
pathCache.clear();
|
||||
|
||||
for (Integer version : supported) {
|
||||
if (!registryMap.containsKey(version)) {
|
||||
registryMap.put(version, new HashMap<Integer, Protocol>());
|
||||
}
|
||||
|
||||
registryMap.get(version).put(output, protocol);
|
||||
}
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("ViaVersion").isEnabled()) {
|
||||
protocol.registerListeners();
|
||||
refreshVersions();
|
||||
} else {
|
||||
registerList.add(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public static void refreshVersions() {
|
||||
supportedVersions.clear();
|
||||
|
||||
supportedVersions.add(ProtocolRegistry.SERVER_PROTOCOL);
|
||||
for (ProtocolVersion versions : ProtocolVersion.getProtocols()) {
|
||||
List<Pair<Integer, Protocol>> paths = getProtocolPath(versions.getId(), ProtocolRegistry.SERVER_PROTOCOL);
|
||||
if (paths == null) continue;
|
||||
supportedVersions.add(versions.getId());
|
||||
for (Pair<Integer, Protocol> path : paths)
|
||||
supportedVersions.add(path.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
public static SortedSet<Integer> getSupportedVersions() {
|
||||
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this plugin is useful to the server.
|
||||
*
|
||||
* @return True if there is a useful pipe
|
||||
*/
|
||||
public static boolean isWorkingPipe() {
|
||||
for (Map<Integer, Protocol> maps : registryMap.values()) {
|
||||
if (maps.containsKey(SERVER_PROTOCOL)) return true;
|
||||
}
|
||||
return false; // No destination for protocol
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server is enabled, to register any non registered listeners.
|
||||
*/
|
||||
public static void registerListeners() {
|
||||
for (Protocol protocol : registerList) {
|
||||
protocol.registerListeners();
|
||||
}
|
||||
registerList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a path to get from an input protocol to the servers protocol.
|
||||
*
|
||||
* @param current The current items in the path
|
||||
* @param clientVersion The current input version
|
||||
* @param serverVersion The desired output version
|
||||
* @return The path which has been generated, null if failed.
|
||||
*/
|
||||
private static List<Pair<Integer, Protocol>> getProtocolPath(List<Pair<Integer, Protocol>> current, int clientVersion, int serverVersion) {
|
||||
if (clientVersion == serverVersion) return null; // We're already there
|
||||
if (current.size() > 50) return null; // Fail safe, protocol too complicated.
|
||||
|
||||
// First check if there is any protocols for this
|
||||
if (!registryMap.containsKey(clientVersion)) {
|
||||
return null; // Not supported
|
||||
}
|
||||
// Next check there isn't an obvious path
|
||||
Map<Integer, Protocol> inputMap = registryMap.get(clientVersion);
|
||||
if (inputMap.containsKey(serverVersion)) {
|
||||
current.add(new Pair<>(serverVersion, inputMap.get(serverVersion)));
|
||||
return current; // Easy solution
|
||||
}
|
||||
// There might be a more advanced solution... So we'll see if any of the others can get us there
|
||||
List<Pair<Integer, Protocol>> shortest = null;
|
||||
|
||||
for (Map.Entry<Integer, Protocol> entry : inputMap.entrySet()) {
|
||||
// Ensure it wasn't caught by the other loop
|
||||
if (!entry.getKey().equals(serverVersion)) {
|
||||
Pair<Integer, Protocol> pair = new Pair<>(entry.getKey(), entry.getValue());
|
||||
// Ensure no recursion
|
||||
if (!current.contains(pair)) {
|
||||
// Create a copy
|
||||
List<Pair<Integer, Protocol>> newCurrent = new ArrayList<>(current);
|
||||
newCurrent.add(pair);
|
||||
// Calculate the rest of the protocol using the current
|
||||
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
|
||||
if (newCurrent != null) {
|
||||
// If it's shorter then choose it
|
||||
if (shortest == null || shortest.size() > newCurrent.size()) {
|
||||
shortest = newCurrent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shortest; // null if none found
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a path from a client version to server version
|
||||
*
|
||||
* @param clientVersion The input client version
|
||||
* @param serverVersion The desired output server version
|
||||
* @return The path it generated, null if it failed.
|
||||
*/
|
||||
public static List<Pair<Integer, Protocol>> getProtocolPath(int clientVersion, int serverVersion) {
|
||||
Pair<Integer, Integer> protocolKey = new Pair<>(clientVersion, serverVersion);
|
||||
// Check cache
|
||||
if (pathCache.containsKey(protocolKey)) {
|
||||
return pathCache.get(protocolKey);
|
||||
}
|
||||
// Generate path
|
||||
List<Pair<Integer, Protocol>> outputPath = getProtocolPath(new ArrayList<Pair<Integer, Protocol>>(), clientVersion, serverVersion);
|
||||
// If it found a path, cache it.
|
||||
if (outputPath != null) {
|
||||
pathCache.put(protocolKey, outputPath);
|
||||
}
|
||||
return outputPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Data
|
||||
public class ProtocolVersion {
|
||||
private static final Map<Integer, ProtocolVersion> versions = new HashMap<>();
|
||||
|
||||
public static final ProtocolVersion v1_7_1;
|
||||
public static final ProtocolVersion v1_7_6;
|
||||
public static final ProtocolVersion v1_8;
|
||||
public static final ProtocolVersion v1_9;
|
||||
public static final ProtocolVersion v1_9_1;
|
||||
public static final ProtocolVersion v1_9_2;
|
||||
public static final ProtocolVersion v1_9_3;
|
||||
public static final ProtocolVersion v1_10;
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
|
||||
static {
|
||||
register(v1_7_1 = new ProtocolVersion(4, "1.7-1.7.5"));
|
||||
register(v1_7_6 = new ProtocolVersion(5, "1.7.6-1.7.10"));
|
||||
register(v1_8 = new ProtocolVersion(47, "1.8.x"));
|
||||
register(v1_9 = new ProtocolVersion(107, "1.9"));
|
||||
register(v1_9_1 = new ProtocolVersion(108, "1.9.1"));
|
||||
register(v1_9_2 = new ProtocolVersion(109, "1.9.2"));
|
||||
register(v1_9_3 = new ProtocolVersion(110, "1.9.3/4"));
|
||||
register(v1_10 = new ProtocolVersion(210, "1.10"));
|
||||
}
|
||||
|
||||
public static void register(@NonNull ProtocolVersion protocol) {
|
||||
versions.put(protocol.getId(), protocol);
|
||||
}
|
||||
|
||||
public static boolean isRegistered(int id) {
|
||||
return versions.containsKey(id);
|
||||
}
|
||||
|
||||
public static ProtocolVersion getProtocol(int id) {
|
||||
if (versions.containsKey(id)) {
|
||||
return versions.get(id);
|
||||
} else {
|
||||
return new ProtocolVersion(id, "Unknown (" + id + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ProtocolVersion> getProtocols() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(versions.values()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class PacketHandler implements ValueWriter {
|
||||
/**
|
||||
* Handle a packet
|
||||
*
|
||||
* @param wrapper The associated wrapper
|
||||
* @throws Exception Throws exception if it failed to handle the packet
|
||||
*/
|
||||
public abstract void handle(PacketWrapper wrapper) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, Object inputValue) throws Exception {
|
||||
try {
|
||||
handle(writer);
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PacketRemapper {
|
||||
private List<Pair<ValueReader, ValueWriter>> valueRemappers = new ArrayList<>();
|
||||
|
||||
public PacketRemapper() {
|
||||
registerMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type to the same type.
|
||||
*
|
||||
* @param type Type to map
|
||||
*/
|
||||
public void map(Type type) {
|
||||
TypeRemapper remapper = new TypeRemapper(type);
|
||||
map(remapper, remapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type from an old type to a new type
|
||||
*
|
||||
* @param oldType The old type
|
||||
* @param newType The new type
|
||||
*/
|
||||
public void map(Type oldType, Type newType) {
|
||||
map(new TypeRemapper(oldType), new TypeRemapper(newType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type from an old type to a transformed new type.
|
||||
*
|
||||
* @param oldType The old type
|
||||
* @param <T1> The old return type.
|
||||
* @param transformer The transformer to use to produce the new type.
|
||||
* @param <T2> The new return type.
|
||||
*/
|
||||
public <T1, T2> void map(Type<T1> oldType, ValueTransformer<T1, T2> transformer) {
|
||||
map(new TypeRemapper(oldType), transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type using a basic ValueReader to a ValueWriter
|
||||
*
|
||||
* @param inputReader The reader to read with.
|
||||
* @param outputWriter The writer to write with
|
||||
* @param <T> The return type
|
||||
*/
|
||||
public <T> void map(ValueReader<T> inputReader, ValueWriter<T> outputWriter) {
|
||||
valueRemappers.add(new Pair<ValueReader, ValueWriter>(inputReader, outputWriter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a value
|
||||
*
|
||||
* @param creator The creator to used to make the value(s).
|
||||
*/
|
||||
public void create(ValueCreator creator) {
|
||||
map(new TypeRemapper(Type.NOTHING), creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler
|
||||
*
|
||||
* @param handler The handler to use to handle the current packet.
|
||||
*/
|
||||
public void handler(PacketHandler handler) {
|
||||
map(new TypeRemapper(Type.NOTHING), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the mappings for this packet
|
||||
*/
|
||||
public abstract void registerMap();
|
||||
|
||||
/**
|
||||
* Remap a packet wrapper
|
||||
*
|
||||
* @param packetWrapper The wrapper to remap
|
||||
* @throws Exception Throws if it fails to write / read to the packet.
|
||||
*/
|
||||
public void remap(PacketWrapper packetWrapper) throws Exception {
|
||||
try {
|
||||
// Read all the current values
|
||||
for (Pair<ValueReader, ValueWriter> valueRemapper : valueRemappers) {
|
||||
Object object = valueRemapper.getKey().read(packetWrapper);
|
||||
// Convert object to write type :O!!!
|
||||
valueRemapper.getValue().write(packetWrapper, object);
|
||||
}
|
||||
// If we had handlers we'd put them here
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class TypeRemapper<T> implements ValueReader<T>, ValueWriter<T> {
|
||||
private final Type<T> type;
|
||||
|
||||
public TypeRemapper(Type<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(PacketWrapper wrapper) throws Exception {
|
||||
return wrapper.read(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper output, T inputValue) {
|
||||
output.write(type, inputValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class ValueCreator implements ValueWriter {
|
||||
/**
|
||||
* Write new values to a Packet.
|
||||
*
|
||||
* @param wrapper The packet to write to
|
||||
* @throws Exception Throws exception if it fails to write.
|
||||
*/
|
||||
public abstract void write(PacketWrapper wrapper) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, Object inputValue) throws Exception {
|
||||
try {
|
||||
write(writer);
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
|
||||
public interface ValueReader<T> {
|
||||
/**
|
||||
* Reads value from a PacketWrapper
|
||||
*
|
||||
* @param wrapper The wrapper to read from
|
||||
* @return Returns the desired type
|
||||
* @throws Exception Throws exception if it fails to read
|
||||
*/
|
||||
T read(PacketWrapper wrapper) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class ValueTransformer<T1, T2> implements ValueWriter<T1> {
|
||||
private final Type<T2> outputType;
|
||||
|
||||
public ValueTransformer(Type<T2> outputType) {
|
||||
this.outputType = outputType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a value from one type to another
|
||||
*
|
||||
* @param wrapper The current packet
|
||||
* @param inputValue The input value
|
||||
* @return The value to write to the wrapper
|
||||
* @throws Exception Throws exception if it fails to transform a value
|
||||
*/
|
||||
public abstract T2 transform(PacketWrapper wrapper, T1 inputValue) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, T1 inputValue) throws Exception {
|
||||
try {
|
||||
writer.write(outputType, transform(writer, inputValue));
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
|
||||
public interface ValueWriter<T> {
|
||||
/**
|
||||
* Write a value to a packet
|
||||
*
|
||||
* @param writer The packet wrapper to write to
|
||||
* @param inputValue The value to write
|
||||
* @throws Exception Throws exception if it fails to write
|
||||
*/
|
||||
void write(PacketWrapper writer, T inputValue) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface ByteBufReader<T> {
|
||||
/**
|
||||
* Read a value from a ByteBuf
|
||||
*
|
||||
* @param buffer The buffer to read from.
|
||||
* @return The type based on the class type.
|
||||
* @throws Exception Throws exception if it failed reading.
|
||||
*/
|
||||
T read(ByteBuf buffer) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface ByteBufWriter<T> {
|
||||
/**
|
||||
* Write an object to a type to a ByteBuf
|
||||
*
|
||||
* @param buffer The buffer to write to
|
||||
* @param object The object to write
|
||||
* @throws Exception Throws if it failed to write
|
||||
*/
|
||||
void write(ByteBuf buffer, T object) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public abstract class PartialType<T, X> extends Type<T> {
|
||||
private final X param;
|
||||
|
||||
public PartialType(X param, Class<T> type) {
|
||||
super(type);
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public abstract T read(ByteBuf buffer, X param) throws Exception;
|
||||
|
||||
public abstract void write(ByteBuf buffer, X param, T object) throws Exception;
|
||||
|
||||
@Override
|
||||
public T read(ByteBuf buffer) throws Exception {
|
||||
return read(buffer, this.param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, T object) throws Exception {
|
||||
write(buffer, this.param, object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.types.*;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
|
||||
/* Defined Types */
|
||||
public static final Type<Byte> BYTE = new ByteType();
|
||||
public static final Type<Byte[]> BYTE_ARRAY = new ArrayType<>(Type.BYTE);
|
||||
|
||||
public static final Type<byte[]> REMAINING_BYTES = new RemainingBytesType();
|
||||
|
||||
public static final Type<Short> UNSIGNED_BYTE = new UnsignedByteType();
|
||||
public static final Type<Short[]> UNSIGNED_BYTE_ARRAY = new ArrayType<>(Type.UNSIGNED_BYTE);
|
||||
|
||||
public static final Type<Boolean> BOOLEAN = new BooleanType();
|
||||
public static final Type<Boolean[]> BOOLEAN_ARRAY = new ArrayType<>(Type.BOOLEAN);
|
||||
/* Number Types */
|
||||
public static final Type<Integer> INT = new IntType();
|
||||
public static final Type<Integer[]> INT_ARRAY = new ArrayType<>(Type.INT);
|
||||
|
||||
public static final Type<Double> DOUBLE = new DoubleType();
|
||||
public static final Type<Double[]> DOUBLE_ARRAY = new ArrayType<>(Type.DOUBLE);
|
||||
|
||||
public static final Type<Long> LONG = new LongType();
|
||||
public static final Type<Long[]> LONG_ARRAY = new ArrayType<>(Type.LONG);
|
||||
|
||||
public static final Type<Float> FLOAT = new FloatType();
|
||||
public static final Type<Float[]> FLOAT_ARRAY = new ArrayType<>(Type.FLOAT);
|
||||
|
||||
public static final Type<Short> SHORT = new ShortType();
|
||||
public static final Type<Short[]> SHORT_ARRAY = new ArrayType<>(Type.SHORT);
|
||||
|
||||
public static final Type<Integer> UNSIGNED_SHORT = new UnsignedShortType();
|
||||
public static final Type<Integer[]> UNSIGNED_SHORT_ARRAY = new ArrayType<>(Type.UNSIGNED_SHORT);
|
||||
/* Other Types */
|
||||
public static final Type<String> STRING = new StringType();
|
||||
public static final Type<String[]> STRING_ARRAY = new ArrayType<>(Type.STRING);
|
||||
|
||||
public static final Type<UUID> UUID = new UUIDType();
|
||||
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Type.UUID);
|
||||
/* Variable Types */
|
||||
public static final Type<Integer> VAR_INT = new VarIntType();
|
||||
public static final Type<Integer[]> VAR_INT_ARRAY = new ArrayType<>(Type.VAR_INT);
|
||||
/* Special Types */
|
||||
public static final Type<Void> NOTHING = new VoidType(); // This is purely used for remapping.
|
||||
/* MC Types */
|
||||
public static final Type<Position> POSITION = new PositionType();
|
||||
public static final Type<EulerAngle> ROTATION = new EulerAngleType();
|
||||
public static final Type<Vector> VECTOR = new VectorType();
|
||||
public static final Type<CompoundTag> NBT = new NBTType();
|
||||
|
||||
public static final Type<UUID> OPTIONAL_UUID = new OptUUIDType();
|
||||
public static final Type<Position> OPTIONAL_POSITION = new OptPositionType();
|
||||
|
||||
public static final Type<Item> ITEM = new ItemType();
|
||||
public static final Type<Item[]> ITEM_ARRAY = new ItemArrayType();
|
||||
/* Actual Class */
|
||||
|
||||
private final Class<? super T> outputClass;
|
||||
private final Set<Class<? extends Type>> compatibilities;
|
||||
private final String typeName;
|
||||
|
||||
public Type(Class<? super T> outputClass) {
|
||||
this(outputClass.getSimpleName(), outputClass);
|
||||
}
|
||||
|
||||
public Type(String typeName, Class<? super T> outputClass) {
|
||||
this.outputClass = outputClass;
|
||||
this.typeName = typeName;
|
||||
this.compatibilities = new HashSet<>();
|
||||
}
|
||||
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return this.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Type|" + getTypeName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
public interface TypeConverter<T> {
|
||||
/**
|
||||
* Convert from a type to the current type
|
||||
*
|
||||
* @param o The input object
|
||||
* @return The converted type as an object
|
||||
*/
|
||||
T from(Object o);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
public class ArrayType<T> extends Type<T[]> {
|
||||
private final Type<T> elementType;
|
||||
|
||||
public ArrayType(Type<T> type) {
|
||||
super(type.getTypeName() + " Array", (Class<T[]>) getArrayClass(type.getOutputClass()));
|
||||
this.elementType = type;
|
||||
}
|
||||
|
||||
/* Taken from http://stackoverflow.com/questions/4901128/obtaining-the-array-class-of-a-component-type */
|
||||
public static Class<?> getArrayClass(Class<?> componentType) {
|
||||
ClassLoader classLoader = componentType.getClassLoader();
|
||||
String name;
|
||||
if (componentType.isArray()) {
|
||||
// just add a leading "["
|
||||
name = "[" + componentType.getName();
|
||||
} else if (componentType == boolean.class) {
|
||||
name = "[Z";
|
||||
} else if (componentType == byte.class) {
|
||||
name = "[B";
|
||||
} else if (componentType == char.class) {
|
||||
name = "[C";
|
||||
} else if (componentType == double.class) {
|
||||
name = "[D";
|
||||
} else if (componentType == float.class) {
|
||||
name = "[F";
|
||||
} else if (componentType == int.class) {
|
||||
name = "[I";
|
||||
} else if (componentType == long.class) {
|
||||
name = "[J";
|
||||
} else if (componentType == short.class) {
|
||||
name = "[S";
|
||||
} else {
|
||||
// must be an object non-array class
|
||||
name = "[L" + componentType.getName() + ";";
|
||||
}
|
||||
try {
|
||||
return classLoader != null ? classLoader.loadClass(name) : Class.forName(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null; // oh
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] read(ByteBuf buffer) throws Exception {
|
||||
int amount = Type.VAR_INT.read(buffer);
|
||||
T[] array = (T[]) Array.newInstance(elementType.getOutputClass(), amount);
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
array[i] = elementType.read(buffer);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, T[] object) throws Exception {
|
||||
Type.VAR_INT.write(buffer, object.length);
|
||||
for (T o : object) {
|
||||
elementType.write(buffer, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class BooleanType extends Type<Boolean> implements TypeConverter<Boolean> {
|
||||
public BooleanType() {
|
||||
super(Boolean.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean read(ByteBuf buffer) {
|
||||
return buffer.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Boolean object) {
|
||||
buffer.writeBoolean(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue() == 1;
|
||||
}
|
||||
return (Boolean) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class ByteType extends Type<Byte> implements TypeConverter<Byte> {
|
||||
public ByteType() {
|
||||
super(Byte.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte read(ByteBuf buffer) {
|
||||
return buffer.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Byte object) {
|
||||
buffer.writeByte(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Byte from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).byteValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return (Boolean) o ? (byte) 1 : 0;
|
||||
}
|
||||
return (Byte) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class DoubleType extends Type<Double> implements TypeConverter<Double> {
|
||||
public DoubleType() {
|
||||
super(Double.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double read(ByteBuf buffer) {
|
||||
return buffer.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Double object) {
|
||||
buffer.writeDouble(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).doubleValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return (Boolean) o ? (byte) 1D : 0D;
|
||||
}
|
||||
return (Double) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class FloatType extends Type<Float> implements TypeConverter<Float> {
|
||||
public FloatType() {
|
||||
super(Float.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float read(ByteBuf buffer) {
|
||||
return buffer.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Float object) {
|
||||
buffer.writeFloat(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Float from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).floatValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1F : 0;
|
||||
}
|
||||
return (Float) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class IntType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
public IntType() {
|
||||
super(Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
return buffer.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
buffer.writeInt(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class LongType extends Type<Long> implements TypeConverter<Long> {
|
||||
public LongType() {
|
||||
super(Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long read(ByteBuf buffer) {
|
||||
return buffer.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Long object) {
|
||||
buffer.writeLong(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).longValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1L : 0;
|
||||
}
|
||||
return (Long) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class RemainingBytesType extends Type<byte[]> {
|
||||
public RemainingBytesType() {
|
||||
super(byte[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] read(ByteBuf buffer) {
|
||||
byte[] array = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, byte[] object) {
|
||||
buffer.writeBytes(object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class ShortType extends Type<Short> implements TypeConverter<Short> {
|
||||
public ShortType() {
|
||||
super(Short.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short read(ByteBuf buffer) {
|
||||
return buffer.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Short object) {
|
||||
buffer.writeShort(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).shortValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? (short) 1 : 0;
|
||||
}
|
||||
return (short) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class StringType extends Type<String> {
|
||||
public StringType() {
|
||||
super(String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String read(ByteBuf buffer) throws Exception {
|
||||
int len = Type.VAR_INT.read(buffer);
|
||||
Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len);
|
||||
|
||||
byte[] b = new byte[len];
|
||||
buffer.readBytes(b);
|
||||
|
||||
return new String(b, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, String object) throws Exception {
|
||||
Preconditions.checkArgument(object.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", object.length());
|
||||
|
||||
byte[] b = object.getBytes(Charsets.UTF_8);
|
||||
Type.VAR_INT.write(buffer, b.length);
|
||||
buffer.writeBytes(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UUIDType extends Type<UUID> {
|
||||
public UUIDType() {
|
||||
super(UUID.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID read(ByteBuf buffer) {
|
||||
return new UUID(buffer.readLong(), buffer.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, UUID object) {
|
||||
buffer.writeLong(object.getMostSignificantBits());
|
||||
buffer.writeLong(object.getLeastSignificantBits());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class UnsignedByteType extends Type<Short> implements TypeConverter<Short> {
|
||||
public UnsignedByteType() {
|
||||
super(Short.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short read(ByteBuf buffer) {
|
||||
return buffer.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Short object) {
|
||||
buffer.writeByte(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).shortValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? (short) 1 : 0;
|
||||
}
|
||||
return (short) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class UnsignedShortType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
public UnsignedShortType() {
|
||||
super(Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
return buffer.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
buffer.writeShort(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class VarIntType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
|
||||
public VarIntType() {
|
||||
super("VarInt", Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
int part;
|
||||
while (true) {
|
||||
part = object & 0x7F;
|
||||
|
||||
object >>>= 7;
|
||||
if (object != 0) {
|
||||
part |= 0x80;
|
||||
}
|
||||
|
||||
buffer.writeByte(part);
|
||||
|
||||
if (object == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
int out = 0;
|
||||
int bytes = 0;
|
||||
byte in;
|
||||
while (true) {
|
||||
in = buffer.readByte();
|
||||
|
||||
out |= (in & 0x7F) << (bytes++ * 7);
|
||||
|
||||
if (bytes > 5) { // 5 is maxBytes
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
|
||||
if ((in & 0x80) != 0x80) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class VoidType extends Type<Void> implements TypeConverter<Void> {
|
||||
public VoidType() {
|
||||
super(Void.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void read(ByteBuf buffer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Void object) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void from(Object o) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class EulerAngleType extends Type<EulerAngle> {
|
||||
public EulerAngleType() {
|
||||
super(EulerAngle.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EulerAngle read(ByteBuf buffer) throws Exception {
|
||||
float x = Type.FLOAT.read(buffer);
|
||||
float y = Type.FLOAT.read(buffer);
|
||||
float z = Type.FLOAT.read(buffer);
|
||||
|
||||
return new EulerAngle(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, EulerAngle object) throws Exception {
|
||||
Type.FLOAT.write(buffer, (float) object.getX());
|
||||
Type.FLOAT.write(buffer, (float) object.getY());
|
||||
Type.FLOAT.write(buffer, (float) object.getZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class ItemArrayType extends Type<Item[]> {
|
||||
|
||||
public ItemArrayType() {
|
||||
super("Item Array", Item[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item[] read(ByteBuf buffer) throws Exception {
|
||||
int amount = Type.SHORT.read(buffer);
|
||||
Item[] array = new Item[amount];
|
||||
for (int i = 0; i < amount; i++) {
|
||||
array[i] = Type.ITEM.read(buffer);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Item[] object) throws Exception {
|
||||
Type.SHORT.write(buffer, (short) object.length);
|
||||
for (Item o : object) {
|
||||
Type.ITEM.write(buffer, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class ItemType extends Type<Item> {
|
||||
public ItemType() {
|
||||
super(Item.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item read(ByteBuf buffer) throws Exception {
|
||||
short id = buffer.readShort();
|
||||
if (id < 0) {
|
||||
return null;
|
||||
} else {
|
||||
Item item = new Item();
|
||||
item.setId(id);
|
||||
item.setAmount(buffer.readByte());
|
||||
item.setData(buffer.readShort());
|
||||
item.setTag(Type.NBT.read(buffer));
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Item object) throws Exception {
|
||||
if (object == null) {
|
||||
buffer.writeShort(-1);
|
||||
} else {
|
||||
buffer.writeShort(object.getId());
|
||||
buffer.writeByte(object.getAmount());
|
||||
buffer.writeShort(object.getData());
|
||||
Type.NBT.write(buffer, object.getTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MetaListTypeTemplate extends Type<List<Metadata>> {
|
||||
public MetaListTypeTemplate() {
|
||||
super("MetaData List", List.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return MetaListTypeTemplate.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public abstract class MetaTypeTemplate extends Type<Metadata> {
|
||||
public MetaTypeTemplate() {
|
||||
super("Metadata type", Metadata.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return MetaTypeTemplate.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import org.spacehq.opennbt.NBTIO;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
public class NBTType extends Type<CompoundTag> {
|
||||
public NBTType() {
|
||||
super(CompoundTag.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag read(ByteBuf buffer) throws Exception {
|
||||
Preconditions.checkArgument(buffer.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", buffer.readableBytes());
|
||||
|
||||
int readerIndex = buffer.readerIndex();
|
||||
byte b = buffer.readByte();
|
||||
if (b == 0) {
|
||||
return null;
|
||||
} else {
|
||||
buffer.readerIndex(readerIndex);
|
||||
ByteBufInputStream bytebufStream = new ByteBufInputStream(buffer);
|
||||
try (DataInputStream dataInputStream = new DataInputStream(bytebufStream)) {
|
||||
return (CompoundTag) NBTIO.readTag(dataInputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, CompoundTag object) throws Exception {
|
||||
if (object == null) {
|
||||
buffer.writeByte(0);
|
||||
} else {
|
||||
ByteBufOutputStream bytebufStream = new ByteBufOutputStream(buffer);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(bytebufStream);
|
||||
|
||||
NBTIO.writeTag(dataOutputStream, object);
|
||||
|
||||
dataOutputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class OptPositionType extends Type<Position> {
|
||||
public OptPositionType() {
|
||||
super(Position.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position read(ByteBuf buffer) throws Exception {
|
||||
boolean present = buffer.readBoolean();
|
||||
if (!present) return null;
|
||||
return Type.POSITION.read(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Position object) throws Exception {
|
||||
buffer.writeBoolean(object != null);
|
||||
if (object != null)
|
||||
Type.POSITION.write(buffer, object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class OptUUIDType extends Type<UUID> {
|
||||
public OptUUIDType() {
|
||||
super(UUID.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID read(ByteBuf buffer) {
|
||||
boolean present = buffer.readBoolean();
|
||||
if (!present) return null;
|
||||
return new UUID(buffer.readLong(), buffer.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, UUID object) {
|
||||
if (object == null) {
|
||||
buffer.writeBoolean(false);
|
||||
} else {
|
||||
buffer.writeBoolean(true);
|
||||
buffer.writeLong(object.getMostSignificantBits());
|
||||
buffer.writeLong(object.getLeastSignificantBits());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class PositionType extends Type<Position> {
|
||||
public PositionType() {
|
||||
super(Position.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position read(ByteBuf buffer) {
|
||||
long val = buffer.readLong();
|
||||
long x = (val >> 38); // signed
|
||||
long y = (val >> 26) & 0xfff; // unsigned
|
||||
// this shifting madness is used to preserve sign
|
||||
long z = (val << 38) >> 38; // signed
|
||||
|
||||
return new Position(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Position object) {
|
||||
buffer.writeLong(((object.getX() & 0x3ffffff) << 38) | ((object.getY() & 0xfff) << 26) | (object.getZ() & 0x3ffffff));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.bukkit.util.Vector;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class VectorType extends Type<Vector> {
|
||||
public VectorType() {
|
||||
super(Vector.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector read(ByteBuf buffer) throws Exception {
|
||||
int x = Type.INT.read(buffer);
|
||||
int y = Type.INT.read(buffer);
|
||||
int z = Type.INT.read(buffer);
|
||||
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Vector object) throws Exception {
|
||||
Type.INT.write(buffer, object.getBlockX());
|
||||
Type.INT.write(buffer, object.getBlockY());
|
||||
Type.INT.write(buffer, object.getBlockZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package us.myles.ViaVersion.boss;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossFlag;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Getter
|
||||
public class ViaBossBar implements BossBar {
|
||||
private UUID uuid;
|
||||
private String title;
|
||||
private float health;
|
||||
private BossColor color;
|
||||
private BossStyle style;
|
||||
private Set<UUID> players;
|
||||
private boolean visible;
|
||||
private Set<BossFlag> flags;
|
||||
|
||||
public ViaBossBar(String title, float health, BossColor color, BossStyle style) {
|
||||
Validate.notNull(title, "Title cannot be null");
|
||||
Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1");
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.title = title;
|
||||
this.health = health;
|
||||
this.color = color == null ? BossColor.PURPLE : color;
|
||||
this.style = style == null ? BossStyle.SOLID : style;
|
||||
this.players = new HashSet<>();
|
||||
this.flags = new HashSet<>();
|
||||
visible = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar setTitle(@NonNull String title) {
|
||||
this.title = title;
|
||||
sendPacket(UpdateAction.UPDATE_TITLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar setHealth(float health) {
|
||||
Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1");
|
||||
this.health = health;
|
||||
sendPacket(UpdateAction.UPDATE_HEALTH);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar setColor(@NonNull BossColor color) {
|
||||
this.color = color;
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar setStyle(@NonNull BossStyle style) {
|
||||
this.style = style;
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addPlayer(@NonNull Player player) {
|
||||
return addPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addPlayer(UUID player) {
|
||||
if (!players.contains(player)) {
|
||||
players.add(player);
|
||||
if (visible)
|
||||
sendPacket(player, getPacket(UpdateAction.ADD));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar removePlayer(@NonNull Player player) {
|
||||
if (players.contains(player.getUniqueId())) {
|
||||
players.remove(player.getUniqueId());
|
||||
sendPacket(player.getUniqueId(), getPacket(UpdateAction.REMOVE));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addPlayers(@NonNull Player... players) {
|
||||
for (Player p : players)
|
||||
addPlayer(p);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addFlag(@NonNull BossFlag flag) {
|
||||
if (!hasFlag(flag))
|
||||
flags.add(flag);
|
||||
sendPacket(UpdateAction.UPDATE_FLAGS);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar removeFlag(@NonNull BossFlag flag) {
|
||||
if (hasFlag(flag))
|
||||
flags.remove(flag);
|
||||
sendPacket(UpdateAction.UPDATE_FLAGS);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFlag(@NonNull BossFlag flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getPlayers() {
|
||||
return Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar show() {
|
||||
setVisible(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar hide() {
|
||||
setVisible(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
private void setVisible(boolean value) {
|
||||
if (visible != value) {
|
||||
visible = value;
|
||||
sendPacket(value ? UpdateAction.ADD : UpdateAction.REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPacket(UpdateAction action) {
|
||||
for (UUID uuid : new ArrayList<>(players)) {
|
||||
ByteBuf buf = getPacket(action);
|
||||
sendPacket(uuid, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPacket(UUID uuid, ByteBuf buf) {
|
||||
if (!ViaVersion.getInstance().isPorted(uuid) || !(ViaVersion.getInstance().getPlayerVersion(uuid) >= ProtocolVersion.v1_9.getId())) {
|
||||
players.remove(uuid);
|
||||
buf.release();
|
||||
return;
|
||||
}
|
||||
ViaVersion.getInstance().sendRawPacket(uuid, buf);
|
||||
}
|
||||
|
||||
private ByteBuf getPacket(UpdateAction action) {
|
||||
try {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Type.VAR_INT.write(buf, 0x0C); // Boss bar packet
|
||||
Type.UUID.write(buf, uuid);
|
||||
Type.VAR_INT.write(buf, action.getId());
|
||||
switch (action) {
|
||||
case ADD:
|
||||
Type.STRING.write(buf, fixJson(title));
|
||||
buf.writeFloat(health);
|
||||
Type.VAR_INT.write(buf, color.getId());
|
||||
Type.VAR_INT.write(buf, style.getId());
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
case REMOVE:
|
||||
break;
|
||||
case UPDATE_HEALTH:
|
||||
buf.writeFloat(health);
|
||||
break;
|
||||
case UPDATE_TITLE:
|
||||
Type.STRING.write(buf, fixJson(title));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
Type.VAR_INT.write(buf, color.getId());
|
||||
Type.VAR_INT.write(buf, style.getId());
|
||||
break;
|
||||
case UPDATE_FLAGS:
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
}
|
||||
|
||||
return buf;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int flagToBytes() {
|
||||
int bitmask = 0;
|
||||
for (BossFlag flag : flags)
|
||||
bitmask |= flag.getId();
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
private String fixJson(String text) {
|
||||
return Protocol1_9TO1_8.fixJson(text);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
private enum UpdateAction {
|
||||
ADD(0),
|
||||
REMOVE(1),
|
||||
UPDATE_HEALTH(2),
|
||||
UPDATE_TITLE(3),
|
||||
UPDATE_STYLE(4),
|
||||
UPDATE_FLAGS(5);
|
||||
|
||||
private final int id;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class ByteWriter {
|
||||
private final byte[] bytes;
|
||||
private int byteIndex;
|
||||
private int bitIndex;
|
||||
|
||||
public ByteWriter(int size) {
|
||||
this.bytes = new byte[size];
|
||||
this.byteIndex = 0;
|
||||
this.bitIndex = 0;
|
||||
}
|
||||
|
||||
public byte[] getOutput() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
public void writeFullByte(int b){
|
||||
writeByte(b, 8);
|
||||
}
|
||||
|
||||
public void writeByte(int b, int length) {
|
||||
byte current = getCurrentByte();
|
||||
byte byteB = (byte)(int)((b) & 0xff);
|
||||
System.out.println("Input: " + byteB);
|
||||
System.out.println(Integer.toBinaryString(byteB));
|
||||
|
||||
int space = (8 - bitIndex);
|
||||
int written = space > length ? length : space;
|
||||
System.out.println("Written is " + written);
|
||||
bytes[byteIndex] = (byte) (current | (extractRange(byteB, 0, written) >> (bitIndex - 1)));
|
||||
System.out.println("output value: " + bytes[byteIndex]);
|
||||
this.bitIndex += length;
|
||||
if(this.bitIndex >= 8) {
|
||||
this.byteIndex += 1;
|
||||
this.bitIndex = bitIndex - 8;
|
||||
// write remaining into this
|
||||
System.out.println("Writing from " + written + " to " + (written + bitIndex));
|
||||
System.out.println("Value: " + extractRange(byteB, written, written + bitIndex));
|
||||
bytes[byteIndex] = (byte) (extractRange(byteB, written, written + bitIndex) << written);
|
||||
}
|
||||
}
|
||||
|
||||
private byte extractRange(int in, int begin, int end){
|
||||
return (byte) ((in >> begin) & ((1 << (end - begin)) - 1));
|
||||
}
|
||||
public byte getCurrentByte() {
|
||||
if(byteIndex == bytes.length) throw new RuntimeException("ByteWriter overflow!");
|
||||
|
||||
return bytes[byteIndex];
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class MagicBitSet extends BitSet{
|
||||
private final int initLength;
|
||||
|
||||
public MagicBitSet(int nbits) {
|
||||
super(nbits);
|
||||
this.initLength = nbits;
|
||||
}
|
||||
|
||||
public int getTrueLength() {
|
||||
return length() == 0 ? initLength : length();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class PacketChunk {
|
||||
private PacketChunkData[] chunkData;
|
||||
private byte[] biomeData;
|
||||
|
||||
public PacketChunk(PacketChunkData[] chunkData, byte[] biomeData) {
|
||||
this.chunkData = chunkData;
|
||||
this.biomeData = biomeData;
|
||||
}
|
||||
|
||||
public PacketChunkData[] getChunkData() {
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
public byte[] getBiomeData() {
|
||||
return biomeData;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
public class PacketChunkData {
|
||||
private short[] blocks = new short[4096];
|
||||
private byte[] blockLight = new byte[2048];
|
||||
private byte[] skyLight;
|
||||
|
||||
public PacketChunkData(boolean isSkyLightData) {
|
||||
if(isSkyLightData){
|
||||
skyLight = new byte[2048];
|
||||
}
|
||||
}
|
||||
|
||||
public short[] getBlocks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
public byte[] getBlockLight() {
|
||||
return this.blockLight;
|
||||
}
|
||||
|
||||
public byte[] getSkyLight() {
|
||||
return this.skyLight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
|
||||
|
||||
public class BasicHandlerConstructor implements HandlerConstructor {
|
||||
@Override
|
||||
public ViaEncodeHandler newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
|
||||
return new ViaEncodeHandler(info, minecraftEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaDecodeHandler newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
|
||||
return new ViaDecodeHandler(info, minecraftDecoder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.expr.ConstructorCall;
|
||||
import javassist.expr.ExprEditor;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
public class ClassGenerator {
|
||||
private static HandlerConstructor constructor = new BasicHandlerConstructor();
|
||||
|
||||
public static HandlerConstructor getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
public static void generate() {
|
||||
if(!ViaVersion.getInstance().isCompatSpigotBuild()) return; // Use Basic Handler as not needed.
|
||||
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new LoaderClassPath(ClassGenerator.class.getClassLoader()));
|
||||
// Generate the classes
|
||||
transformSuperclass(pool, ViaDecodeHandler.class, ReflectionUtil.nms("PacketDecoder"));
|
||||
transformSuperclass(pool, ViaEncodeHandler.class, ReflectionUtil.nms("PacketEncoder"));
|
||||
|
||||
// Implement Constructor
|
||||
CtClass generated = pool.makeClass("us.myles.ViaVersion.classgenerator.generated.GeneratedConstructor");
|
||||
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
|
||||
|
||||
generated.setInterfaces(new CtClass[]{handlerInterface});
|
||||
// Import required classes
|
||||
pool.importPackage("us.myles.ViaVersion.classgenerator.generated");
|
||||
pool.importPackage("us.myles.ViaVersion.classgenerator");
|
||||
pool.importPackage("us.myles.ViaVersion.api.data");
|
||||
pool.importPackage("io.netty.handler.codec");
|
||||
// Implement Methods
|
||||
generated.addMethod(CtMethod.make("public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {\n" +
|
||||
" return new ViaEncodeHandler(info, minecraftEncoder);\n" +
|
||||
" }", generated));
|
||||
generated.addMethod(CtMethod.make("public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {\n" +
|
||||
" return new ViaDecodeHandler(info, minecraftDecoder);\n" +
|
||||
" }", generated));
|
||||
|
||||
constructor = (HandlerConstructor) generated.toClass(HandlerConstructor.class.getClassLoader()).newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static Class transformSuperclass(ClassPool pool, Class input, Class superclass) {
|
||||
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
|
||||
|
||||
try {
|
||||
CtClass toExtend = pool.get(superclass.getName());
|
||||
CtClass generated = pool.getAndRename(input.getName(), newName);
|
||||
generated.setSuperclass(toExtend);
|
||||
// Modify constructor to call super
|
||||
if(generated.getConstructors().length != 0) {
|
||||
generated.getConstructors()[0].instrument(new ExprEditor() {
|
||||
@Override
|
||||
public void edit(ConstructorCall c) throws CannotCompileException {
|
||||
if (c.isSuper()) {
|
||||
// Constructor for both has a stats thing.
|
||||
c.replace("super(null);");
|
||||
}
|
||||
super.edit(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
return generated.toClass(HandlerConstructor.class.getClassLoader());
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
|
||||
public interface HandlerConstructor {
|
||||
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
|
||||
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package us.myles.ViaVersion.commands;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.commands.defaultsubs.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ViaCommandHandler implements ViaVersionCommand, CommandExecutor, TabCompleter {
|
||||
private Map<String, ViaSubCommand> commandMap;
|
||||
|
||||
public ViaCommandHandler() {
|
||||
commandMap = new HashMap<>();
|
||||
try {
|
||||
registerDefaults();
|
||||
} catch (Exception e) {
|
||||
//ignore never throws exception because it doesn't exists
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSubCommand(@NonNull ViaSubCommand command) throws Exception {
|
||||
Validate.isTrue(command.name().matches("^[a-z0-9_-]{3,15}$"), command.name() + " is not a valid subcommand name");
|
||||
if (hasSubCommand(command.name()))
|
||||
throw new Exception("ViaSubCommand " + command.name() + " does already exists!"); //Maybe another exception later.
|
||||
commandMap.put(command.name().toLowerCase(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubCommand(String name) {
|
||||
return commandMap.containsKey(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaSubCommand getSubCommand(String name) {
|
||||
return commandMap.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String arg, String[] args) {
|
||||
if (args.length == 0) {
|
||||
showHelp(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasSubCommand(args[0])) {
|
||||
sender.sendMessage(color("&cThis command is not found"));
|
||||
showHelp(sender);
|
||||
return false;
|
||||
}
|
||||
ViaSubCommand handler = getSubCommand(args[0]);
|
||||
|
||||
if (!hasPermission(sender, handler.permission())) {
|
||||
sender.sendMessage(color("&cYou are not allowed to use this command!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
boolean result = handler.execute(sender, subArgs);
|
||||
if (!result)
|
||||
sender.sendMessage("Usage: /viaversion " + handler.usage());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String arg, String[] args) {
|
||||
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
//SubCommands tabcomplete
|
||||
if (args.length == 1) {
|
||||
if (!args[0].equals("")) {
|
||||
for (ViaSubCommand sub : allowed)
|
||||
if (sub.name().toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
output.add(sub.name());
|
||||
} else {
|
||||
for (ViaSubCommand sub : allowed)
|
||||
output.add(sub.name());
|
||||
}
|
||||
}
|
||||
//Let the SubCommand handle it
|
||||
else if (args.length >= 2) {
|
||||
if (getSubCommand(args[0]) != null) {
|
||||
ViaSubCommand sub = getSubCommand(args[0]);
|
||||
if (!allowed.contains(sub))
|
||||
return output;
|
||||
|
||||
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
|
||||
List<String> tab = sub.onTabComplete(sender, subArgs);
|
||||
Collections.sort(tab);
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public void showHelp(CommandSender sender) {
|
||||
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
|
||||
if (allowed.size() == 0) {
|
||||
sender.sendMessage(color("&cYou are not allowed to use this command!"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
|
||||
sender.sendMessage(color("&6Commands:"));
|
||||
for (ViaSubCommand cmd : allowed)
|
||||
sender.sendMessage(color(String.format("&2/viaversion %s &7- &6%s", cmd.usage(), cmd.description())));
|
||||
allowed.clear();
|
||||
}
|
||||
|
||||
private Set<ViaSubCommand> calculateAllowedCommands(CommandSender sender) {
|
||||
Set<ViaSubCommand> cmds = new HashSet<>();
|
||||
for (ViaSubCommand sub : commandMap.values())
|
||||
if (hasPermission(sender, sub.permission()))
|
||||
cmds.add(sub);
|
||||
return cmds;
|
||||
}
|
||||
|
||||
private boolean hasPermission(CommandSender sender, String permission) {
|
||||
return permission == null || sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
private void registerDefaults() throws Exception {
|
||||
registerSubCommand(new ListSubCmd());
|
||||
registerSubCommand(new PPSSubCmd());
|
||||
registerSubCommand(new DebugSubCmd());
|
||||
registerSubCommand(new DisplayLeaksSubCmd());
|
||||
registerSubCommand(new DontBugMeSubCmd());
|
||||
registerSubCommand(new AutoTeamSubCmd());
|
||||
registerSubCommand(new HelpSubCmd());
|
||||
registerSubCommand(new ReloadSubCmd());
|
||||
}
|
||||
|
||||
public static String color(String string) {
|
||||
try {
|
||||
string = ChatColor.translateAlternateColorCodes('&', string); //Dont replace all & with $ like we did before.
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public static void sendMessage(@NonNull CommandSender sender, String message, Object... args) {
|
||||
sender.sendMessage(color(args == null ? message : String.format(message, args)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class AutoTeamSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "autoteam";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle automatically teaming to prevent colliding.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
boolean newValue = !ViaVersion.getConfig().isAutoTeam();
|
||||
plugin.getConfig().set("auto-team", newValue);
|
||||
plugin.saveConfig();
|
||||
sendMessage(sender, "&6We will %s", (newValue ? "&aautomatically team players" : "&cno longer auto team players"));
|
||||
sendMessage(sender, "&6All players will need to re-login for the change to take place.");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DebugSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "debug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle debug mode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
plugin.setDebug(!plugin.isDebug());
|
||||
sendMessage(sender, "&6Debug mode is now %s", (plugin.isDebug() ? "&aenabled" : "&cdisabled"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import io.netty.util.ResourceLeakDetector;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DisplayLeaksSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "displayleaks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Try to hunt memory leaks!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
if (ResourceLeakDetector.getLevel() != ResourceLeakDetector.Level.ADVANCED)
|
||||
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
|
||||
else
|
||||
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
|
||||
|
||||
sendMessage(sender, "&6Leak detector is now %s", (ResourceLeakDetector.getLevel() == ResourceLeakDetector.Level.ADVANCED ? "&aenabled" : "&cdisabled"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DontBugMeSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "dontbugme";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle checking for updates";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
boolean newValue = !ViaVersion.getConfig().isCheckForUpdates();
|
||||
plugin.getConfig().set("checkforupdates", newValue);
|
||||
plugin.saveConfig();
|
||||
sendMessage(sender, "&6We will %snotify you about updates.", (newValue ? "&a" : "&cnot "));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
|
||||
public class HelpSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "You are looking at it right now!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
((ViaCommandHandler) plugin.getCommandHandler()).showHelp(sender);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ListSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Shows lists of the versions from logged in players";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
Map<Integer, Set<String>> playerVersions = new HashMap<>();
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
|
||||
if (!playerVersions.containsKey(playerVersion))
|
||||
playerVersions.put(playerVersion, new HashSet<String>());
|
||||
playerVersions.get(playerVersion).add(p.getName());
|
||||
}
|
||||
Map<Integer, Set<String>> sorted = new TreeMap<>(playerVersions);
|
||||
|
||||
for (Map.Entry<Integer, Set<String>> entry : sorted.entrySet())
|
||||
sendMessage(sender, "&8[&6%s&8]: &b%s", ProtocolVersion.getProtocol(entry.getKey()).getName(), entry.getValue());
|
||||
|
||||
sorted.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PPSSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "pps";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Shows the packets per second of online players";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "pps";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
Map<Integer, Set<String>> playerVersions = new HashMap<>();
|
||||
int totalPackets = 0;
|
||||
int clients = 0;
|
||||
long max = 0;
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (!ViaVersion.getInstance().isPorted(p))
|
||||
continue;
|
||||
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
|
||||
if (!playerVersions.containsKey(playerVersion))
|
||||
playerVersions.put(playerVersion, new HashSet<String>());
|
||||
UserConnection uc = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(p);
|
||||
if (uc.getPacketsPerSecond() > -1) {
|
||||
playerVersions.get(playerVersion).add(p.getName() + " (" + uc.getPacketsPerSecond() + " PPS)");
|
||||
totalPackets += uc.getPacketsPerSecond();
|
||||
if (uc.getPacketsPerSecond() > max) {
|
||||
max = uc.getPacketsPerSecond();
|
||||
}
|
||||
clients++;
|
||||
}
|
||||
}
|
||||
Map<Integer, Set<String>> sorted = new TreeMap<>(playerVersions);
|
||||
sendMessage(sender, "&4Live Packets Per Second");
|
||||
if (clients > 1) {
|
||||
sendMessage(sender, "&cAverage: &f" + (totalPackets / clients));
|
||||
sendMessage(sender, "&cHighest: &f" + max);
|
||||
}
|
||||
for (Map.Entry<Integer, Set<String>> entry : sorted.entrySet())
|
||||
sendMessage(sender, "&8[&6%s&8]: &b%s", ProtocolVersion.getProtocol(entry.getKey()).getName(), entry.getValue());
|
||||
sorted.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class ReloadSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reload the config from the disk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
plugin.getConf().generateConfig();
|
||||
sendMessage(sender, "&6Configuration successfully reloaded! Some features may need a restart.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package us.myles.ViaVersion.exception;
|
||||
|
||||
public class CancelException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package us.myles.ViaVersion.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InformativeException extends Exception {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
int sources = 0;
|
||||
|
||||
public InformativeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public InformativeException set(String key, Object value) {
|
||||
info.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InformativeException addSource(Class<?> sourceClazz) {
|
||||
return set("Source #" + sources++, getSource(sourceClazz));
|
||||
}
|
||||
|
||||
private String getSource(Class<?> sourceClazz) {
|
||||
if (sourceClazz.isAnonymousClass()) {
|
||||
return sourceClazz.getName() + " (Anonymous)";
|
||||
} else {
|
||||
return sourceClazz.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Please post this error to http://github.com/MylesIsCool/ViaVersion/issues\n{");
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> entry : info.entrySet()) {
|
||||
builder.append((i == 0 ? "" : ", ") + entry.getKey() + ": " + entry.getValue().toString());
|
||||
i++;
|
||||
}
|
||||
builder.append("}\nActual Error: ");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
// Don't record this stack
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user